Reputation: 20484
The GoRouter package provides examples of nested navigation using ShellRoute. How could this be used to build separate navigator stacks without breaking the linking strategy.
In my app, there is a simple GoRouter navigator that manages a series of pages. Some of the pages can bring up a modal on top of some of the screen. Inside this modal is another navigation stack.
I tried embedding a custom Page class directly into the shell route for each Navigator stack but this disabled the navigation. I have got this to work by creating another Router.withConfig() inside the modal however, I believe that this would break the deep linking.
Here is my shell route example. I created two Page classes. On is the base route for the pages that are not in modal view MyMainScreen
and the other is the ModalScreen that should partially fly over this route and contain it's own Stack of pages MyModalSheet
but nothing happens when a Go or Push request is made on any page anymore.
final _index = GoRouter(
routes: [
ShellRoute(
pageBuilder: (context, state, child) => MyMainScreen(
key: state.pageKey,
child: child,
),
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
),
GoRoute(
path: '/access',
builder: (context, state) => const AccessPage(),
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsPage(),
),
],
),
ShellRoute(
pageBuilder: (context, state, child) => MyModalSheet(
key: state.pageKey,
child: child,
),
routes: [
GoRoute(
path: '/docs',
builder: (context, state) => const DocsPage(),
),
],
)
Edit:: Here is a working solution with Keys and nested rooters. I have passed the key mainStack
to the page in the first inner navigator that is to open the modal view in the modal inner navigator.
That is then called in the AccessPage() this way:
stackKey?.currentContext != null
? () => GoRouter.of(stackKey!.currentContext!).push('/docs')
: null,
This does work but:
final GlobalKey mainStack = GlobalKey();
final _index = GoRouter(
navigatorKey: mainStack,
routes: [
GoRoute(
path: '/',
pageBuilder: (context, state) => MyMainScreen(
key: state.pageKey,
child: Router.withConfig(
config: GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
),
GoRoute(
path: '/access',
builder: (context, state) => AccessPage(
stackKey: mainStack, // <<---- passed key.
),
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsPage(),
),
],
),
),
),
routes: [
GoRoute(
path: 'docs',
pageBuilder: (context, state) => MyModalSheet(
key: state.pageKey,
child: const DocsPage(),
),
)
],
),
Upvotes: 1
Views: 4107
Reputation: 20484
GoRouter works on the principle that there is only one Router, be that a Router widget, CupertinoApp widget or MaterialApp widget.
Providing more than one router breaks the methodology of how strings are converted into one or more routes. The objective mentioned in the question is the principle of using Multiple Navigators. Flutter provides built in support for this via ShellRoute.
The ShellRoute creates a separate Navigator for all routes contained within that ShellRoute removing the need to define an additional Router.
The only exception of where this falls flat is in the use of tabbed navigation. This is because a route that is not matched in a given path from a go function is not rendered on the screen and therefore the state of any route or shell route that would be produced by one path but not by another will not be included in the structure at all.
There is an PR on StatefulShellRoute but it is very much a hack. Fundamentally GoRouter was not designed for that use as it was designed to Render exactly what the path tells it to render, nothing more and nothing less.
For multiple overlapping screens with internal navigators, GoRouter is still an extremely helpful navigator. For tabbed navigation it is not ideal but there is a very active community working on it with some existing solutions already.
Upvotes: 0