routemaster icon indicating copy to clipboard operation
routemaster copied to clipboard

Wrapping a specific set of routes

Open theweiweiway opened this issue 3 years ago • 7 comments

Hey Tom

Super cool package! The dynamic route map based on state is a new idea I haven't really seen before , but I really like it

I couldn't find this in the examples, but I was wondering if there is a way to wrap a specific set of routes with other widgets.

an application of this would be to wrap all /groups routes with a GroupsBloc

Thanks~

theweiweiway avatar Apr 25 '21 13:04 theweiweiway

Thank you! Just to confirm - this would be to inject an object, something like this pseudocode?

RouteMap(
  routes: {
    '/': (_) => MaterialPage(child: HomePage()),

    ChangeNotifierProvider(
      create: (_) => GroupsBloc(),
      routes: {
        '/group': (_) => MaterialPage(child: GroupPage()),
        // More routes
      }
    ),
  },
)

I've got some thoughts about groups of routes, but nothing implemented yet.

tomgilder avatar Apr 25 '21 13:04 tomgilder

Yep!

theweiweiway avatar Apr 25 '21 13:04 theweiweiway

Do you have a specific example of why this would be useful, and why you wouldn't just wrap all the routes in the provider?

tomgilder avatar May 02 '21 09:05 tomgilder

For me at least - i like to scope Bloc only to the routes that need them. For example, I would only want to scope my GroupsBloc to all routes that start with /groups

Cool, I think it would be especially useful to teams in large apps if they could make a modular group of routes with their own set of injected dependencies.

Also, by wrapping things, we could achieve something that VRouter does with VNester - you could have a persistent appbar or whatever widget you want that always shows and does not get re-rendered, and then the rest of the screen would change based on the URL

I'll have a think about this...

theweiweiway avatar May 02 '21 22:05 theweiweiway

Cool update! The TabPage component looks really cool -

I think the TabPage is pretty close to what this issue is asking for. The only difference would be that the /feed and /settings routes would also have actual pages associated with them too.

    '/': (route) => TabPage(
          child: HomePage(), // can define Bloc in here to scope Bloc only to children
          paths: [
              '/feed',  // FeedPage()
              '/settings', // SettingsPage()
          ],
        ),

But I just realized something - if you create a WrapperPage that wraps a bunch of routes like this, what happens if you want to Guard a wrapped route based on the local Bloc? It seems like you can only use the context defined in the RouteMasterDelegate to guard routes currently

theweiweiway avatar May 04 '21 12:05 theweiweiway

This sounds related to https://github.com/tomgilder/routemaster/issues/4. Using the example that you have defined there, I wonder if this could be solved by allowing RouteMap to accept a builder @tomgilder.

final masterRouteMap = RouteMap(routes: {
  ...teamOneMap.routes,
  ...teamTwoMap.routes,
});

final teamOneMap = RouteMap(
  routes: {
    '/app-section-one': (_) => TeamOnePage(),
  },
);

final teamTwoMap = RouteMap(
  builder: (context, child) => ChangeNotifierProvider(
    create: (_) => GroupsBloc(),
    child: child,
  ),
  routes: {
    '/app-section-two': (_) => TeamTwoPage(),
  },
);

chimon2000 avatar May 04 '21 23:05 chimon2000

You can pretty much do this currently without any chances to Routemaster.

And yes, I barely understand BuilderRouteMap despite just typing it myself 😁

final teamOneMap = BuilderRouteMap(
  builder: (builder) {
    return MaterialPage<void>(
      child: ChangeNotifierProvider(
        create: (context) => AppState(),
        child: Builder(builder: builder),
      ),
    );
  },
  widgetRoutes: {
    '/one': (routeData, context) => Container(
          child: Text(Provider.of<AppState>(context).message),
        ),
  },
);

final routeMap = RouteMap(
  routes: {
    '/': (routeData) => MaterialPage(child: Text('Hello world')),
    ...teamOneMap.routes,
  },
);

class BuilderRouteMap extends DefaultRouterConfig {
  BuilderRouteMap({
    required MaterialPage Function(Widget Function(BuildContext)) builder,
    required Map<String, Widget Function(RouteData, BuildContext)> widgetRoutes,
  }) : routes = widgetRoutes.map(
          (key, widgetBuilder) => MapEntry(
            key,
            (routeData) => builder(
              (context) => widgetBuilder(routeData, context),
            ),
          ),
        );

  @override
  final Map<String, PageBuilder> routes;
}

The only downside is that from within teamOneMap you wouldn't be able to use the special "pages" like Redirect(), but that's just an API tradeoff sadly.

tomgilder avatar May 05 '21 15:05 tomgilder