自作アプリ「学びピン」に、
「YouTubeアプリ → 共有 → 学びピン」で動画を2タップ追加できる機能を入れました。
これまではURLをコピーしてアプリを開いて貼り付け…という導線でしたが、
共有シートから直接放り込めるようになりました。

使ったのは receive_sharing_intent。
Androidはあっさり、iOSは沼だったので、詰まったポイントと設定を実装記録として残します。
同じことをやる個人開発者の方の時短になれば。
仕組みはシンプル(Dart側はほぼ共通)
やっていることは、共有で飛んできたテキストから YouTube の URL を拾って、
既存の「動画を追加」フローに流すだけです。
受信部分の Dart コードは Android / iOS 共通で、プラットフォーム分岐は書いていません。
// アプリ起動中の共有
ReceiveSharingIntent.instance.getMediaStream().listen(_handleShared);
// 共有から起動された場合(コールド起動)
ReceiveSharingIntent.instance.getInitialMedia().then((v) {
_handleShared(v);
ReceiveSharingIntent.instance.reset(); // 二重処理防止に必須
});
YouTubeの共有本文は「タイトル+改行+URL」で飛んでくることが多いので、正規表現でURL部分だけ抜き出すのがコツです(watch / youtu.be / shorts 対応)。
Android:ほぼ詰まらない
pubspec.yaml に追加して、AndroidManifest.xml の MainActivity にテキスト共有のフィルタを足すだけ。
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
唯一ハマったのは、プラグインの JVMターゲット不一致(Java 1.8 と Kotlin 17)で assembleDebug が落ちたこと。ルートの android/build.gradle.kts で、このプラグインだけ Kotlin を 1.8 に揃えて解消しました。
subprojects {
if (project.name == "receive_sharing_intent") {
afterEvaluate {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>()
.configureEach { kotlinOptions { jvmTarget = "1.8" } }
}
}
}
豆知識として、共有シートの1画面目(おすすめ枠)に出すかどうかはOS側が決めるので、最初は「その他」から選ぶ形になります。使っていくうちに前面へ昇格します。設定ミスではありません。
iOS:ここからが本番(Share Extension の沼)
iOSはマニフェスト1行では出せず、Share Extensionというミニターゲットを追加する必要があります。
詰まった順に。
1. CocoaPods の ffi がビルドできない
新しいmacOSだと、OS標準の古いRuby(2.6)で ffi のネイティブビルドが失敗します。素直に Homebrew版のCocoaPods(brew install cocoapods)に切り替えるのが早いです。
2. Podfile は「入れ子」にする
共有拡張のターゲットは、Runnerと並列(トップレベル)に置くと Unable to integrate the following embedded targets で落ちます。Runnerの中に入れ子にするのが正解でした。
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'Share Extension' do
inherit! :search_paths
end
end
3. Build Phases の順序
Runnerのビルドフェーズで「Embed Foundation Extensions」を「Thin Binary」より上へ。
4. 最大の沼:'Flutter/Flutter.h' file not foundClang dependency scanning failure とセットで出るこれは、Xcode 26 の Explicitly Built Modules(明示的モジュールビルド)が原因の既知バグでした(Flutter issue #185210)。この方式をオフにすると通ります。プロジェクトと各Podに以下を設定。
SWIFT_ENABLE_EXPLICIT_MODULES = NO
CLANG_ENABLE_EXPLICIT_MODULES = NO
5. 対応iOSバージョン
拡張の Deployment Target を 14.0 に(プラグインの要件。13.0 だと minimum deployment target of iOS 14.0 で落ちます)。
6. App Group のスペース混入CUSTOM_GROUP_ID に末尾スペースが紛れていて、App Group名が一致せず共有データが渡らない、という地味なハマりもありました。目に見えないので要注意。
7. スプラッシュで止まる
起動はするのにスプラッシュから進まない時、原因がデバッグ接続(Dart compiler exited unexpectedly)なら、flutter run --release で切り分け・回避できます。
まとめ
- 共有を受け取るロジック自体はFlutterで完結し、Android/iOSで共通に書けた。
- 沼はほぼiOSのネイティブ設定側。特に Xcode 26 の新しいモジュールビルド方式は、既存の解説記事にまだ載っていないことが多く、ここに一番時間を取られた。
- 一度通してしまえば快適そのもの。ユーザーの「動画を追加する手間」が、コピペ往復から共有2タップに激減した。
同じ構成(Flutter + Firebase + Share Extension + Xcode 26)で詰まっている方は、まず Explicitly Built Modules をオフにするところから試してみてください。
