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 5 of 5 of this article. Click here to view the first page.

Deep Linking

To implement deep links, this tutorial uses the uni_links package.

This package helps with deep links on Android, as well as Universal Links and Custom URL Schemes on iOS. To handle deep links on Android, modify the AndroidManifest.xml file in the android/app/src/main directory. For Android, add an <intent-filter> tag inside the MainActivity's <activity> tag as follows:

<intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data
     android:scheme="navapp"
     android:host="deeplinks" />
  </intent-filter>

For iOS, modify ios/Runner/Info.plist by adding:

<key>CFBundleURLTypes</key>
<array>
  <dict>
	<key>CFBundleTypeRole</key>
	<string>Editor</string>
	<key>CFBundleURLName</key>
	<string>deeplinks</string>
	<key>CFBundleURLSchemes</key>
	<array>
		<string>navapp</string>
	</array>
	</dict>
</array>

Both of these changes add a scheme named navapp to the app where navapp means Navigation App. In terms of deep links, this means this app can open URLs that look like navapp://deeplinks. You can use whatever scheme you like as long as it's unique.

Now that you know how to make deep links open your app, follow the steps below to understand how to consume the link the user clicked on.

Parse Deep Link URI

Open the router_delegate.dart file and add parseRoute, which takes a Uri as an argument, parses the path and sets the page(s):

void parseRoute(Uri uri) {
// 1
  if (uri.pathSegments.isEmpty) {
    setNewRoutePath(SplashPageConfig);
    return;
  }

// 2
  // Handle navapp://deeplinks/details/#
  if (uri.pathSegments.length == 2) {
    if (uri.pathSegments[0] == 'details') {
// 3
      pushWidget(Details(int.parse(uri.pathSegments[1])), DetailsPageConfig);
    }
  } else if (uri.pathSegments.length == 1) {
    final path = uri.pathSegments[0];
// 4
    switch (path) {
      case 'splash':
        replaceAll(SplashPageConfig);
        break;
      case 'login':
        replaceAll(LoginPageConfig);
        break;
      case 'createAccount':
// 5
        setPath([
          _createPage(Login(), LoginPageConfig),
          _createPage(CreateAccount(), CreateAccountPageConfig)
        ]);
        break;
      case 'listItems':
        replaceAll(ListItemsPageConfig);
        break;
      case 'cart':
        setPath([
          _createPage(ListItems(), ListItemsPageConfig),
          _createPage(Cart(), CartPageConfig)
        ]);
        break;
      case 'checkout':
        setPath([
          _createPage(ListItems(), ListItemsPageConfig),
          _createPage(Checkout(), CheckoutPageConfig)
        ]);
        break;
      case 'settings':
        setPath([
          _createPage(ListItems(), ListItemsPageConfig),
          _createPage(Settings(), SettingsPageConfig)
        ]);
        break;
    }
  }
}

In the code above:

  1. Check if there are no pathSegments in the URI. If there are, navigate to the Splash page.
  2. Handle the special case for the Details page, as the path will have two pathSegments.
  3. Parse the item number and push a Details page with the item number. In a real app, this item number could be a product's unique ID.
  4. Use path as an input for the switch case.
  5. In this case and other cases, push the pages necessary to navigate to the destination using setPath.

Next, to link parseRoute to the root widget's router, open main.dart.

Add the following code after ShoppingBackButtonDispatcher backButtonDispatcher;:

StreamSubscription _linkSubscription;

_linkSubscription is a StreamSubscription for listening to incoming links. Call .cancel() on it to dispose of the stream.

To do so, add:

  _linkSubscription?.cancel();

before super.dispose();. Now add the missing initPlatformState, which is responsible for setting up the listener for the deep links:

// Platform messages are asynchronous, so declare them with the async keyword.
Future<void> initPlatformState() async {
  // Attach a listener to the Uri links stream
// 1
  _linkSubscription = getUriLinksStream().listen((Uri uri) {
    if (!mounted) return;
    setState(() {
// 2
      delegate.parseRoute(uri);
    });
  }, onError: (Object err) {
    print('Got error $err');
  });
}

Here's what you do in the code above:

  1. Initialize StreamSubcription by listening for any deep link events.
  2. Have the app's delegate parse the uri and then navigate using the previously defined parseRoute.

At this point, you've implemented deep links, so stop the running instance of your app and re-run it to include these changes. Don't use hot restart or hot reload because there were changes on the native Android and native iOS side of the project and they aren't considered by these two tools.

Testing Android URIs

To test your deep links on Android, the easiest way is from the command line.

Open Terminal on macOS or Linux or CMD on Windows and verify that the app navigates to the Settings page using the following adb shell command:

adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "navapp://deeplinks/settings"'

Notice that this adb command includes the app's deep link scheme navapp and host deeplinks from Android Manifest.xml.

To navigate to the Details page with the index of the item as 1, try the following command:

adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "navapp://deeplinks/details/1"'

To navigate to the Cart page, try the following command:

adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "navapp://deeplinks/cart"'

You'll see your app navigates to the target page. If that's not the case, check the logs to see if there are any errors.

Where to Go From Here?

Download the final version of this project using the Download Materials button at the top or bottom of this tutorial.

Congratulations! That was a lot of code, but it should help you whenever you plan to implement your own RouterDelegate with Navigator 2.0.

Check out the following links to learn more about some of the concepts in this tutorial:

We hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!