While it’s technically possible to nest “Navigator”, this is unrecommended here (as it breaks Hero animation)
You can use onGenerateRoute
to build nested ‘routes’, in the case of a route ‘/dashboard/profile’, build a Tree WidgetApp > Dashboard > Profile
. Which I assume is what you’re trying to achieve.
Combined with a higher order function, you can have something that creates onGenerateRoute
for you.
To provide a clue of the code-flow: the NestedRoute
neglects the exact build of the layout, letting it in the builder
method (e.g. builder: (child) => new Dashboard(child: child),
). When calling the buildRoute
method we will generate a PageRouteBuilder
for the very instance of this page, but letting _build
manage the creation of the Widgets
. In _build
we either use the builder
as is – or let it inflate the subroute, by recalling the requested subroute, calling its own _build
. Once done, we’ll be using the built subroute as the argument of our builder. Long story short, you recursively dive into further path levels to build the last level of the route, then let it rise from recursion and use the result as an argument for the outer level and so on.
BuildNestedRoutes
does the dirty job for you and parses the lists of NestedRoutes
to build the necessary RouteSettings
.
So, from the below example
Example :
@override
Widget build(BuildContext context) {
return new MaterialApp(
initialRoute: '/foo/bar',
home: const FooBar(),
onGenerateRoute: buildNestedRoutes(
[
new NestedRoute(
name: 'foo',
builder: (child) => new Center(child: child),
subRoutes: [
new NestedRoute(
name: 'bar',
builder: (_) => const Text('bar'),
),
new NestedRoute(
name: 'baz',
builder: (_) => const Text('baz'),
)
],
),
],
),
);
}
Here you simply defined your nested routes (name + associated component).
And NestedRoute
class + buildNestedRoutes
method are defined this way :
typedef Widget NestedRouteBuilder(Widget child);
@immutable
class NestedRoute {
final String name;
final List<NestedRoute> subRoutes;
final NestedRouteBuilder builder;
const NestedRoute({@required this.name, this.subRoutes, @required this.builder});
Route buildRoute(List<String> paths, int index) {
return new PageRouteBuilder<dynamic>(
pageBuilder: (_, __, ___) => _build(paths, index),
);
}
Widget _build(List<String> paths, int index) {
if (index > paths.length) {
return builder(null);
}
final route = subRoutes?.firstWhere((route) => route.name == paths[index], orElse: () => null);
return builder(route?._build(paths, index + 1));
}
}
RouteFactory buildNestedRoutes(List<NestedRoute> routes) {
return (RouteSettings settings) {
final paths = settings.name.split("https://stackoverflow.com/");
if (paths.length <= 1) {
return null;
}
final rootRoute = routes.firstWhere((route) => route.name == paths[1]);
return rootRoute.buildRoute(paths, 2);
};
}
This way, your Foo
and Bar
components will not be tightly coupled with your routing system ; but still have nested routes.
It’s more readable then having your routes dispatched all over the place. And you’ll easily add a new one.