GoRouter.dev

Current field guide · 2026

Choose which Navigator owns a page

A root Navigator and a shell Navigator serve different purposes; use keys when a destination must appear above the shell.

Edition published 21 Oct 2026go_router 18.1.1 reference
On this page

Make ownership explicit

A shell commonly owns its own Navigator. A modal-like detail or full-screen flow may need the root Navigator instead. The page's visual placement follows that ownership, not just how deeply its path is nested.

Keep key relationships valid

Create stable GlobalKey instances outside frequently rebuilt widgets. Attach the root key to the router and a shell key to the appropriate ShellRoute. Set parentNavigatorKey on the route that needs the parent Navigator.

Avoid competing stacks

Mixing arbitrary imperative Navigator calls with declarative locations can make browser history and deep links difficult to reason about. Use an imperative push only when its lifecycle and expected Back behavior are intentional.

Verify the destination in isolation

Open the full-screen route directly, arrive from inside the shell, and leave with Back. Check the visible frame and the retained branch underneath. A screenshot of one entry path is not enough to validate the navigation model.

dart / example
final rootKey = GlobalKey<NavigatorState>();
final shellKey = GlobalKey<NavigatorState>();

// Attach rootKey to GoRouter.navigatorKey.
// Attach shellKey to ShellRoute.navigatorKey.
GoRoute(
  path: 'details',
  parentNavigatorKey: rootKey,
  builder: (_, __) => const FullScreenDetails(),
)
Earlier edition: read the complete original guide. Its older APIs are kept in historical context.

Sources & version context

GoRouter.dev documentation edition. Examples illustrate the APIs described in the linked primary documentation. Adapt application-owned widgets and services to your project. They are not an executed Flutter session in this browser.

Find your next route