Zenn
🕌

【Flutter】DeepLinkでアプリを往復するには

2025/03/16に公開

二つのアプリ間で情報の連携を行うために、DeepLinkを使いたいと思ったのですが、意外とアプリ間を往復する動きが紹介されている記事があまりなかったので実験してみました。

実装のイメージ

アプリA、アプリBが存在するときに以下のような挙動を想定してます

  1. アプリAがアプリBに情報を問い合わせるDeepLinkをコール
  2. アプリBが立ち上がり、アプリBの中でアプリAに渡すための情報を準備する
  3. アプリBでアプリAに情報を渡すDeepLinkをコール
  4. アプリAが立ち上がり、アプリBの情報を受け取る
  5. アプリAでアプリBから受け取った情報をもとに処理を行う

色々なアプリを調べてみましたが、Amazonのアプリで、Paypayの支払い方法を登録するときの動きがイメージに近かったです。

シーケンス図

実装してみた

A,B二つのアプリを作成しました。
実際のコードもGitHubにあげました。

アプリA(最初に起動されているアプリで、IDを要求する側)

https://github.com/Yuta-KTD/deep_link_interactive

アプリB(IDを発行する側)

https://github.com/Yuta-KTD/deep_link_interactive_sub
実行環境はFlutter3.29.2になります

$ flutter --version
Flutter 3.29.2 • channel stable • https://github.com/flutter/flutter.git
Framework • revision c236373904 (3 days ago) • 2025-03-13 16:17:06 -0400
Engine • revision 18b71d647a
Tools • Dart 3.7.2 • DevTools 2.42.3

仕組みとしては、アプリBを呼び出す時にURLスキームにホスト名 fetchid をつけて呼び出します。
そのホスト名をアプリB側で確認して、fetchidであればアプリB側でIDを発行して、アプリAを呼び出す際にIDをクエリパラメーターとして付与する、といった流れです。

結論、想定通りに動いてくれました🎉

※ アプリBに遷移用のボタンも配置しましたが、タップせずに遷移できてます。ややこしくてごめんなさい。

iOS Android
iOS_result android_result

個人的にこんな感じのシームレスなアプリ間の遷移はみていて気持ちがいいです。
UXを考えると、アプリ間を遷移することを知らないユーザーにとっては少し挙動に不気味さを覚えることもあるので、遷移することをダイアログ通知してあげることも一つの手段だと思います。

実装の中で気づいた制約

本検証の中で気づいた制約について紹介します。
主にDeepLink周りの制約になります。

[Android]Schemeを大文字小文字まで合わせないとアプリを呼び出せない

adbコマンドを使用してアプリ呼び出しをテストしていましたが、最初は呼び出せず苦労してました。

$  adb -d shell am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "deepLinkMainScheme://open"
Starting: Intent { act=android.intent.action.VIEW cat=[android.intent.category.BROWSABLE] dat=deepLinkMainScheme:///... }
Error: Activity not started, 

AndroidManifestの設定を変えたりして色々試した結果、AndroidManifestの指定と、呼び出しの大文字小文字を揃える必要があることに気づきました。
アプリ内部で launchUrl で呼び出す時は、基本全て小文字になるのでAndroidManifest側も小文字にしました。

android/app/src/main/AndroidManifest.xml
<data android:scheme="deeplinkmainscheme"
    android:host="open"/>

[Android]Impellerを使用すると描画に関するエラーが発生し、遷移後にアプリが動作しないことがある

実機(Pixel6a/Android14)で何回か動作確認していると、実行ログに下記のようなエラーが出力されていて、画面が真っ黒になってDeepLinkが動作しないことがありました。
android_impeller
👇はアプリB側のログになります。

D/com.llfbandit.app_links(22307): Handled intent: action: android.intent.action.VIEW / data: deeplinksubscheme://fetchid
I/ContentCaptureHelper(22307): Setting logging level to OFF
E/gralloc4(22307): ERROR: Format allocation info not found for format: 3b
E/gralloc4(22307): ERROR: Format allocation info not found for format: 0
E/gralloc4(22307): Invalid base format! req_base_format = (<unrecognized format> 0x0), req_format = (<unrecognized format> 0x3b), type = 0x0
E/gralloc4(22307): ERROR: Unrecognized and/or unsupported format (<unrecognized format> 0x3b) and usage (CPU_READ_NEVER|CPU_WRITE_NEVER|GPU_TEXTURE|GPU_RENDER_TARGET|COMPOSER_OVERLAY 0xb00)

動画やログを見る感じ、アプリA→Bの遷移まではできていそうです。
その後に gralloc4 を用いた描画に関するメモリの割り当てに失敗しているように見えます。
なんとなくですが、レンダリングエンジン周りが関係してそうだったので、Impellerを無効化したところ、問題が解消しました。

android/app/src/main/AndroidManifest.xml
<meta-data
    android:name="io.flutter.embedding.android.EnableImpeller"
    android:value="false" />

似たようなエラーでAdmob関連のIssueがありました。
ここでは遅延処理を入れて解決できたみたいですが、端末によっては遅延が間に合わないこともあるかなと思います。
残念ですが根本原因の特定や、その解決ができるまではSkiaを使おうかなと思います。

[iOS]ルート定義をしないと、ホスト指定したDeepLink呼び出しでページがスタックされる

あまりないかもしれませんが、GoRouterなどでルート定義をしなかった場合、ホスト指定したDeepLinkでアプリが呼び出されると、意図しないページが作成されてしまいます。
GoRouterで明示的にページ構成を定義することで回避できました。

参考

https://zenn.dev/matsumaru/articles/fd63cf2793638f
https://zenn.dev/msorz/articles/72a282ff71a350
https://docs.flutter.dev/perf/impeller

ENECHANGE

Discussion

ログインするとコメントできます