Navigator Builder
Sometimes it is necessary to insert a widget above the Navigator
, but below
MaterialApp
/CupertinoApp
, e.g. to insert a provider that needs access to the
app's context to get the current locale and localization, to build a UI outside
of navigation or to completely replace with Navigator
with something of your
own (which is outside the scope of this document).
For these purposes, you need to use the navigatorBuilder
parameter in the
GoRouter
constructor. This is similar to the builder
parameter in the
MaterialApp
constructor, but gives access to infrastructure provided by
MaterialApp
.
An example of placing some data provider widget:
final _router = GoRouter(
routes: ...,
// add a wrapper around the navigator to put loginInfo into the widget tree
navigatorBuilder: (context, state, child) =>
ChangeNotifierProvider<LoginInfo>.value(
value: loginInfo,
builder: (context, _) => child,
),
);
A more interesting example of using navigatorBuilder
is the following, which
puts a floating button on every page to allow for easy logout:
final _router = GoRouter(
routes: ...,
// add a wrapper around the navigator to:
// - put loginInfo into the widget tree, and to
// - add an overlay to show a logout option
navigatorBuilder: (context, state, child) =>
ChangeNotifierProvider<LoginInfo>.value(
value: loginInfo,
builder: (context, _) =>
loginInfo.loggedIn ? AuthOverlay(child: child) : child;
},
),
);
This example checks the login status in the navigatorBuilder
:
- if the user is logged in, an instance of the
AuthOverlay
widget is created, which wraps the theNavigator
passed tonavigatorBuilder
via thechild
parameter and provides a logout button on every page - if the user is not logged in, return the
Navigator
via thechild
parameter
The AuthOverlay
shows the logout button and the Navigator
in a Stack
:
class AuthOverlay extends StatelessWidget {
const AuthOverlay({required this.child, Key? key}) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) => Stack(
children: [
child,
Positioned(
top: 90,
right: 4,
child: ElevatedButton(
onPressed: () {
context.read<LoginInfo>().logout();
context.goNamed('home'); // clear out the `from` query param
},
child: const Icon(Icons.logout),
),
),
],
);
}
Here's what this look like in action: