Table of Contents
- 前提
- ホスト(ネイティブ)側のコード
- iOS
- Android
- Flutter側のコード
前提
ネイティブからFlutterにURIを渡すためには、EventChannelを利用すると思います。単純にFlutter側でmain()
でリスナーを設定し、ネイティブでapplication(_:open:options:)
やonNewIntent
などでURIを送り込むと、アプリ起動中にURIが流れてきた場合は動作するしれませんが、URIによってアプリが起動された際にはURIを渡すことができません。何故なら、Flutterエンジンの初期化には時間がかかるためです。
そのため、リッスンされるまではネイティブのコードでURIを貯蔵しておき、リッスンが開始された際に流し込む、というようなアプローチが必要になります。
ホスト(ネイティブ)側のコード
iOS
class UriEventApi: NSObject {
static let channelName = "com.example.app.event/uri" // Channel name (任意に変更)
var channel: FlutterEventChannel
var eventSink: FlutterEventSink? // リッスンが開始されたタイミングで代入され、キャンセルされたタイミングでnilにされる
var pendingUri: String?
init(binaryMessenger: FlutterBinaryMessenger) {
channel = FlutterEventChannel(name: UriEventApi.channelName, binaryMessenger: binaryMessenger)
}
func initHandler() {
channel.setStreamHandler(self)
}
func onUri(uri: String) {
if (eventSink != nil) { // Flutter側で既にリッスンが開始されている場合、
eventSink!(uri) // そのままeventSinkを発火させる
} else { // リッスンが開始されていない = まだFlutterエンジンが初期化されていない 場合、
pendingUri = uri // pendingUriに代入して貯めておく
}
}
}
extension UriEventApi: FlutterStreamHandler {
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { // リッスンが開始された際、
eventSink = events
if (pendingUri != nil) { // 保留中のURIが存在する場合、
eventSink!(pendingUri) // eventSinkを発火させてFlutter側に伝える
pendingUri = nil
}
return nil
}
func onCancel(withArguments arguments: Any?) -> FlutterError? {
eventSink = nil
return nil
}
}
Android
// 実装の内容はiOS(Swift)と同様
class UriEventApi(binaryMessenger: BinaryMessenger) : EventChannel.StreamHandler {
private val channel = EventChannel(binaryMessenger, CHANNEL_NAME)
companion object {
const val CHANNEL_NAME = "com.example.event/uri"
}
private var eventSink: EventChannel.EventSink? = null
private var pendingData: String? = null
fun initHandler() {
channel.setStreamHandler(this)
}
fun onUri(uri: String) {
if (eventSink == null) {
pendingData = uri
} else {
eventSink!!.success(uri)
}
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
eventSink = events
if (pendingData != null) {
eventSink!!.success(pendingData)
pendingData = null
}
}
override fun onCancel(arguments: Any?) {
eventSink = null
}
}
Flutter側のコード
扱いやすくするためにUriEventApi
という名前でクラスを作ります。
// lib/event_api/uri_event_api.dart
class UriEventApi {
static const _eventName = "com.example.app.event/uri";
static const _channel = EventChannel(_eventName);
StreamSubscription listen() {
return _channel.receiveBroadcastStream().listen((uri) {
// process
});
}
}
これを任意のタイミングでリッスンします。
UriEventApi().listen();
このコードで基本的には動くはずです。