Declarative Routing
go_router is governed by a set of routes which you specify as part of the
GoRouter constructor:
class App extends StatelessWidget {
...
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const Page1Screen(),
),
GoRoute(
path: '/page2',
builder: (context, state) => const Page2Screen(),
),
],
);
}
In this case, we've defined two routes. Each route path will be matched
against the location to which the user is navigating. Only a single route can be
matched, specifically the one whose path matches the entire location (and so
it doesn't matter in which order you list your routes). The path will be
matched in a case-insensitive way, although the case for
parameters will be preserved.
In addition to the path, each route will typically have a builder function,
which is responsible for building the Widget that is to take up the entire
screen of your app. The default transition will be used between
pages depending on the type of app you've put at the top of your widget tree,
e.g. the use of MaterialApp will cause go_router to use the MaterialPage
transitions.
Router state
While it's not used in the snippet above, the builder function are passed a
state object, which is an instance of the GoRouterState class that contains
some useful information:
GoRouterState property | description | example 1 | example 2 |
|---|---|---|---|
location | location of the full route, including query params | /login?from=/family/f2 | /family/f2/person/p1 |
subloc | location of this sub-route w/o query params | /login | /family/f2 |
name | route name | login | family |
path | route path | /login | family/:fid |
fullpath | full path to this sub-route | /login | /family/:fid |
params | params extracted from the location | {} | {'fid': 'f2'} |
queryParams | optional params from the end of the location | {'from': '/family/f1'} | {} |
extra | optional object param | null | null |
error | Exception associated with this sub-route, if any | Exception('404') | ... |
pageKey | unique key for this sub-route | ValueKey('/login') | ValueKey('/family/:fid') |
The state object is useful for passing parameters to parameterized
routes and for redirection. Not all of the state
parameters will be set every time. In general, GoRouterState defines a
superset of the potential current state of a GoRouter instance. For example,
the error parameter will only be set if there's an error.
Initialization
With a list of routes, you can create an instance of a GoRouter, which itself
provides the objects you need to call the MaterialApp.router constructor (or
the CupertinoApp.router constructor):
class App extends StatelessWidget {
App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
);
final _router = GoRouter(routes: ...);
}
With the router in place, your app can now navigate between pages.
Error handling
By default, go_router comes with default error screens for both MaterialApp
and CupertinoApp as well as a default error screen in the case that you're
using neither. You can replace the default error screen by setting the
errorBuilder parameter of the GoRouter:
class App extends StatelessWidget {
...
final _router = GoRouter(
...
errorBuilder: (context, state) => ErrorScreen(state.error),
);
}
The error screen, whether it's provided by you or go_router, will be used in case a location doesn't match a route, more than one route is found for a given location or if any of the builder functions throws an exception.
Deep Linking
Flutter defines "deep linking" as "opening a URL displays that screen in your
app." Anything that's listed as a GoRoute can be accessed via deep linking
across Android, iOS and the web. Support works out of the box for the web, of
course, via the address bar, but requires additional configuration for Android
and iOS as described in the Flutter
docs.