I followed John Ryans tutorial to learn the new navigation and routing system (Navigator 2.0) in flutter.
A problem occurs when it comes to handle non-existing pages:
The user puts in a non existing URL.
Unfortunately this URL is saved in the browsers history while the routing system navigates to a '404'-page at the same time.
When the user pushes the browsers previous button, the routing system navigates back to the non existing page which in turn is redirected to the '404'-page again.
The user is captured by the '404'-page unless the user right-clicks on the previous button to select the second last page.
I made a minimal Navigator-2.0-App to demonstrate this problem:
import 'package:flutter/material.dart';
class MyConfiguration {
const MyConfiguration.details()
: isDetail = true,
isUnknown = false;
const MyConfiguration.unknown()
: isDetail = false,
isUnknown = true;
const MyConfiguration.home()
: isDetail = false,
isUnknown = false;
final bool isDetail;
final bool isUnknown;
}
class MyRouteParser extends RouteInformationParser<MyConfiguration> {
const MyRouteParser() : super();
@override
Future<MyConfiguration> parseRouteInformation(
RouteInformation routeInformation) async {
final location = routeInformation.location!;
final uri = Uri.parse(location);
if (uri.pathSegments.isEmpty) {
return const MyConfiguration.home();
}
if (uri.pathSegments.length == 1 && uri.pathSegments.first == 'details') {
return const MyConfiguration.details();
}
return const MyConfiguration.unknown();
}
@override
RouteInformation? restoreRouteInformation(MyConfiguration configuration) {
if (configuration.isDetail) {
return const RouteInformation(location: '/details');
}
if (configuration.isUnknown) {
return const RouteInformation(location: '/404'); // #1
}
return const RouteInformation(location: '/');
}
}
class MyRouterDelegate extends RouterDelegate<MyConfiguration>
with ChangeNotifier, PopNavigatorRouterDelegateMixin {
@override
MyConfiguration currentConfiguration = const MyConfiguration.unknown();
@override
final navigatorKey = GlobalKey<NavigatorState>();
@override
Future<void> setNewRoutePath(MyConfiguration configuration) async {
currentConfiguration = configuration;
}
@override
Widget build(BuildContext context) {
final homePage = MaterialPage(
key: const ValueKey('home'),
child: Scaffold(
appBar: AppBar(title: const Text('Home')),
body: TextButton(
onPressed: () {
currentConfiguration = const MyConfiguration.details();
notifyListeners();
},
child: const Text('Show Details')),
));
final detailPage = MaterialPage(
key: const ValueKey('details'),
child: Scaffold(
appBar: AppBar(title: const Text('Details')),
));
final unknownPage = MaterialPage(
key: const ValueKey('unknown'),
child: Scaffold(
appBar: AppBar(title: const Text('Unknown')),
));
return Navigator(
key: navigatorKey,
pages: [
homePage,
if (currentConfiguration.isDetail)
detailPage
else if (currentConfiguration.isUnknown)
unknownPage,
],
onPopPage: (route, result) {
if (!route.didPop(result)) {
return false;
}
currentConfiguration = const MyConfiguration.home();
notifyListeners();
return true;
},
);
}
}
void main() {
runApp(MaterialApp.router(
routeInformationParser: const MyRouteParser(),
routerDelegate: MyRouterDelegate()));
}
Prevent be stuck:
if (configuration.isUnknown) { return null; }But unfortunately the address bar wouldn't show
404.
