Search code examples
flutterroutes

Flutter Auto route error "...must be that of a widget that is a descendant of an AutoRouter widget."


I understand the problem but I don't know the solution, can anyone help?

In main.dart I have a Scaffold with a Drawer menu. The Drawer menu provides a link to the About page.

builder: (BuildContext context, themeProvider, child) {
          return MaterialApp(
            home: Scaffold(
              endDrawer: MenuDrawer(),
              body: MaterialApp.router(
                routerConfig: _appRouter.config(),
              ),
            ), //end Scaffold
           ); //end MaterialApp

This loads my initial route (the HOME page).

Inside the Menu Drawer the "About" button link is coded like this:

OutlinedButton(
   child: const Text("ABOUT PAGE"),
   onPressed: () {
      context.router.pushNamed('/about');
   },
),

....but when I press this button I get the error:

══╡ EXCEPTION CAUGHT BY GESTURE
The following assertion was thrown while handling a gesture: AutoRouter operation requested with a context that does not include an AutoRouter. The context used to retrieve the Router must be that of a widget that is a descendant of an AutoRouter widget.

As I understand it, this is because the menu drawer resides in the main body, not in the body of "home". But I don't want to have a menu for each specific page (or an appbar and menu for each page)...I just want one above all the pages. So how do I modify my onPressed code to work?


Solution

  • If I understood correctly, you may just simplify your setup a little to avoid additional nesting (MaterialApp.router inside MaterialApp). That's how you'll have a single context for all pages:

    @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          routerConfig: _appRouter.config(),
          builder: (context, router) {
            return Scaffold(
              endDrawer: MenuDrawer(),
              body: router,
            );
          },
        );
      }
    

    Another option is to use nested navigation. After discussion in comments, I actually think it's more robust way.

    First, read about nested navigation [here]

    Config has to be modified:

    @AutoRouterConfig()
    class AppRouter extends RootStackRouter {
      @override
      List<AutoRoute> get routes => [
            // nesting starts here.
            AutoRoute(
              path: '/',
              page: RootRoute.page,
              children: [
                // sample routes
                AutoRoute(path: '', page: HomeRoute.page),
                AutoRoute(path: 'about', page: AboutRoute.page),
              ],
            ),
          ];
    }
    

    So now you have 3 pages: RootPage, HomePage, AboutPage. HomePage and AboutPage are actual pages with your content (so they have different names I guess). RootPage is technical page and should include all your drawers, appears and other navigation-specific stuff. Example:

    @RoutePage()
    class RootPage extends StatelessWidget {
      const RootPage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return AutoTabsScaffold(
          appBarBuilder: (_, tabsRouter) => AppBar(
            title: const Text('My App'),
          ),
          endDrawer: const MenuDrawer(),
        );
      }
    }
    

    You main build method will look like this now:

      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          routerConfig: _appRouter.config(),
        );
      }
    

    Meaning you provides only router config to build method and AutoRouter takes care of other things.

    I also created a GitHub example to better explain the concept, repository is here. Feel free to look and use in your code, it needed.