Config DeepLink in Flutter (EN)

Introduction

In this article, I will show how to set up deep links in Flutter by associating the Android app with a website and correctly routing pages using the go_router package. We will explore the configuration of AndroidManifest.xml, the domain verification process, and how to handle deep links in Flutter efficiently.

We will detail the three types of deep links offered in Android app links: Direct Links, Web Links, and Android App Links. Each allows the user to interact with the app in a specific way when clicking on a link.

Classic deep links allow specific URLs to redirect users to internal parts of the app. When a deep link is clicked, Android checks if there is an app that can open this URL. If one exists, it will be launched automatically or may open a disambiguation dialog.

How it works:

  • A deep link is mapped to a specific page or resource of the app.

  • If the app is installed, it will open on the specific screen according to the accessed URL.

  • If the app is not installed, the link will be handled by a web browser.

Example: In the example below, we have an AndroidManifest configured to open geographic schemes.

<activity
    android:name=".MyMapActivity"
    android:exported="true"
    ...>
    <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="geo" />
    </intent-filter>
</activity>

As you can see, it opened a disambiguation dialog allowing the link to be opened in either Maps or Chrome.

Advantages:

  • Allows quick access to specific parts of the app.

  • Optimizes user experience by redirecting to the correct content.

Disadvantages:

  • Only works if the app is installed.

Web links are normal URLs (HTTP or HTTPS) that, when clicked, can be opened either in a browser or in the app. If the Android app is set up to handle certain URLs, it can capture them and open the app instead of the browser. If the app is not available, the link will open in the browser.

The following code snippet shows an example of a web link filter:

<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="http" />
    <data android:host="myownpersonaldomain.com" />
</intent-filter>

How it works:

  • The user clicks on a normal web link (https://www.yoursite.com/product/123).

  • If the app is installed and set up to capture these links, it will open with the corresponding content.

  • If the app is not installed, the URL opens in the browser.

Example: A link like:

https://www.yoursite.com/product/123

would open the product 123 page in the app, if installed, or in the browser otherwise.

Advantages:

  • Works on both app and web, offering flexibility.

  • If the app is not installed, the content is still accessible.

Disadvantages:

  • Requires proper configuration for the app to capture the link.

Android App Links are an evolution of deep links and web links. They allow Android to automatically verify if the app can open a link from a given domain and open the app without showing a choice screen to the user. This is done through domain verification, ensuring the link belongs to the app, enhancing security.

The following code snippet shows an example of Android app link filters:

<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <data android:scheme="http" />
    <data android:scheme="https" />

    <data android:host="..." />
</intent-filter>

Notice the android:autoVerify=true in the intent filter, which is responsible for automatically filtering the link and opening the app without needing a disambiguation dialog.

How it works:

  • When a link is clicked, Android checks if the domain is verified for that app (through the assetlinks.json file).

  • If the app is trusted for that link, it will open the content directly in the app.

  • If the app is not installed, the link opens in the browser without showing a choice dialog.

Example:

A link like:

https://www.yoursite.com/product/123

would open directly in the app, if configured and the domain verified, or in the browser if the app is not installed.

Advantages:

  • Eliminates the choice screen, ensuring a smoother experience.

  • Domain verification provides greater security and control.

Disadvantages:

  • Requires domain verification and extra configuration to work properly.

Assetlinks.json File

The assetlinks.json file is essential for Android to recognize that your app is authorized to open links from your domain. It defines the relationship between the app and the website, including the permissions to handle URLs from the domain.

Content of the assetlinks.json file:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourapp.flutter",
      "sha256_cert_fingerprints": [
        "A1:B2:C3:D4:...:F5"
      ]
    }
  }
]
  • relation: Defines that the app can handle all URLs (common.handle_all_urls).

  • namespace: Defines that the application is of type android_app.

  • package_name: The package name of your Android app.

  • sha256_cert_fingerprints: The SHA-256 certificate of the app’s signature. This ensures that only your app can respond to these links.

Getting the SHA-256 Certificate

To get the SHA-256, run one of the following commands in the terminal (Linux or Mac) or in the command prompt (Windows) inside the Android folder of your Flutter app:

keytool -list -v -alias <your-key-alias> -keystore <path-to-your-keystore> | grep 'SHA256'
  • <your-key-alias>: The alias of your app’s signing key.

  • <path-to-your-keystore>: The path to your .keystore file.

./gradlew signingReport

After running, you will see the SHA-256 fingerprint, which should be inserted into the assetlinks.json file.

Setting Up Firebase Hosting to Host assetlinks.json

If you are using Firebase Hosting, the next step is to host the assetlinks.json file in the correct path.

Steps:

  1. Install Firebase CLI Tools: If you haven't installed Firebase CLI yet, do so with the following command:

     npm install -g firebase-tools
    
  2. Start Firebase Hosting: In your project directory, initialize Firebase Hosting:

     firebase init hosting
    

    This will generate a set of files in the directory, including firebase.json and a public folder.

  3. Add assetlinks.json to the correct directory: The assetlinks.json file must be hosted in the .well-known directory, located at the root of your website. Create this directory inside the public folder:

     mkdir -p public/.well-known
    

    Then, add the assetlinks.json file inside this directory:

     /public/.well-known/assetlinks.json
    
  4. Deploy to Firebase Hosting: After configuring the assetlinks.json file in the correct directory, deploy the site to Firebase Hosting:

     firebase deploy --only hosting
    
  5. Verify the URL of assetlinks.json: After deploying, you can check if the file was hosted correctly by accessing:

     https://www.yourdomain.com/.well-known/assetlinks.json
    

    Ensure that the file is publicly accessible and the content is correct.

Now that the assetlinks.json is hosted, you need to ensure that Android App Links are configured in your app. We have already shown how to configure the AndroidManifest.xml, so now you can test the behavior.

To verify if deep links and App Links are working correctly:

  1. Test Locally with ADB: You can test your deep link directly on a connected device with the ADB command:

     adb shell am start -W -a android.intent.action.VIEW -d "https://www.yourdomain.com/product/123" com.yourapp.flutter
    

    This will open the app directly on the product screen, assuming the configuration is correct.

  2. Check Domain Association: If the link is associated correctly, Android will open the app without asking the user if they want to open it in the browser or the app.

The go_router is an excellent library for managing routes in Flutter, especially when working with deep links. Below is an example of how to set up routes and handle deep link parameters:

final GoRouter _

router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomeScreen(),
    ),
    GoRoute(
      path: '/product/:id',
      builder: (context, state) {
        final String? id = state.params['id'];
        return ProductScreen(id: id);
      },
    ),
  ],
);

@override
Widget build(BuildContext context) {
  return MaterialApp.router(
    routerConfig: _router,
  );
}

Here, you define a dynamic route /product/:id, where the id will be extracted from the URL when the deep link is accessed.

Conclusion

In this article, we explored how to implement deep links in Android with Flutter, highlighting the types of links: Deep Links, Web Links, and Android App Links. We also covered the configuration of the assetlinks.json file, hosted on Firebase Hosting for domain verification, ensuring your Android app is recognized as responsible for opening links from your website. Additionally, we showed how to configure AndroidManifest.xml and use go_router in Flutter to manage routing. With this, you improve user navigation and integration between the web and the app.

0
Subscribe to my newsletter

Read articles from Daniel Augusto Jasmelino Filho directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Daniel Augusto Jasmelino Filho
Daniel Augusto Jasmelino Filho