Flutter go_router Navigation

The built-in Navigator handles simple apps. Apps with web addresses, deep links, and login checks need more power. The go_router package, maintained by the Flutter team, adds URL-style paths, parameters, and automatic redirects with little code.

Why Use go_router

  • Every screen has a URL-like path, which works on the web and in deep links.
  • Path parameters carry values such as a product number.
  • Redirect rules guard private screens.
  • Nested routes build tabs and shells with persistent navigation bars.

Installing the Package

Run this command inside your project:


flutter pub add go_router

Flutter adds the latest compatible version to pubspec.yaml.

Setting Up the Router

Create a GoRouter object that lists every path. Use MaterialApp.router instead of the regular MaterialApp.


import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final GoRouter router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomeScreen(),
    ),
    GoRoute(
      path: '/about',
      builder: (context, state) => AboutScreen(),
    ),
  ],
);

void main() {
  runApp(MaterialApp.router(routerConfig: router));
}

  URL path        Screen
  ─────────       ───────────
  /               HomeScreen
  /about          AboutScreen
  /product/42     ProductScreen (id = 42)

Go and Push

The package offers two ways to move.

CallBehavior
context.go('/about')Replaces the navigation stack with the new path
context.push('/about')Adds a screen on top of the current one
context.pop()Returns to the previous screen

Use go for moves between main sections. Use push for temporary detail screens that need a back button.

Path Parameters

A colon marks a variable part of the path.


GoRoute(
  path: '/product/:id',
  builder: (context, state) {
    final id = state.pathParameters['id']!;
    return ProductScreen(id: id);
  },
),

// Opening it
context.go('/product/42');

The value 42 arrives as text. Convert it with int.parse(id) when the screen needs a number.

Query Parameters

Optional values travel after a question mark, such as /search?term=shoes.


GoRoute(
  path: '/search',
  builder: (context, state) {
    final term = state.uri.queryParameters['term'] ?? '';
    return SearchScreen(term: term);
  },
),

Nested Routes

A child route lives inside a parent and builds its path from the parent path.


GoRoute(
  path: '/settings',
  builder: (context, state) => SettingsScreen(),
  routes: [
    GoRoute(
      path: 'privacy',
      builder: (context, state) => PrivacyScreen(),
    ),
  ],
),

The child opens at /settings/privacy. Notice the child path has no leading slash.

Redirects for Login Protection

A redirect function runs before every navigation. Return a new path to divert the user, or return null to allow the move.


bool isLoggedIn = false;

final GoRouter router = GoRouter(
  redirect: (context, state) {
    final goingToLogin = state.matchedLocation == '/login';
    if (!isLoggedIn && !goingToLogin) {
      return '/login';
    }
    return null;
  },
  routes: [ /* routes here */ ],
);

  User opens /profile
        │
        ▼
   Logged in? ── yes ──→ show /profile
        │ no
        ▼
   redirect to /login

Error Page

Set errorBuilder to show a friendly page when someone enters an unknown path.


GoRouter(
  errorBuilder: (context, state) => NotFoundScreen(),
  routes: [ /* routes here */ ],
);

ShellRoute for Persistent Navigation

A ShellRoute wraps several routes in a shared layout, such as a scaffold with a bottom bar. The bar stays on screen while the inner page changes. Use StatefulShellRoute when each tab must remember its own scroll position and history.

Navigator or go_router

SituationBetter Choice
Small app, few screensNavigator with named routes
Web app with shareable linksgo_router
Login guards and deep linksgo_router

Check the package page on pub.dev for the latest API details, because routing packages evolve between releases.

Web Behavior

On the web, go_router connects Flutter screens to the browser address bar. Users bookmark a page, refresh it, and share the link. The browser back and forward buttons walk through the navigation history. These behaviors come for free once every screen has a path.

Deep Links

A deep link opens a specific screen from outside the app, such as a link in an email. Android and iOS need extra setup to claim a web address for your app. The router handles the Flutter side by matching the incoming path against the route list and building the right screen.

Practice Ideas

Build a store app with a home page, a product page at /product/:id, and a login page. Add a redirect that sends visitors to the login page until a login flag turns true. Run the app in Chrome and watch the address bar change as you move between screens.