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

No GoRouter found in context and other exceptions on attempting to pop from anywhere #196

Closed noga-dev closed 2 years ago

noga-dev commented 2 years ago

By wrapping the MaterialApp's builder's child with CallbackShortcut and passing various options of popping route, like

Navigator.of(context)

or

context.pop()

or

GoRouterDelegate

or

_goRouter.pop()

or

Router.of(context).routerDelegate.popRoute()

etc.

I get exceptions like

Router operation requested with a context that does not include a Router.

and

No GoRouter found in context.

Only thing that seems to kind of work is appRouter.navigator?.pop(), and that requires 2 attempts after the first time. With Auto Route it's enough to call _autoRoute.pop() from anywhere in the app using a shortcut of my choosing for it to pop the closest encompassing route.

Can the same not be done in GoRouter?

Example of my latest draft of MaterialApp

return MaterialApp.router(
  routeInformationParser: _router.routeInformationParser,
  routerDelegate: _router.routerDelegate,
  builder: (context, child) {
    CallbackShortcuts(
        bindings: {
          LogicalKeySet(
                    LogicalKeyboardKey.escape,
                  ): () => appRouter.navigator?.pop(),
                    LogicalKeyboardKey.escape,
                    LogicalKeyboardKey.alt,
                  ): () => appRouter.pop(), // or context.pop()
        }
        child: child!,
    )
  }
);

To reiterate, the first binding works in GoRouter after two attempts, the second one just throws. But it does work in Auto Route with no problems.

noga-dev commented 2 years ago

Also would be good to have a history of the stack with the ability to go back and forth like in Routemaster.

lulupointu commented 2 years ago

The first binding works in GoRouter after two attempts

During the first, the navigator is not build yet (since the building of the builder happens before the building of the Navigator). During the second, the building happens in the same order but the Navigator has been build the first time, hence the success.

The second one just throws

This is because the context that is given to you is too high up the tree to fetch InheritedGoRouter (which is the widget used to get GoRouter). The solution is for GoRouter.of to detect the case where the context is too high and return the right result. @csells you might want to check what Provider does. However this will be a bit harder and will necessitate a bit of refactoring because RouterDelegate is such a bad object to work with (this refactoring would be the same as the one explained in this issue, killing two birds with one stone.

lulupointu commented 2 years ago

Also would be good to have a history of the stack with the ability to go back and forth like in Routemaster.

I have much to say about this but you should open another issue since this has nothing to do with your first question :)