go_router: The De Facto Standard for Flutter Routing in 2026
go_router dominates Flutter routing with 1.28M+ monthly downloads. Learn declarative routing, nested navigation, deep linking, and authentication guards.
Published on • September 15, 2026
AI Assistant

go_router has become the undisputed routing solution for Flutter, with over 1.28 million monthly downloads on pub.dev. Its declarative API, deep linking support, and nested navigation patterns make it the default choice for production Flutter apps.
Why go_router won
Flutter’s built-in Navigator 2 API is powerful but verbose. go_router wraps it in a declarative, URL-driven API that’s easier to reason about:
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/user/:id',
builder: (context, state) => UserScreen(
id: state.pathParameters['id']!,
),
),
],
);
No more Navigator.push with type arguments. Routes are URLs, and parameters are part of the path.
Basic route configuration
Define routes as a tree structure:
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
routes: [
GoRoute(
path: 'dashboard',
builder: (context, state) => DashboardScreen(),
),
GoRoute(
path: 'settings',
builder: (context, state) => SettingsScreen(),
),
],
),
GoRoute(
path: '/login',
builder: (context, state) => LoginScreen(),
),
],
);
Nested routes create URL paths like /dashboard and /settings under the root route.
Shell routes for nested navigation
Shell routes preserve layout across nested navigations:
final router = GoRouter(
routes: [
ShellRoute(
builder: (context, state, child) => ScaffoldWithNav(child: child),
routes: [
GoRoute(path: '/home', builder: (_, __) => HomeScreen()),
GoRoute(path: '/profile', builder: (_, __) => ProfileScreen()),
],
),
],
);
The ScaffoldWithNav widget stays mounted while /home and /profile swap underneath.
Deep linking
go_router handles deep links automatically:
// Android: android/app/src/main/AndroidManifest.xml
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="myapp.com" />
</intent-filter>
// iOS: ios/Runner/Info.plist
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array><string>myapp</string></array>
</dict>
</array>
When a user taps https://myapp.com/user/123, go_router navigates to the matching route.
Redirect guards for authentication
Protect routes with redirect logic:
final router = GoRouter(
redirect: (context, state) {
final isLoggedIn = AuthService.of(context).isLoggedIn;
final isLoggingIn = state.matchedLocation == '/login';
if (!isLoggedIn && !isLoggingIn) return '/login';
if (isLoggedIn && isLoggingIn) return '/';
return null; // No redirect
},
routes: [
GoRoute(path: '/', builder: (_, __) => HomeScreen()),
GoRoute(path: '/login', builder: (_, __) => LoginScreen()),
GoRoute(path: '/dashboard', builder: (_, __) => DashboardScreen()),
],
);
The redirect function runs on every navigation, enforcing authentication rules declaratively.
Programmatic navigation
Navigate programmatically with context.go and context.push:
// Replace entire navigation stack
context.go('/dashboard');
// Push onto current stack
context.push('/user/123');
// Push with extra data
context.push('/order', extra: OrderRequest(items: cart.items));
// Pop back
context.pop();
// Replace current route
context.go('/success');
Type-safe route parameters
Avoid stringly-typed parameters with route extensions:
extension UserRoutes on GoRouterHelper {
void pushUser(String id) => push('/user/$id');
void goHome() => go('/');
}
// Usage
context.pushUser('123');
Error handling
Handle unknown routes gracefully:
final router = GoRouter(
errorBuilder: (context, state) => Scaffold(
body: Center(
child: Text('Page not found: ${state.error}'),
),
),
);
URL strategies for web
Configure URL strategies for Flutter Web:
final router = GoRouter(
urlPathStrategy: UrlPathStrategy.path, // /path instead of #/hash
routes: [...],
);
Platform-aware routing
Adapt routing behavior per platform:
final router = GoRouter(
routes: [...],
platformGuards: [
if (kIsWeb) WebGuard(), // Web-specific guard
if (!kIsWeb) MobileGuard(), // Mobile-specific guard
],
);
Comparison with alternatives
| Feature | go_router | auto_route | GetX |
|---|---|---|---|
| Declarative | ✅ | ✅ | ❌ |
| Deep linking | ✅ | ✅ | Limited |
| Nested navigation | ✅ | ✅ | ❌ |
| Type-safe params | Via extensions | ✅ | ❌ |
| Community support | Excellent | Good | Declining |
| Maintenance | Active | Active | Uncertain |
go_router’s combination of declarative API, deep linking, and community support makes it the standard choice for Flutter routing in 2026.