Flutter Navigator 2.0 and Deep Links

With Flutter’s Navigator 2.0, learn how to handle deep links in Flutter and gain the ultimate navigation control for your app. By Kevin D Moore.

3.9 (26) · 3 Reviews

Download materials
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

RouteInformationParser

A RouteInformationParser is a delegate used by Router to parse a route’s information into a configuration of any type T which in your case would be PageConfiguration.

This app’s RouteInformationParser is also known as ShoppingParser. Make this class extend from RouteInformationParser. To begin, create a new Dart file, shopping_parser.dart, in the router directory and add the following code to this file:

import 'package:flutter/material.dart';
import 'ui_pages.dart';

class ShoppingParser extends RouteInformationParser<PageConfiguration> {
}

RouterInformationParser requires that its subclasses override parseRouteInformation and restoreRouteInformation.

parseRouteInformation converts the given route information into parsed data — PageConfiguration in this case — to pass to RouterDelegate:

@override
Future<PageConfiguration> parseRouteInformation(
      RouteInformation routeInformation) async {
  // 1
  final uri = Uri.parse(routeInformation.location);
  // 2
  if (uri.pathSegments.isEmpty) {
    return SplashPageConfig;
  }

  // 3
  final path = uri.pathSegments[0];
    // 4
  switch (path) {
    case SplashPath:
      return SplashPageConfig;
    case LoginPath:
      return LoginPageConfig;
    case CreateAccountPath:
      return CreateAccountPageConfig;
    case ListItemsPath:
      return ListItemsPageConfig;
    case DetailsPath:
      return DetailsPageConfig;
    case CartPath:
      return CartPageConfig;
    case CheckoutPath:
      return CheckoutPageConfig;
    case SettingsPath:
      return SettingsPageConfig;
    default:
      return SplashPageConfig;
  }
}

Here’s what’s happening in the code above:

  1. location from routeInformation is a String that represents the location of the application. The string is usually in the format of multiple string identifiers with slashes between — for example: `/`, `/path` or `/path/to/the/app`. It’s equivalent to the URL in a web application. Use parse from Uri to create a Uri from this String.
  2. If there are no paths, which is most likely the case when the user is launching the app, return SplashPage.
  3. Otherwise, get the first path segment from the pathSegements list of the uri.
  4. Then return the PageConfiguration corresponding to this first path segment.

restoreRouteInformation isn't required if you don't opt for the route information reporting, which is mainly used for updating browser history for web applications. If you decide to opt in, you must also override this method to return RouteInformation based on the provided PageConfiguration.

So, override restoreRouteInformation. In a way, this method does the exact opposite of the previously defined parseRouteInformation by taking in a PageDate and returning an object of type RouteInformation:

@override
RouteInformation restoreRouteInformation(PageConfiguration configuration) {
  switch (configuration.uiPage) {
    case Pages.Splash:
      return const RouteInformation(location: SplashPath);
    case Pages.Login:
      return const RouteInformation(location: LoginPath);
    case Pages.CreateAccount:
      return const RouteInformation(location: CreateAccountPath);
    case Pages.List:
      return const RouteInformation(location: ListItemsPath);
    case Pages.Details:
      return const RouteInformation(location: DetailsPath);
    case Pages.Cart:
      return const RouteInformation(location: CartPath);
    case Pages.Checkout:
      return const RouteInformation(location: CheckoutPath);
    case Pages.Settings:
      return const RouteInformation(location: SettingsPath);
    default: 
      return const RouteInformation(location: SplashPath);
  }
}

This method uses uiPage from Page to return a RouteInformation with its location set to the given path. Notice that there's a RouteInformation with the location of SplashPath in case there are no matches for uiPage.

Root Widget and Router

Now that you have all the required Router classes, hook them up with the root widget of your app in the main.dart file. Open main.dart and find:
// TODO Create Delegate, Parser and Back button Dispatcher

Define instances of ShoppingRouterDelegate and ShoppingParser.

ShoppingRouterDelegate delegate;
final parser = ShoppingParser();

Then, replace // TODO Setup Router & dispatcher with the following:

// 1
  delegate = ShoppingRouterDelegate(appState);
// 2
  delegate.setNewRoutePath(SplashPageConfig);

In the code above, you:

  • Create the delegate with the appState field.
  • Set up the initial route of this app to be the Splash page using setNewRoutePath.

Add any needed imports. For most Flutter apps, you might have MaterialApp or CupertinoApp as the root widget. Both of these use WidgetsApp internally. WidgetsApp creates a Router or a Navigator internally. In case of a Router, the Navigator is configured via the provided routerDelegate. Navigator then manages the pages list that updates the app's navigation whenever this list of pages changes.

Since Navigator 2.0 is backward-compatible with Navigator 1.0, the easiest way to start with Navigator 2.0 is to use MaterialApp's MaterialApp.router(...) constructor. This requires you to provide instances of a RouterDelegate and a RouteInformationParser as the ones discussed above.

Hence, in the root widget's build method, replace MaterialApp with:

child: MaterialApp.router(
  title: 'Navigation App',
  debugShowCheckedModeBanner: false,
  theme: ThemeData(
      primarySwatch: Colors.blue,
      visualDensity: VisualDensity.adaptivePlatformDensity,
  ),
  routerDelegate: delegate,
  routeInformationParser: parser,
),);

Notice that you're passing in the created routerDelegate and routeInformationParser. Run the app to make sure it still works.

Navigating Between Pages

Now you'll cover how to navigate between the various pages.

Open splash.dart from the ui folder and find appState.setSplashFinished(). If you go to setSplashFinished() you will see:

void setSplashFinished() {
  // 1
  _splashFinished = true; 
  if (_loggedIn) {
    // 2
    _currentAction = PageAction(state: PageState.replaceAll, page: ListItemsPageConfig);
  } else {
    // 3
    _currentAction = PageAction(state: PageState.replaceAll, page: LoginPageConfig);
  }
  notifyListeners();
}
  • Set the splash state to be finished.
  • If the user is logged in, show the list page.
  • Otherwise show the login page.

By setting the current action and calling notifyListeners, you will trigger a state change and the router will update its list of pages based on the current app state.

BackButtonDispatcher

Navigator 2.0 also uses a BackButtonDispatcher class to handle system back button presses. If you want to create a custom dispatcher, you can create a subclass of RootBackButtonDispatcher.

This app's BackButtonDispatcher is also known as ShoppingBackButtonDispatcher. Create this class by creating a new Dart file named back_dispatcher.dart in the router directory and adding the following code:

import 'package:flutter/material.dart';
import 'router_delegate.dart';

// 1
class ShoppingBackButtonDispatcher extends RootBackButtonDispatcher {
  // 2
  final ShoppingRouterDelegate _routerDelegate;

  ShoppingBackButtonDispatcher(this._routerDelegate)
      : super();

  // 3
  Future<bool> didPopRoute() {
    return _routerDelegate.popRoute();
  }
}

In the code above:

  1. Make ShoppingBackButtonDispatcher extend RootBackButtonDispatcher.
  2. Declare a final instance of ShoppingRouterDelegate. This helps you link the dispatcher to the app's RouterDelegate, i.e. ShoppingRouterDelegate.
  3. Delegate didPopRoute to _routerDelegate.

Note that this class doesn't do any complex back button handling here. Rather, it's just an example of subclassing RootBackButtonDispatcher to create a custom Back Button Dispatcher. If you need to do some custom back button handling, add your code to didPopRoute().

To use this class open main.dart, add the import statement and add the initializing code after final parser = ShoppingParser();:

ShoppingBackButtonDispatcher backButtonDispatcher;

Then initialize backButtonDispatcher in _MyAppState after delegate.setNewRoutePath(SplashPage);:

backButtonDispatcher = ShoppingBackButtonDispatcher(delegate);

Finally, use this dispatcher in your router in the build method by adding it before the routerDelegate: delegate, statement:

backButtonDispatcher: backButtonDispatcher,

This time, use Hot Restart instead of Hot Reload to restart the app. Then, observe there aren't changes to the flow of navigation because, as mentioned earlier, the didPopRoute in ShoppingBackButtonDispatcher does nothing special.