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?