Configuration topic

Create a GoRouter configuration by calling the GoRouter constructor and providing list of GoRoute objects:

GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const Page1Screen(),
    ),
    GoRoute(
      path: '/page2',
      builder: (context, state) => const Page2Screen(),
    ),
  ],
);

GoRoute

To configure a GoRoute, a path template and builder must be provided. Specify a path template to handle by providing a path parameter, and a builder by providing either the builder or pageBuilder parameter:

GoRoute(
  path: '/users/:userId',
  builder: (context, state) => const UserScreen(),
),

To navigate to this route, use go(). To learn more about how navigation works, visit the Navigation topic.

Parameters

To specify a path parameter, prefix a path segment with a : character, followed by a unique name, for example, :userId. You can access the value of the parameter by accessing it through the GoRouterState object provided to the builder callback:

GoRoute(
  path: '/users/:userId',
  builder: (context, state) => const UserScreen(id: state.pathParameters['userId']),
),

Similarly, to access a query string parameter (the part of URL after the ?), use GoRouterState. For example, a URL path such as /users?filter=admins can read the filter parameter:

GoRoute(
  path: '/users',
  builder: (context, state) => const UsersScreen(filter: state.uri.queryParameters['filter']),
),

Child routes

A matched route can result in more than one screen being displayed on a Navigator. This is equivalent to calling push(), where a new screen is displayed above the previous screen with a transition animation, and with an in-app back button in the AppBar widget, if it is used.

To display a screen on top of another, add a child route by adding it to the parent route's routes list:

GoRoute(
  path: '/',
  builder: (context, state) {
    return HomeScreen();
  },
  routes: [
    GoRoute(
      path: 'details',
      builder: (context, state) {
        return DetailsScreen();
      },
    ),
  ],
)

Dynamic RoutingConfig

The RoutingConfig provides a way to update the GoRoute[s] after the GoRouter has already created. This can be done by creating a GoRouter with special constructor GoRouter.routingConfig

final ValueNotifier<RoutingConfig> myRoutingConfig = ValueNotifier<RoutingConfig>(
  RoutingConfig(
    routes: <RouteBase>[GoRoute(path: '/', builder: (_, __) => HomeScreen())],
  ),
);
final GoRouter router = GoRouter.routingConfig(routingConfig: myRoutingConfig);

To change the GoRoute later, modify the value of the ValueNotifier directly.

myRoutingConfig.value = RoutingConfig(
  routes: <RouteBase>[
    GoRoute(path: '/', builder: (_, __) => AlternativeHomeScreen()),
    GoRoute(path: '/a-new-route', builder: (_, __) => SomeScreen()),
  ],
);

The value change is automatically picked up by GoRouter and causes it to reparse the current routes, i.e. RouteMatchList, stored in GoRouter. The RouteMatchList will reflect the latest change of the RoutingConfig.

Nested navigation

Some apps display destinations in a subsection of the screen, for example, an app using a BottomNavigationBar that stays on-screen when navigating between destinations.

To add an additional Navigator, use ShellRoute and provide a builder that returns a widget:

ShellRoute(
  builder:
      (BuildContext context, GoRouterState state, Widget child) {
    return Scaffold(
      body: child,
      /* ... */
      bottomNavigationBar: BottomNavigationBar(
      /* ... */
      ),
    );
  },
  routes: <RouteBase>[
    GoRoute(
      path: 'details',
      builder: (BuildContext context, GoRouterState state) {
        return const DetailsScreen();
      },
    ),
  ],
),

The child widget is a Navigator configured to display the matching sub-routes.

For more details, see the ShellRoute API documentation. For a complete example, see the ShellRoute sample in the example/ directory.

Stateful nested navigation

In addition to using nested navigation with for instance a BottomNavigationBar, many apps also require that state is maintained when navigating between destinations. To accomplish this, use StatefulShellRoute instead of ShellRoute.

StatefulShellRoute creates separate Navigators for each of its nested branches (i.e. parallel navigation trees), making it possible to build an app with stateful nested navigation. The constructor StatefulShellRoute.indexedStack provides a default implementation for managing the branch navigators, using an IndexedStack.

When using StatefulShellRoute, routes aren't configured on the shell route itself. Instead, they are configured for each of the branches. Example:

branches: <StatefulShellBranch>[
  // The route branch for the first tab of the bottom navigation bar.
  StatefulShellBranch(
    navigatorKey: _sectionANavigatorKey,
    routes: <RouteBase>[
      GoRoute(
        // The screen to display as the root in the first tab of the
        // bottom navigation bar.
        path: '/a',
        builder: (BuildContext context, GoRouterState state) =>
            const RootScreen(label: 'A', detailsPath: '/a/details'),
        routes: <RouteBase>[
          // The details screen to display stacked on navigator of the
          // first tab. This will cover screen A but not the application
          // shell (bottom navigation bar).
          GoRoute(
            path: 'details',
            builder: (BuildContext context, GoRouterState state) =>
                const DetailsScreen(label: 'A'),
          ),
        ],
      ),
    ],
    // To enable preloading of the initial locations of branches, pass
    // 'true' for the parameter `preload` (false is default).
  ),

Similar to ShellRoute, a builder must be provided to build the actual shell Widget that encapsulates the branch navigation container. The latter is implemented by the class StatefulNavigationShell, which is passed as the last argument to the builder function. Example:

StatefulShellRoute.indexedStack(
  builder: (BuildContext context, GoRouterState state,
      StatefulNavigationShell navigationShell) {
    // Return the widget that implements the custom shell (in this case
    // using a BottomNavigationBar). The StatefulNavigationShell is passed
    // to be able access the state of the shell and to navigate to other
    // branches in a stateful way.
    return ScaffoldWithNavBar(navigationShell: navigationShell);
  },

Within the custom shell widget, the StatefulNavigationShell is first and foremost used as the child, or body, of the shell. Secondly, it is also used for
handling stateful switching between branches, as well as providing the currently active branch index. Example:

@override
Widget build(BuildContext context) {
  return Scaffold(
    // The StatefulNavigationShell from the associated StatefulShellRoute is
    // directly passed as the body of the Scaffold.
    body: navigationShell,
    bottomNavigationBar: BottomNavigationBar(
      // Here, the items of BottomNavigationBar are hard coded. In a real
      // world scenario, the items would most likely be generated from the
      // branches of the shell route, which can be fetched using
      // `navigationShell.route.branches`.
      items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'),
        BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'),
        BottomNavigationBarItem(icon: Icon(Icons.tab), label: 'Section C'),
      ],
      currentIndex: navigationShell.currentIndex,
      // Navigate to the current location of the branch at the provided index
      // when tapping an item in the BottomNavigationBar.
      onTap: (int index) => navigationShell.goBranch(index),
    ),
  );
}

For a complete example, see the Stateful Nested Navigation in the example/ directory. For further details, see the StatefulShellRoute API documentation.

Initial location

The initial location is shown when the app first opens and there is no deep link provided by the platform. To specify the initial location, provide the initialLocation parameter to the GoRouter constructor:

GoRouter(
  initialLocation: '/details',
  /* ... */
);

Logging

To enable log output, enable the debugLogDiagnostics parameter:

final _router = GoRouter(
  routes: [/* ... */],
  debugLogDiagnostics: true,
);

Classes

GoRoute Get started Configuration Redirection Transition animations Named routes
A route that is displayed visually above the matching parent route using the Navigator.
GoRoute Get started Configuration Redirection Transition animations Named routes
A route that is displayed visually above the matching parent route using the Navigator.
GoRouter Get started Upgrading Configuration Navigation Redirection Web Deep linking Named routes Error handling
The route configuration for the app.
GoRouter Get started Upgrading Configuration Navigation Redirection Web Deep linking Named routes Error handling
The route configuration for the app.
RoutingConfig Configuration
A set of parameters that defines routing in GoRouter.
RoutingConfig Configuration
A set of parameters that defines routing in GoRouter.
ShellRoute Configuration
A route that displays a UI shell around the matching child route.
ShellRoute Configuration
A route that displays a UI shell around the matching child route.
StatefulShellRoute Configuration
A route that displays a UI shell with separate Navigators for its sub-routes.
StatefulShellRoute Configuration
A route that displays a UI shell with separate Navigators for its sub-routes.