Navigator 2.0 remove 'page not found' from history

Viewed 54

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()));
}
  1. Prevent be stuck:

    if (configuration.isUnknown) {
      return null;
    }
    

    But unfortunately the address bar wouldn't show 404.

History buttons works until invalid URL is put in

0 Answers
Related