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.