Nesting routes with flutter

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.

Leave a Comment