Deep Linking In Flutter - Part 1

Deep Linking In Flutter - Part 1

Deep linking consists of using a uniform resource identifier (URI) that links to a specific location within a mobile app rather than simply launching the app.

For android this is refered to as deeplinks while on iOS it's refered to as custom url scheme.

Deep links are URLs that take users directly to specific content in your app. However, if other apps installed on a user's device can handle the same intent, users might not go directly to your app. For example, clicking a URL in an email from a bank might lead to a dialog asking the user whether to use the browser or the bank's own app to open the link.

When a link is clicked, the device OS tries each of the following actions, in sequential order, until the request succeeds:

  • Open the user's preferred app that can handle the URI, if one is designated.
  • Open the only available app that can handle the URI.
  • Allow the user to select an app from a dialog.

In this article we would create a project and deep link / custom url capabilities to handle my portfolio url. we would then go ahead to handle the resulting action within the app using the Uni Link package.

Getting Started

We simply create a new project using flutter create, then navigate to the project folder and run flutter pub get.

flutter create example

cd example

flutter pub get

To start this is what main.dart looks like. Just a single text at the center that says 'HOME PAGE'.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Deeplink Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('HOME PAGE'),
      ),
    );
  }
}

To add deep link capabilities all we need to do is add an intent filter to the android manifest file. So first navigate to the AndroidManifest.xml file android > app > src > main > AndroidManifest.xml

<activity>
    // ...
    <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="https"
                         android:host="ifeanyi.web.app" />
    </intent-filter>
</activity>

Setting Up Custom Url (IOS)

To add custom url capabilities for iOS you need to declare the scheme in ios > Runner > Info.plist

<key>CFBundleURLTypes</key>
      <array>
            <dict>
              <key>CFBundleTypeRole</key>
              <string>Editor</string>
              <key>CFBundleURLName</key>
              <string>ifeanyi.web.app</string>
              <key>CFBundleURLSchemes</key>
              <array>
                <string>https</string>
              </array>
            </dict>
      </array>

To test this I created a text message with my portfolio url so we can have a clickable link.

Before and after adding deeplink with_without_intent.gif

For android you can use the Android Debug Bridge with the activity manager (am) tool to test that the intent filter URIs you specified for deep linking resolve to the correct app activity.

To do this you can navigate through the file manager to where you stored the android SDK on your system and open platform tools. For me it looked like this C:\Users\ifeanyi\AppData\Local\Android\Sdk\platform-tools . Then select the path tab above, type in cmd and press enter. The command promt should then open with the platform-tools directory.

Run the following command with your emulator running in the background

adb shell am start -a android.intent.action.VIEW –c android.intent.category.BROWSABLE -d "https://ifeanyi.web.app"

testdeeplink.gif

Right now all were doing is simply launching the app. Ideally you would want to get the link that was used to open the app and act accordingly depending on what the link contains.

For this we would make use of the Uni Links package. So first we'll add that as a dependency and run flutter pub get

dependencies:
  flutter:
    sdk: flutter
  uni_links: ^0.5.1

There are different way to handle incoming links using this package

  • getInitialLink: This returns the link the app was started with, if any. And it returns a String.
  • getInitialUri: Same as the getInitialLink, but returned as a Uri.
  • linkStream: This is important if your app is already running in the background and then opened via a link. In that case getInitailLink would be null and getLinksStream would get a new event holding the link.
  • uriLinkStream: Same as linkStream with Uri objects as its events.

So with this we can get the link the app was started with. I would use a FutureBuilder to demostrate this.

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<String>(
          future: getInitialLink(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Center(child: CircularProgressIndicator());
            }
            final link = snapshot.data ?? '';
            final message = link.isEmpty
                ? 'OPENED WITHOUT LINK'
                : 'OPENED WITH LINK\n$link';
            return Center(
              child: Text(
                message,
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: 40,
                  fontWeight: FontWeight.bold,
                ),
              ),
            );
          }),
    );
  }
}

So if getInitialLink doesnt return a link it means the app wasn't opened with a link and if it does have a link you can then do what you want with it, like navigating to a new page for example.

Opening app directly Vs through a link opened_with_without_link.gif

Next we also need to handle the situation where the app is already open and running in the backgroud and then opened via a link. To handle this I would simply wrap what we have in a StreamBuilder to show how it works.

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<String>(
          stream: linkStream,
          builder: (context, streamSnapshot) {
            final link = streamSnapshot.data ?? '';
            if (link.isNotEmpty) {
              return Center(
                child: Text(
                  'RETURNED WITH LINK\n$link',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 40,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              );
            }
            // ... return FutureBuilder above
          }),
    );
  }
}

Opening app directly and returning via a link return_with_link.gif

Conclusion

With deep linking you're basically registering your app to the OS as being capable of opening a particular link and the app should therefore be one of the options displayed to the user if a default app for that action hasn't being assigned.

While android deeplinks and iOS custom URL schemes are an acceptable form of deep linking, App links(android) and Universal links(iOS) are strongly recommended because:

  • Secure and specific: Android App Links and iOS Universal Links use HTTP URLs that link to a website domain you own, so no other app can use your links. For this to work you have to verify ownership of the domain through one of our website association methods.
  • Seamless user experience: No dialog appears for the user to select what app handles the link. It simply opens the app or redirects to the website if the app isn't installed.

If you would like to see an article on how to set up Applinks and Universal links do leave a comment.

Thanks for reading

The Full Code is available on GitHub

For more info on android deeplink you can visit the Android developer guide

For more info on Uni Link package you can check it out on pub dev.