csells / go_router

The purpose of the go_router for Flutter is to use declarative routes to reduce complexity, regardless of the platform you're targeting (mobile, web, desktop), handling deep linking from Android, iOS and the web while still allowing an easy-to-use developer experience.
https://gorouter.dev
441 stars 97 forks source link

[proposal] add the option to scope providers to paths #185

Closed dkbast closed 2 years ago

dkbast commented 2 years ago

I often want to provide something to a route and all its sub-routes - for example a QuizDefinition to all the quiz screens which will be added to the stack. So far I have often just added the Provider above the MaterialApp but was never really happy with that, since then I had to remember to reset the Provider etc pp.

I recognise that provider might not be the best name, but I hope it is clear what I want to achieve:

Proposal:

 GoRoute(
      providerBuilder: (context, navigator) => 
            Provider(create: (_)=> ProvidedObject(),
            child: navigator
      ),
      path: '/quiz',
      pageBuilder: (context, state) => MaterialPage<void>(
        key: state.pageKey,
        child: MyChild,
      ),
      routes: [...mySubRoutes], // should also apply to pages pushed on the stack imperatively
    );

And in go_router_delegate.dart we would have to wrap the section below with a MultiProvider or iterate over the providers which probably could be retrieved while performing the pages lookup.

Open question: how to cache the providers so they are not rebuilt on adding a page to the stack?

    // wrap the returned Navigator to enable GoRouter.of(context).go()
    return builderWithNav(
      context,
      Navigator(
        restorationScopeId: restorationScopeId,
        key: _key, // needed to enable Android system Back button
        pages: pages,
        observers: observers,
        onPopPage: (route, dynamic result) {
          if (!route.didPop(result)) return false;

          log2('GoRouterDelegate.onPopPage: matches.last= ${_matches.last}');
          _matches.remove(_matches.last);
          if (_matches.isEmpty) {
            throw Exception(
              'have popped the last page off of the stack; '
              'there are no pages left to show',
            );
          }

          // this hack allows the browser's address bar to be updated after a
          // push and pressing the Back button, but it shouldn't be necessary...
          _safeNotifyListeners();

          return true;
        },
      ),
    );
csells commented 2 years ago

You can use navigatorBuilder to achieve what you're looking for.

dkbast commented 2 years ago

My main concern is that I would like to attach the information about which providers/builders should be added to the GoRoute and not have a separate object holding this information, so I might have to extend GoRoute and find a way to access it again. I was thinking, that the GoRouteMatch'es passed to _builder already has access to all GoRoute objects and that I could build something like getPages() which gets the builders for each GoRoute - I'll try that over the weekend and report back.

Update: I think I will try passing the GoRouteMatch'es to the navigatorBuilder together with adding a navigatorBuilder field to the GoRoute, there could also be a default that if no navigatorBuilder is set that all builders are applied automatically

esDotDev commented 2 years ago

More or less a dupe of this I think https://github.com/csells/go_router/issues/133

csells commented 2 years ago

dupe of #133