2
2
more flexible and easy-to-use solution than the default routing options provided by
Flutter. It can be useful if you want more control over how routes are defined and
managed in your app. It also has a good support for web so that’s a nice choice for
your application.
You can define URL patterns, navigate using a URL, handle deep links, and a number
of other navigation-related scenarios.
Features
GoRouter has a number of features to make navigation straightforward:
Get started
To get started, add go_router to your pubspec.yaml. In this article we’ll be using
^7.1.1.
dependencies:
go_router: ^7.1.1
Route Configuration
After doing that lets add GoRouter configuration to your app:
import 'package:go_router/go_router.dart';
// GoRouter configuration
final _router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
name: 'home', // Optional, add name to your routes. Allows you navigate by
name instead of path
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
name: 'page2',
path: '/page2',
builder: (context, state) => Page2Screen(),
),
],
);
Then we can use either the MaterialApp.router or CupertinoApp.router constructor
and set the routerConfig parameter to your GoRouter configuration object:
Parameters
To specify a path parameter, prefix a path segment with a : character, followed by
a unique name, for example, :userId. We access the parameter value by GoRouterState
object provided to the builder callback:
GoRoute(
path: '/fruits/:id',
builder: (context, state) {
final id = state.params['id'] // Get "id" param from URL
return FruitsPage(id: id);
},
),
We can also access query string parameter using GoRouterState. For example, a URL
path such as /fruits?search=antonio can read the search parameter:
GoRoute(
path: '/fruits',
builder: (context, state) {
final search = state.queryParams['search'];
return FruitsPage(search: search);
},
),
Adding 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, and an in-app back button in the AppBar widget is provided.
GoRoute(
path: '/fruits',
builder: (context, state) {
return FruitsPage();
},
routes: <RouteBase>[ // Add child routes
GoRoute(
path: 'fruits-details', // NOTE: Don't need to specify "/" character for
router’s parents
builder: (context, state) {
return FruitDetailsPage();
},
),
],
)
Navigation Between Screens
There are many ways to navigate between destinations with go_router.
build(BuildContext context) {
return TextButton(
onPressed: () => context.go('/fruits/fruit-detail'),
);
}
We can also navigate by name instead of URL, call context.goNamed()
build(BuildContext context) {
return TextButton(
// remember to add "name" to your routes
onPressed: () => context.goNamed('fruit-detail'),
);
}
To build a URI with query parameters, you can use the Uri class:
context.go(
Uri(
path: '/fruit-detail',
queryParameters: {'id': '10'},
).toString(),
);
We can pop the current screen via context.pop().
GitHub - antonio-nicolau/flutter-go_router-with-nested-tab-navigation
You can't perform that action at this time. You signed in with another tab or
window. You signed out in another tab or…
github.com
Guards
To guard specific routes, e.g. from un-authenticated users, global redirect can be
set up via GoRouter. A most common example would be the set up redirect that guards
any route that is not /login and redirects to /login if the user is not
authenticated
GoRouter(
redirect: (BuildContext context, GoRouterState state) {
final isAuthenticated = // your logic to check if user is authenticated
if (!isAuthenticated) {
return '/login';
} else {
return null; // return "null" to display the intended route without
redirecting
}
},
...
You can define redirect on the GoRouter constructor. Called before any navigation
event.
Define redirect on the GoRoute constructor. Called when a navigation event is about
to display the route.
You can specify a redirectLimit to configure the maximum number of redirects that
are expected to occur in your app. By default, this value is set to 5. GoRouter
will display the error screen if this redirect limit is exceeded
Transition animations
GoRouter allows you to customise the transition animation for each GoRoute. To
configure a custom transition animation, provide a pageBuilder parameter to the
GoRoute constructor:
GoRoute(
path: '/fruit-details',
pageBuilder: (context, state) {
return CustomTransitionPage(
key: state.pageKey,
child: FruitDetailsScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
// Change the opacity of the screen using a Curve based on the the
animation's value
return FadeTransition(
opacity: CurveTween(curve: Curves.easeInOutCirc).animate(animation),
child: child,
);
},
);
},
),
For a complete example, see the transition animations sample.
GoRouter(
/* ... */
errorBuilder: (context, state) => ErrorPage(state.error),
);
Type-safe routes
Instead of using URL strings (context.go(“/auth”)) to navigate, go_router supports
type-safe routes using the go_router_builder package.
dev_dependencies:
go_router_builder: ^1.0.16
build_runner: ^2.3.3
build_verify: ^3.1.0
Defining a route
Then define each route as a class extending GoRouteData and overriding the build
method.
@override
Widget build(BuildContext context, GoRouterState state) => const HomeScreen();
}
Route tree
The tree of routes is defined as an attribute on each of the top-level routes:
import 'package:go_router/go_router.dart';
@immutable
class SongRoute extends GoRouteData {
final int id;
const SongRoute({required this.id});
@override
Widget build(BuildContext context) {
return SongScreen(songId: id.toString());
}
}
To build the generated files use the build_runner command:
TextButton(
onPressed: () {
const SongRoute(id: 2).go(context);
},
child: const Text('Go to song 2'),
),
Before you go !!!
There’s still a nice feature with go_router, you can add a NavigatorObserver to our
GoRouter for observing the behavior of a Navigator, listen for whenever a route was
push, pop or replace. To do so let’s create a class that extends
NavigatorObserver :
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
log('did pop route');
}
}
Now lets add MyNavigatorObserver to our GoRouter
GoRouter(
...
observers: [ // Add your navigator observers
MyNavigatorObserver(),
],
...
)
Whenever those events are triggered your navigator will be notified.