FlutterでDynamic Linksを使ってみる

[Life is Tech！#1 Advent Calendar](https://qiita.com/advent-calendar/2020/life-is-tech)の2日目を担当するAndroid/WebSメンターのもなです.  

最近のスマホアプリ開発において, Googleが作成したオープンソースのUIソフトウェア開発キットである[Flutter](https://flutter.dev/)が用いられることが少なくありません.

今回はそんなFlutterでDynamic Linksを使ってみるという趣旨の記事を書いていきたいと思います. 

## Dynamic Linksとは?
Flutterについて知っている・聞いたことがあるという人は多い印象です. 一方, Dynamic Linksについては馴染みがない人も多いのではないでしょうか? Dynamic LinksはDeep Linkという「スマホアプリの特定の画面に移動するリンク」を扱いやすく拡張したものです.

正式には, [Firebase Dynamic Links](https://firebase.google.com/products/dynamic-links/)といい, 以下のような説明がされています.

> Dynamic Links は、既存のユーザーと潜在的ユーザーを iOS または Android アプリ内の任意の場所に誘導できるスマートな URL です。Dynamic Links を使用すれば、ユーザーをモバイル ウェブサイトからアプリ内の同等のコンテンツにシームレスに誘導できます。

URLを開くと通常Webブラウザが表示されますが, InstagramやSpotifyなどの特定のアプリが起動したという経験をしたことはありませんか? そのようなURLリンクにはDeep Link(Dynamic Links)が用いられています. またDynamic Linksを用いることでアプリがインストールされていない場合にWebブラウザを表示させたりアプリストアに遷移させたりとカスタマイズすることができます.

それでは実際にFlutterでDynamic Linksを使ってみましょう.

## 事前準備
Dynamic Linksを用いるためにはFlutterアプリをFirebaseと連携する必要があります. この作業はiOS/Androidで勝手が異なるため下記のリンクをもって割愛させていただきます.

[Flutter アプリに Firebase を追加する](https://firebase.google.com/docs/flutter/setup?hl=ja)

また今後の説明では, Androidで連携したという条件でお話しを進めていきます.

## Dynamic Linksを設定する
Dynamic Linksを作成する方法はいくつかあります. 最も簡単なのはFirebaseのコンソール上で作成することです.

まず, Flutterアプリと連携したFirebaseコンソールを起動し, 拡大の`Dynamic Links`タブを開きましょう.
![1-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/e5033d60-b5d9-cb8f-e3a4-c2cff9f9bb4d.png)
すると上記のような画面が表示されるので`始める`を押します.
![2-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/008a2cdc-6ba8-faa2-4f51-28f1db3e2553.png)
次に, URL接頭辞の追加を行っていきます. 基本的にはデフォルトで問題はありません.
ドメインのテキストフィールドをクリックするとデフォルトで提供されるドメインが表示されるので選択しましょう.`続行`ボタンを押して次のような画面になったら`終了`ボタンをおしてURL接頭辞の追加は完了です.
![3-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/16a82dce-3751-bc8d-8f0e-519d91e7f07b.png)

次に, 実際にダイナミックリンクを作成していきます. URLの接頭辞が設定できていれば, `Dynamic Links`画面が以下のようになっているはずです.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/0fc5bd3f-17e4-7686-c182-81916fa39eb9.png)
早速, Dynamic Linksを作成していきましょう. `新しいダイナミック リンク`ボタンを押します.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/432f0386-c222-bb0f-e0c4-b8bc853e4eb9.png)
まず最初に, `短縮URLのリンク設定`を行います.今回は`https://monapk.page.link/test`とします.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/445a9e86-297b-7a0e-2fcc-63055df5a917.png)
次に, `ダイナミック リンクの設定`です.ここで指定する`ディープリンクURL`はPCなどでURLを開いた場合やアプリ未インストール時のデフォルトの遷移先をしていします. また`ダイナミックリンク名`には名前の通り作成するDynamic Linkに対して名称を付けます. 後々, クリック回数等のデータを解析することができるので適切な名前を設定しておきましょう.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/aa812b52-a8cd-c030-ed1d-415ec6d26e94.png)
そして, iOS/AndroidごとにDynamicLinksを開いた時の処理を設定します. 大別すると`ブラウザでDeepLinkを開く`, `アプリ内でDeepLinkを開く`という2種類に分かれます. `アプリ内でDeepLinkを開く`を指定している場合, アプリのインストール状況に応じて遷移先を決定することができます. カスタムURLを用いることでアプリ未インストール時, 各種ストアの代わりに[Deploy Gate](https://deploygate.com/?locale=ja)のようなアプリ配布サイトに遷移させることも可能です.

最後の, `キャンペーン トラッキング、ソーシャルタグ、詳細オプション（省略可）`は省略します.稀に作成ボタンを押しても反映されないことがあるのでそのような場合は画面更新を行いましょう.

Dynamic Linksの設定は以上となります. この段階で今回作成したDynamic Link(https://monapk.page.link/test) を開くとデフォルトのURLで指定していたQiitaのサイトにリダイレクトされます.

## Dynamic Linksをアプリで受け取る
それでは実際にアプリ側でDynamic Linksを受け取っていきましょう. 今回, Dynamic Linksでアプリが開かれた場合Deep Linkの値を表示するシンプルなアプリを作成します.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/851813/09b2b32a-bd58-0097-b51b-7a400fecee97.png)

まず,[firebase_dynamic_links](https://pub.dev/packages/firebase_dynamic_links)というパッケージを使用します. このパッケージを用いることでアプリがDynamic LinkまたはDeep Linkで開かれたのかを検知したり, 動的にDynamic Linkを作成したりすることができるようになります.

```pubspec.yaml
dependencies:
  firebase_dynamic_links: ^0.6.1
```

次にサンプルアプリのメイン実装です. とは言っても実装のほとんどがパッケージ任せであるため完結に.

```main.dart

class _MyHomePageState extends State<MyHomePage> {
  String _deepLink = "null";

  @override
  void initState() {
    super.initState();
    // 1. Dynamic Linkを受け取ったのかについて確認
    _initDynamicLinks();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Dynamic Link',
            ),
            Text(
              _deepLink,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
    );
  }

  void _initDynamicLinks() async {
    // 2. FirebaseDynamicLinksのpackageで提供されているメソッド
    FirebaseDynamicLinks.instance.onLink(onSuccess: (PendingDynamicLinkData dynamicLink) async {
      final Uri deepLink = dynamicLink?.link;
			
      if (deepLink != null) {
	    // 3. アプリ立ち上げ中に即時実行するとnullが渡る可能性があるので2~3秒停止
        await Future.delayed(Duration(seconds: 3));
        setState(() {
          _deepLink = deepLink.toString();
        });
      }
    }, onError: (OnLinkErrorException e) async {
      print('onLinkError');
      print(e.message);
    });
  }
}
```

流れとしては, アプリ起動時にDynamic Linksを受け取っているのかを`initState`内で確認しているだけです. 一方, コンソール上で設定したデフォルトのDeep Linkしか受け取れないという欠点があります.

## Dynamic Linksを動的に作る
Firebaseコンソール上では静的なDynamic Linkを設定することができましたが, Flutterアプリ上からfirebase_dynamic_linksパッケージを用いて以下のように動的にDynamic Linkを作成することもできます.

```dart
  Future<Uri> createInviteDynamicLink({String parameter}) async {
    final DynamicLinkParameters parameters = DynamicLinkParameters(
      uriPrefix: 'https://monapk.page.link', // URL接頭辞
      link: Uri.parse('https://qiita.com/?query=$parameter'), //デフォルトのDeep Link
      // Android用の設定
      androidParameters: AndroidParameters(
        packageName: 'package_name',
        minimumVersion: 1,
        fallbackUrl: Uri.parse('https://...'), //アプリ未インストール時の遷移先
      ),
      // iOS用の設定
      iosParameters: IosParameters(
        bundleId: 'your_ios_bundle_identifier',
        minimumVersion: '1',
        appStoreId: 'your_app_store_id',
        fallbackUrl: Uri.parse('https://...'),
      ),
    );
    var dynamicUrl = await parameters.buildUrl();
    return dynamicUrl;
  }
```

基本的には, Firebaseのコンソール上で設定したことをコードで表現し直している形になります. しかし, デフォルトのDeep Linkに対してクエリパラメータを動的に付与することができるので細かいハンドリングもできるようになります. 


今回の記事はDynamic Linksに対する簡単な紹介でした. URLを開いてアプリが起動したらDynamic LinksやDeep Linkが使われているということだけでも覚えていただけると幸いです!

## 参考リンク
[Firebase + Flutter — Dynamic Links — Step By Step Guide](https://medium.com/dev-genius/firebase-flutter-dynamic-links-step-by-step-guide-630402ee729b)