Flutter - Using Firebase Dynamic Links

This tutorial shows you how to use Firebase Dynamic Links in Flutter, which enables deep linking to your Flutter application.

A deep link is a link that can be used to redirect users to a mobile application. With deep linking, you can control to which specific location of the application the user should be redirected to. If the user doesn't have your application installed on the device, you can also set how to redirect the user. For example, you can redirect the user to your web page or the installation page of Android PlayStore or Apple AppStore, which may increase the chances for new users to install your application.

You can implement your own deep linking service. But if you are looking for a ready-to-use and free deep linking service, you can use Firebase Dynamic Links. The cloud service allows you to create some links and configure the behavior of each link. Below is the tutorial of how to use Firebase Dynamic Links in your Flutter application.

Integrate Firebase to Flutter Application

Like using other Firebase services, you need to integrate your Firebase account with your Flutter application. Basically, what you need to do is create a Firebase project and add application(s) to the Firebase project. Then, download and copy the config files to your Flutter project. You can read the details in the tutorial below.

To use Dynamic LInks in a Flutter application, you can use firebase_dynamic_links package. Add the below dependency to your pubspec.yaml file.

  dependencies:
    firebase_dynamic_links: ^2.0.1

Set Up Domain

Before creating Dynamic Links, you need to set up a domain for the links that you will create in the future. You can open Firebase Console, then navigate to Dynamic Links using the sidebar menu. If you haven't used Dynamic Links, there should be a Get Started button. Just click the button, it should display a popup that asks you to enter a prefix URL. If you choose to use the Google-provided domains, the set up should be very easy. You only need to enter the domain name that you want to use which must have .page.link suffix.

Firebase Dynamic Links - Add Domain

If you choose to use your own custom domain, the process is a bit complicated since Firebase will ask you to add a TXT to your DNS provider in order to verify the ownership of the domain.

Configuration for iOS

If you need to receive dynamic links on iOS, you need to configure a few things.

First, go to the Info tab of Xcode project. Then, create a new URL Type to be used for Dynamic Links. Still on the same tab, enter a unique value for the Identifier field and your bundle identifier for the URL Schemes field.

On the Capabilities tab, you need to enable Associated Domains and add the following. The value must not include https:// or any slashes or path

  applinks:YOUR_URL_PREFIX

If you use a custom domain, create a key called FirebaseDynamicLinksCustomDomains in Info.plist file. The value is your app's Dynamic Link URL prefixes.

  <key>FirebaseDynamicLinksCustomDomains</key>
  <array>
    <string>https://woolha.com/tutorials</string>
    <string>https://woolha.com/other</string>
  </array>

Configuration for Android

In Android, it's quite common to let the users choose whether to open the link in the application or a mobile browser by showing a dialogue. By default, if you create a Dynamic Link and configure to open the link in an Android application, the dialog will not be shown. To make the dialog appears when an Android user open the link, you need to add an intent-filter in the AndroidManifest.xml file

  <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="YOUR_SUBDOMAIN.page.link"
    />
  </intent-filter>

After creating the domain, now you can add some links to your project. There are some ways to add a link. You can create a link from the Firebase Console, using API call from your backend server, and even from your mobile application. This tutorial only shows how to add a link from the Firebase Console and from a Flutter application with the help of firebase_dynamic_links package.

Firebase provides an easy way to add dynamic links using web interface. After you have set up a domain, if you open the Dynamic Links menu, there should be a list of the created links. If you haven't created any link, the list should be empty. To add a new dynamic link, click the New Dynamic Link button.

Firebase Dynamic Links - List

First, you need to set up a short URL link. It's a link that's managed by Firebase. If someone accesses the link, Firebase can control the behavior which can be set up in the next steps.

Firebase Dynamic Links - Short URL Link

Next, you need to set up a dynamic link URL, which is a deep link into your application. If the user has the app installed, the deep link will be passed to the application and you can determine what actions to do when the application receives the link. On desktops, the defined URL will be opened in a browser.

Firebase Dynamic Links - Set Up Dynamic Link

After that, you need to define the link behavior for iOS. You can choose to open the link in a browser or open it in your iOS application. If you choose open in your iOS application, you need to select which application to use (or create a new one). If the application is not installed, you have to define the behavior, whether to open the App Store page for your application or redirect to a custom URL. There are also some advanced settings as well.

Besides iOS, you also have to define the behavior for Android. The options are very similar, you can select whether to open the link in a browser or an Android application. If you opt to open the link in an Android application, you need to define the behavior if the app isn't installed, either open the Google Play page that allows users to install or open a custom URL or Google Play Instant Experience.

Firebase Dynamic Links - Android Behavior

Optionally, you can also set campaign trackings, social tags, and other advanced options.

Firebase Dynamic Links - Advanced Options

You can create a dynamic link programmatically in a Flutter application. First, you need to define the dynamic link by creating a DynamicLinkParameters instance. Below is the constructor that you need to use.

  DynamicLinkParameters({
    AndroidParameters? androidParameters,
    required String uriPrefix,
    DynamicLinkParametersOptions? dynamicLinkParametersOptions,
    GoogleAnalyticsParameters? googleAnalyticsParameters,
    IosParameters? iosParameters,
    ItunesConnectAnalyticsParameters? itunesConnectAnalyticsParameters,
    required Uri link,
    NavigationInfoParameters? navigationInfoParameters,
    SocialMetaTagParameters? socialMetaTagParameters,
  });

There are two required parameters: uriPrefix and link. The URI prefix is the domain URI prefix for your application, while link is the link the target app will open. You can also define the behavior for Android and iOS by passing androidParameters and iosParameters respectively.

For Android, you need to pass AndroidParameters as androidParameters argument. The constructor of AndroidParameters class has the following parameters:

  • Uri? fallbackUrl: The link to open when the app isn’t installed.
  • int? minimumVersion: The minimum version of the application that can open the link.
  • String packageName: The Android app’s package name.

For iOS, you need to pass IosParameters as iosParameters argument. The constructor of IosParameters class has the following parameters:

  • String? appStoreId: The link to open when the app isn’t installed.
  • required String bundleId: The bundle ID of the iOS app to use to open the link.
  • String? customScheme: The target app’s custom URL scheme.
  • Uri? fallbackUrl: The link to open when the app isn’t installed.
  • String? ipadBundleId: The bundle ID of the iOS app to use on iPads to open the link.
  • Uri? ipadFallbackUrl: The link to open on iPads when the app isn’t installed.
  • String? minimumVersion: The minimum version of the app that can open the link.

Besides the platform-specific behavior, you can also define the parameters for Google Analytics, iTunes Connect Analytics, navigation info, and social meta tags.

Below is a basic usage example which only configures the behaviors for Android and iOS.

  Future<void> _createDynamicLink() async {
    final DynamicLinkParameters parameters = DynamicLinkParameters(
      uriPrefix: 'https://woolha.page.link/tutorials',
      link: Uri.parse('https://woolha.com/tutorials'),
      androidParameters: AndroidParameters(
        packageName: 'com.woolha.example',
        minimumVersion: 1,
      ),
      iosParameters: IosParameters(
        bundleId: 'com.woolha.example',
        minimumVersion: '1',
        appStoreId: '123',
      ),
    );

    final ShortDynamicLink shortDynamicLink = await parameters.buildShortLink();
    final Uri shortUrl = shortDynamicLink.shortUrl;
  }

After adding firebase_dynamic_links package and running flutter pub get command, you need to import the package in the file where you want to use it.

  import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';

After that, you can get the singleton instance of FirebaseDynamicLinks by calling FirebaseDynamicLinks.instance. The application can receive a dynamic link regardless it's running or not and we need to handle both conditions. If the application is not running, you can call the getInitialLink method, which returns Future<PendingDynamicLinkData?>. The deep link parameter can be obtained by accessing the link property of PendingDynamicLinkData. Then, you can determine what action to do based on the link. For example, you can navigate to a particular route.

  final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
  final Uri deepLink = data?.link;

  if (deepLink != null) {
    Navigator.pushNamed(context, deepLink.path);
  }

You also need to handle the condition when the application is active. You need to use the onLink method. There are two callbacks that you can pass: onSuccess and onError. The success callback accepts a parameter whose type is PendingDynamicLinkData, which allows you to get the URI and then determine the action to do. The error callback is used to handle occurred errors.

  FirebaseDynamicLinks.instance.onLink(
    onSuccess: (PendingDynamicLinkData dynamicLink) async {
      final Uri deepLink = dynamicLink?.link;

      if (deepLink != null) {
        Navigator.pushNamed(context, deepLink.path);
      }
    },
    onError: (OnLinkErrorException e) async {
      Navigator.pushNamed(context, '/error');
    },
  );

The code above should be called when the application is launched. You can call it inside the initState method of a page that becomes the main entry-point route.

  class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
  
    @override
    void initState() {
      super.initState();
      this._initDynamicLinks();
    }
  
    void _initDynamicLinks() async {
      FirebaseDynamicLinks.instance.onLink(
          onSuccess: (PendingDynamicLinkData dynamicLink) async {
            final Uri deepLink = dynamicLink?.link;
  
            if (deepLink != null) {
              Navigator.pushNamed(context, deepLink.path);
            }
          },
          onError: (OnLinkErrorException e) async {
            Navigator.pushNamed(context, '/error');
          }
      );
  
      final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
      final Uri deepLink = data?.link;
  
      if (deepLink != null) {
        Navigator.pushNamed(context, deepLink.path);
      }
    }
  }

Full Code

  import 'dart:async';
  import 'package:flutter/material.dart';
  import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
  
  void main() => runApp(MyApp());
  
  class MyApp extends StatelessWidget {
  
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Woolha.com Flutter Tutorial',
        routes: {
          '/': (context) => HomePage(),
          '/tutorials': (context) => TutorialsPage(),
          '/error': (context) => ErrorPage(),
        },
      );
    }
  }
  
  class HomePage extends StatefulWidget {
  
    @override
    _HomePageState createState() => _HomePageState();
  }
  
  class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
  
    String _shortUrl = '';
  
    @override
    void initState() {
      super.initState();
      this._initDynamicLinks();
    }
  
    void _initDynamicLinks() async {
      FirebaseDynamicLinks.instance.onLink(
          onSuccess: (PendingDynamicLinkData dynamicLink) async {
            final Uri deepLink = dynamicLink?.link;
  
            if (deepLink != null) {
              Navigator.pushNamed(context, deepLink.path);
            }
          },
          onError: (OnLinkErrorException e) async {
            Navigator.pushNamed(context, '/error');
          }
      );
  
      final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
      final Uri deepLink = data?.link;
  
      if (deepLink != null) {
        Navigator.pushNamed(context, deepLink.path);
      }
    }
  
    Future<void> _createDynamicLink() async {
      final DynamicLinkParameters parameters = DynamicLinkParameters(
        uriPrefix: 'https://woolha2.page.link',
        link: Uri.parse('https://woolha.com/tutorials'),
        androidParameters: AndroidParameters(
          packageName: 'com.woolha.example',
          minimumVersion: 1,
        ),
        iosParameters: IosParameters(
          bundleId: 'com.woolha.example',
          minimumVersion: '1',
          appStoreId: '123',
        ),
      );
  
      final ShortDynamicLink shortDynamicLink = await parameters.buildShortLink();
      final Uri shortUrl = shortDynamicLink.shortUrl;
  
      setState(() {
        _shortUrl = shortUrl.toString();
      });
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
        ),
        body: SizedBox(
          width: double.infinity,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              OutlinedButton(
                child: const Text('Create Link'),
                onPressed: () {
                  _createDynamicLink();
                },
              ),
              SelectableText(_shortUrl),
            ],
          ),
        ),
      );
    }
  }
  
  class TutorialsPage extends StatelessWidget {
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
        ),
        body: const Center(
          child: const Text('Tutorials Page'),
        ),
      );
    }
  }
  
  class ErrorPage extends StatelessWidget {
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
        ),
        body: const Center(
          child: const Text('Error Page'),
        ),
      );
    }
  }

Summary

With Firebase Dynamic Links, you can create deep links to your Flutter application on Android and iOS platforms. You need to create links from the Firebase Console, your backend server, or your mobile application and configure the behavior of each link. The deep links will be sent to your application and you can determine what should happen when the application receives a link. The firebase_dynamic_links package makes it easier to create and receive Dynamic LInks.

You can also read our tutorial about: