Why choose the go_router Flutter package?
The go_router Flutter package offers a declarative API for handling navigation, deep links, and nested routes without writing boilerplate code. It fits naturally into clean architecture because you can isolate routing logic behind a service or helper class, keeping UI widgets free from direct package calls.
Tip: Review the official pub.dev page and the package changelog before locking a version for production.
Installation
Add the package to your project with the Flutter CLI:
flutter pub add go_routerAfter the command completes, run flutter pub get to fetch the dependency.
Basic setup – a beginner‑friendly example
Below is a minimal, runnable example that demonstrates a home screen, a details screen, and a nested settings screen using go_router.
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// Define the router configuration in one place.
static final GoRouter _router = GoRouter(
routes: [
GoRoute(
path: '/',
name: 'home',
builder: (context, state) => const HomeScreen(),
routes: [
GoRoute(
path: 'details/:id',
name: 'details',
builder: (context, state) {
final id = state.params['id'];
return DetailsScreen(itemId: id ?? 'unknown');
},
),
GoRoute(
path: 'settings',
name: 'settings',
builder: (context, state) => const SettingsScreen(),
),
],
),
],
// Optional: handle unknown routes.
errorBuilder: (context, state) => const Scaffold(
body: Center(child: Text('Page not found')),
),
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'go_router Demo',
routerConfig: _router,
theme: ThemeData(primarySwatch: Colors.blue),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () => context.go('/details/42'),
child: const Text('Open Details (id=42)'),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () => context.go('/settings'),
child: const Text('Open Settings'),
),
],
),
),
);
}
}
class DetailsScreen extends StatelessWidget {
final String itemId;
const DetailsScreen({Key? key, required this.itemId}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Details – $itemId')),
body: Center(child: Text('Showing details for item $itemId')),
);
}
}
class SettingsScreen extends StatelessWidget {
const SettingsScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
body: const Center(child: Text('Settings go here')),
);
}
}This example demonstrates:
- Declarative route definitions with
GoRoute. - Path parameters (e.g.,
:id). - Nested routes for a clean URL hierarchy.
- Programmatic navigation using
context.go().
Integrating go_router behind an abstraction layer
In larger codebases, keep routing calls out of UI widgets. Create a small service that exposes navigation methods:
class NavigationService {
final GoRouter _router;
NavigationService(this._router);
void goToHome() => _router.go('/');
void goToDetails(String id) => _router.go('/details/$id');
void goToSettings() => _router.go('/settings');
}Inject NavigationService via your preferred DI framework and call it from view models or controllers instead of using context.go() directly.
Setup notes and best practices
- Version pinning: Use a caret or exact version (e.g.,
go_router: ^7.0.0) and monitor the changelog for breaking changes. - Deep linking: The package automatically parses URL fragments on mobile and web. Test with
adb shell am start -a android.intent.action.VIEW -d "myapp://details/99"on Android. - Error handling: Provide an
errorBuilderto show a friendly UI for unknown routes. - State restoration: If you need state restoration, enable it in
MaterialApp.routerand pass arouterDelegatethat supports restoration.
Mistakes to avoid
- Mixing
Navigator.pushwithgo_routercalls – stick to one navigation strategy. - Hard‑coding route strings throughout the app – use named routes or a constants file.
- Placing
GoRouterinside a widget that rebuilds frequently – define it as a top‑level static or inside a provider that lives for the app’s lifetime. - Ignoring platform‑specific URL schemes – verify that your Android
AndroidManifest.xmland iOSInfo.plistcontain the correct intent filters.
Remember: The go_router API evolves. Always check the changelog when upgrading major versions.
Searchable terms developers often use
- go_router navigation
- Flutter deep linking
- Nested routes Flutter
- Declarative routing Flutter
- MaterialApp.router example
FAQ
- Q: Does go_router support web URL synchronization?
A: Yes. When you useMaterialApp.router, the package updates the browser address bar and parses incoming URLs automatically. - Q: Can I use go_router with existing Navigator 1.0 code?
A: You can, but mixing the two approaches can lead to unexpected stack behavior. It’s recommended to migrate fully togo_routeror keep the old navigation isolated in a separate module. - Q: How do I pass complex objects between routes?
A: Prefer passing simple primitives via the URL and retrieve richer data from a shared state (e.g., a provider, Riverpod, or Bloc) inside the destination screen. - Q: Is go_router compatible with Flutter’s state restoration?
A: Starting with version 7, the package offers optional support for state restoration. Enable it by settingrouterDelegate: _router.routerDelegateand configuringrestorationScopeIdon yourMaterialApp. - Q: Where can I find official documentation?
A: The primary source is the package page on pub.dev: https://pub.dev/packages/go_router. The README contains the latest API usage and migration guides.
Frequently Asked Questions
Does go_router support web URL synchronization?
Yes. When you use MaterialApp.router, the package updates the browser address bar and parses incoming URLs automatically.
Can I use go_router with existing Navigator 1.0 code?
You can, but mixing the two approaches can lead to unexpected stack behavior. It’s recommended to migrate fully to go_router or keep the old navigation isolated in a separate module.
How do I pass complex objects between routes?
Prefer passing simple primitives via the URL and retrieve richer data from a shared state (e.g., a provider, Riverpod, or Bloc) inside the destination screen.
Is go_router compatible with Flutter’s state restoration?
Starting with version 7, the package offers optional support for state restoration. Enable it by setting routerDelegate: _router.routerDelegate and configuring restorationScopeId on your MaterialApp.
Where can I find official documentation?
The primary source is the package page on pub.dev: https://pub.dev/packages/go_router. The README contains the latest API usage and migration guides.