Issue regarding nested MaterialApp.router() in Flutter Navigator 2.0

2,350

If you look into app.dart MaterialApp Is a convenience widget that wraps a handful of "widgets that are commonly required for material design applications."

If you were to use the default constructor a top level Navigator object is configured for you.

The MaterialApp.router() is another convenience. "Creates a [MaterialApp] that uses the [Router] instead of a [Navigator]."

The router constructor provides you a way to create a MaterialApp and configure and return a custom Navigator.

What you are doing, when you use this constructor, is wrapping descendent widgets in all the convenience widgets that MaterialApp has to offer(Including the debug banner).

For Nested Routers what you want to do instead is just use the Router() widget directly, and you will avoid invoking all the extras that MaterialApp affords you during the initialization of your app.

Also of note, there should ideally only be one information parser per app. as per the notes in router.dart you should pass null to the nester Router Wdiget.

"To opt out of URL updates entirely, pass null for [routeInformationProvider]
/// and [routeInformationParser]. This is not recommended in general, but may be
/// appropriate in the following cases:
///
/// * The application does not target the web platform.
///
/// * **There are multiple router widgets in the application. Only one [Router]
///   widget should update the URL (typically the top-most one created by the
///   [WidgetsApp.router], [MaterialApp.router], or [CupertinoApp.router]).**
///
/// * The application does not need to implement in-app navigation using the
///   browser's back and forward buttons."


    class _Page1State extends State<Page1> {

  @override
  Widget build(BuildContext context) {

    print("Page 1");

    return Scaffold(
      body: Column(
        children: [
          InkWell(
            onTap: () {
              widget.valueChangeCallback(true,"A");
            },
            child: Text("Move to Sub Pages")
          ),
          Expanded(child: Router(routeInformationParser: null, routerDelegate: NestedAppRouterDelegate(), backButtonDispatcher: ChildBackButtonDispatcher(Router.of(context).backButtonDispatcher),)),
        ],
      ),
    );
  }

Also providing the child back button dispatcher as shown will allow you to contact the parent router when executing back button presses...Hope that helps!

Share:
2,350
Sarvesh Dalvi
Author by

Sarvesh Dalvi

Updated on December 29, 2022

Comments

  • Sarvesh Dalvi
    Sarvesh Dalvi over 1 year

    Ask the question denotes I'm trying to create a nested Navigator using Navigator 2.0 for my Flutter web app. Below is the starting point of my app.

    void main() {
      runApp(App());
    }
    
    
    class App extends StatefulWidget {
      @override
      _AppState createState() => _AppState();
    }
    
    class _AppState extends State<App> {
    
      @override
      Widget build(BuildContext context) {
    
        return MaterialApp.router(
            routeInformationParser: AppRouteParser(), routerDelegate: AppRouterDelegate(),
          title: "Demo",
        );
      }
    
    }
    

    As you can see I've added a MaterialApp.router() to handle all the top layer navigations.

    Now I wanted to add a nested navigator inside this one which will work the same way as above and will handle the url changes properly. That why I decided to use the same MaterialApp.router() widget inside as a child as my nested Navigator.

    Everything is working fine after doin this but I am getting two debug banners like the image below :

    enter image description here

    This makes me wonder if I using the proper method to achieve the result.

    The child Navigator belongs in Page1 widget of the root navigator like below is the Navigator widget of root MaterialApp.router:

    class AppRouterDelegate extends RouterDelegate<AppRoute>
        with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoute> {
    
      final GlobalKey<NavigatorState> _navigatorKey;
    
      bool isPage1A = false;
      bool isPage1B = false;
      bool isUnknown = false;
    
      AppRouterDelegate() : _navigatorKey = GlobalKey<NavigatorState>();
    
      @override
      Widget build(BuildContext context) {
    
         return Navigator(
             pages: [
               MaterialPage(key: ValueKey("Page 1"),child: Page1(_valueChangeCallback)),
               if(isPage1A)
                 MaterialPage(key: ValueKey("Page 1A"),child: Page1A(_valueChangeCallback)),
               if(isPage1B)
                 MaterialPage(key: ValueKey("Page 1B"),child: Page1B(_valueChangeCallback)),
              /* if(isUnknown)
                 MaterialPage(key: ValueKey("404"),child: TestPage()) */
             ],
             onPopPage: (route,result){print("Pop !!!!");  return route.didPop(result);}
         );
      }
    
    
      _valueChangeCallback(bool value,String subPage,[String subPage2]) {
    
        //print("Value change callback");
    
          if(subPage2 == null) {
            if(subPage == "A")
              isPage1A = value;
            else if(subPage == "B")
              isPage1B = value;
          }
          else {
            if(subPage2 == "B") {
              isPage1A = !value;
              isPage1B = value;
            }
            else if(subPage2 == "A") {
              isPage1A = value;
              isPage1B = !value;
            }
          }
          notifyListeners();
      }
    

    And below is the Page1 widget where the child MaterialApp.router is located :

    class Page1 extends StatefulWidget {
    
      Function valueChangeCallback;
    
      Page1(this.valueChangeCallback);
    
      @override
      _Page1State createState() => _Page1State();
    }
    
    class _Page1State extends State<Page1> {
    
      @override
      Widget build(BuildContext context) {
    
        print("Page 1");
    
        return Scaffold(
          body: Column(
            children: [
              InkWell(
                onTap: () {
                  widget.valueChangeCallback(true,"A");
                },
                child: Text("Move to Sub Pages")
              ),
              Expanded(child: MaterialApp.router(routeInformationParser: NestedAppRouteInformationParser(), routerDelegate: NestedAppRouterDelegate())),
            ],
          ),
        );
      }
    }