1

EDIT: Originally this post was specific to using a global navigator but I have found that the case was not specific to using a global navigator. Rather, its a bug that can happen from misuse of the Navigator API.

Flutter 1.22.0 • channel beta • https://github.com/flutter/flutter.git
Framework • revision d408d302e2 (4 weeks ago) • 2020-09-29 11:49:17 -0700
Engine • revision 5babba6c4d
Tools • Dart 2.10.0

I have followed the recommendation in a previous SO post. This works to some extent: How to navigate without context in flutter app?

But, I have come across some issues with simply using a global navigator key. I am not able to call popUntil with a route predicate to navigate back to my home screen, and because I cannot view the Navigator history stack, I am not able to effectively debug this.

/// constants/keys.dart
    final GlobalKey<NavigatorState> navKey = new GlobalKey<NavigatorState>();


/// constants/routes.dart
    const String HOME = 'home';
    const String SCREEN_A = 'a';
    const String SCREEN_B = 'b';


/// main.dart
class App extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            home: Home(),
            navigatorKey: Keys.navKey,
            onGenerateRoute: (routeSettings) {
                switch (routeSettings.name) {
                    case Routes.HOME:
                        return MaterialPageRoute(
                            builder: (context) => Home(),
                            settings: RouteSettings(name: Routes.HOME),
                        );
                    case Routes.SCREEN_A:
                        return MaterialPageRoute(
                            builder: (context) => ScreenA(),
                            settings: RouteSettings(name: Routes.SCREEN_A),
                        );
                    case Routes.SCREEN_B:
                        return MaterialPageRoute(
                            builder: (context) => ScreenB(),
                            settings: RouteSettings(name: Routes.SCREEN_B),
                        );
                    default:
                        return MaterialPageRoute(
                            builder: (context) => Home(),
                            settings: RouteSettings(name: Routes.HOME),
                        );
                }
            },
        );
    }
}

This is a tangent to show that you cannot use pages to view history added using the Navigator API.

/// Experiment, instrument build of Home, ScreenA, ScreenB with code to view pages:
Navigator nav = Keys.navKey.currentWidget;
print("Location: Home, Number of pages: ${nav.pages.length}");
for (Page page in nav.pages) {
  print("Page: ${page.name}, ${page.key}, ${page.toString()}");
}

/// OUTPUT: 
// Home, Number of pages: 0
// ScreenA, Number of pages: 0
// ScreenB, Number of pages: 0

I did not expect pages to display the navigation history

Some Routes do not correspond to Page objects, namely, those that are added to the history using the Navigator API (push and friends). A Route that does not correspond to a Page object is called a pageless route and is tied to the Route that does correspond to a Page object that is below it in the history.

End Tangent

The main pain point I have with Navigator is my need to navigate from within methods that do not have a build context in scope and require a global key to access widgets such as a Navigator. For example, I have to attach a listener for failure of a blue tooth connection and then navigate back to the HOME screen from the listener. In order to do so, I have had to rely on the global nav key. I also prefer having access to a global nav key instead of calling Navigator.of(context).

However, because I cannot view the Navigator history stack, I cannot easily debug my issue.

I am able to navigate forward using these calls with the global nav key:

Keys.navKey.currentState.pushNamed(Routes.HOME);

And I can use either:

Keys.navKey.currentState.pushNamedAndRemoveUntil(Routes.HOME, (Route<dynamic> route) => false);

or:

Keys.navKey.currentState.pushReplacementNamed(Routes.HOME);

to navigate backward.

But I can't simply pop off the internal Navigator history stack without getting a black screen.

/// Black Screen
Keys.navKey.currentState.popUntil(ModalRoute.withName(Routes.HOME));

To me, it seems like the correct method would be popUntil as it pops everything off the stack except the required route. Why is it not possible to simply call popUntil to navigate back to the home screen safely in this example?

Anthony O
  • 622
  • 7
  • 26

1 Answers1

0

I am able to navigate back to the default Route using this snippet:

 Keys.navKey.currentState.popUntil(ModalRoute.withName(Navigator.defaultRouteName));

Using the debugger, I see that the default route name is '/' and not 'home'. Using the navigator to programmatically navigate does not push the initial route onto the stack as home. This makes sense. I never call Keys.navKey.currentState.push('home'). This is something to keep in mind if you want to return to your app default screen using the navigator, the above method call seems to be the correct way to do it, unless you find a way to set the bottom of your Navigator stack to your home route.

Anthony O
  • 622
  • 7
  • 26