くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

FlutterでDeepLinkに対応する(Android/App Links)

Webサイトの特定のURLにアクセスするとアプリが開く、
Deep Linkに対応したいなと思い、
いろいろ調べてみたときの備忘録(*´ω`*)

Flutterの公式ドキュメントはこのあたり

routing packageに、go_routerを利用しているので、
そのあたりも含めて(*´ω`*)

AndroidManifest.xmlの変更

.MainActivity<activity>配下に、以下を追加する。
android:host="example.com"の部分は自分のドメインで。

  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application ...>
      <activity android:name=".MainActivity" ...>
        <!-- 略 -->
+       <meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
+       <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" android:host="example.com" />
+         <data android:scheme="https" />
+       </intent-filter>
      </activity>
    <application>
  </manifest>

assetlinks.jsonの配置

以下のファイルを作成して、Webサイトに配置する。
package_namesha256_cert_fingerprintsは、
自分の環境にあわせて設定する。

<自分のdomain>/.well-known/assetlinks.json

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.deeplink_cookbook",
    "sha256_cert_fingerprints":
    ["FF:2A:CF:7B:DD:CC:F1:03:3E:E8:B2:27:7C:A2:E3:3C:DE:13:DB:AC:8E:EB:3A:B9:72:A1:0E:26:8A:F5:EC:AF"]
  }
}]

sha256_cert_fingerprintsは以下から取得できる。

  • GooglePlayの場合は、
    • 「対象のアプリ>設定>アプリの署名」から
  • ローカルの場合は、
    • keytool -list -v -keystore <path-to-keystore>
    • cd android && ./gradlew app:signingReport

go_routerの設定

基本的はGoRouteで設定したパスと一致していればOK
ただ、アプリが起動中と起動前では渡されてくるURIが異なるので注意

  • 起動中 ... パスのみ(https://my-site/aaa/bbb/aaa/bbb)
  • 起動前 ... ホストなども含む(https://my-site/aaa/bbbのまま)

そのため、uri.hosturi.schemeを確認して、
uri.pathへのリダイレクトが必要。

import 'package:go_router/go_router.dart';

const router = GoRouter(
  // ...略
  redirect: (context, state) {
    if (state.uri.host.isEmpty || state.uri.scheme.isEmpty) {
      return state.uri.path;
    }
    return null;
  },
);

Tips

特定のパスのみアプリを開く

shemehost以外に、pathPatternも指定できる。

<data android:scheme="https" android:host="maguro-sagashi.com" android:pathPattern="(/..)?/stages/.*" />

他にも、pathpathPrefixpathSuffixなどもある。

複数のドメインで対応する

各サイトでassetlinks.jsonを配置し、
AndroidManifest.xmlに複数行かけばOK

<data android:scheme="https" android:host="maguro-sagashi.com" android:pathPattern="/stages/.*" />
<data android:scheme="https" android:host="maguro-sagashi.com" android:pathPattern="/../stages/.*" />
<data android:scheme="https" android:host="www.maguro-sagashi.com" android:pathPattern="stages/.*" />
<data android:scheme="https" android:host="www.maguro-sagashi.com" android:pathPattern="/../stages/.*" />

Webサイトとgo_routeのパスが異なる場合

go_routerのredirectで対応すればOK

マグロ探しの場合、英語と日本語でパスが変わるので、
そういった場合も含めて、redirectする必要がある。

// JP: https://maguro-sagashi.com/stages/inori
// EN: https://maguro-sagashi.com/en/stages/inori

import 'package:go_router/go_router.dart';

const router = GoRouter(
  // ...略
  redirect: (context, state) {
    final stageIdMatch = RegExp(r'/stages/([^/]+)$').firstMatch(state.uri.path);
    if (stageIdMatch != null) {
      return "/creative/stage/${stageIdMatch.group(1)}";
    }
    if (state.uri.host.isEmpty || state.uri.scheme.isEmpty) {
      return state.uri.path;
    }
    return null;
  },
);

複数のアプリが立ち上がらないようにする

AndroidManifest.xmllaunchModesingleInstanceにすればOK

- <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" ...>
+ <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleInstance" ...>

以上!! これでいい感じに遊べる気がする(*´ω`*)

参考にしたサイトさま