はじめに
Flutter (Dart) とプラットフォーム (Android/iOSなど) 間の通信/呼び出しAPIについてまとめています。
- MethodChannel
- EventChannel ← 今回
- MessageChannel
1. EventChannelとは
プラットフォーム側からFlutter (Dart) 側にイベント通知するためのAPIの一つであるEventChannelについて解説します。
加速度センサーの値の変化やプラットフォーム側のイベントサイクルの変化など、プラットフォーム側で定期的もしくは突発的に発生するイベントをFlutter側に通知するためのAPIです。Flutterの仕組み的には、ここで解説しているPlatform Channelsを利用しています。
2. 基本フロー
実際のコードの説明の前にAPIの基本的なフローについて解説します。
ただし、プラットフォーム毎に若干API名が異なる場合があるため (iOS) 、注意してください。
2.1 プラットフォーム → Dart
EventChannelはプラットフォーム側からDart側へのみの一方通行の1対多?の通信APIです。MethodChannelとは異なり、逆方向は出来ません。AndroidフレームワークのBroadcastのイメージを持っていただければ分かり易いかと思います。
Dart側
- EventChannel#receiveBroadcastStreamでプラットフォーム側のイベント受信用のリスナーを登録
- 受信を終了時、StreamSubscription#cancelをコール
プラットフォーム側
- EventChannel#setStreamHandlerでイベントリスナーを登録
- EventChannelの初期化が完了後、onListerがコールされるので、その引数のEventSinkをローカルに保存
- EventSink#successでイベント送信
- 全てのイベント送信完了後にendOfStreamをコール
3. サンプルプロジェクト
実際のAPIの使い方は後述しますが、サンプルプロジェクトを以下に用意しています。そのまま動作確認可能ですので、参考にしてください。
ただし、正式に何かの機能を実装する場合にはプラグインの形で組み込んだ方が良いです。
https://github.com/Kurun-pan/flutter-eventchannel-example
4. Flutter + Android (Kotlin) のコード解説
4.1 Dart側の実装
通信チャンネル作成
MethodChannel同様、EventChannelの通信チャンネルを作成します。引数に指定する文字列は他のアプリケーションと被らず、一意に決まるようにするために慣例で"アプリパッケージ名/チャンネル名"とするのが一般的な様子です。
イベント受信リスナーの登録
作成したチャンネルのreceiveBroadcastStreamでStreamSubscriptionインスタンスが取得出来ます。このインスタンスのlistenメソッドでイベント受信リスナーを登録します。
listenメソッドのcancelOnError引数は、プラットフォームからの送信イベントがエラー時に自動的に通信を終了 (cancel) するかどうかの設定です。
StreamSubscription<T> listen(void onData(T event),
{Function onError, void onDone(), bool cancelOnError});
受信終了
これ以上受信が不要になった場合には、StreamSubscription#cancelをコールします。すると、プラットフォーム側にもキャンセルされたことが通知され、通信が完全に終了します。
サンプルコード
class _MyHomePageState extends State<MyHomePage> {
static const EventChannel _channel = const EventChannel('com.example.eventchannel/interop');
StreamSubscription _streamSubscription;
String _platformMessage;
void _enableEventReceiver() {
_streamSubscription = _channel.receiveBroadcastStream().listen(
(dynamic event) {
print('Received event: $event');
setState(() {
_platformMessage = event;
});
},
onError: (dynamic error) {
print('Received error: ${error.message}');
},
cancelOnError: true);
}
void _disableEventReceiver() {
if (_streamSubscription != null) {
_streamSubscription.cancel();
_streamSubscription = null;
}
}
@override
initState() {
super.initState();
_enableEventReceiver();
}
@override
void dispose() {
super.dispose();
_disableEventReceiver();
}
4.2 プラットフォーム側の実装
チャンネル生成とイベントリスナー登録
EventChannelインスタンスを生成し、EventChannel#setStreamHandlerでイベントリスナーを登録します。
初期化処理
初期化処理が完了し、イベント送信の準備が出来たタイミングでコールされます。このタイミングでイベント送信に関する初期化処理などを行うと良いでしょう。
引数のEventSinkはローカルに保存しておき、必要なタイミングでイベント送信します。
イベント送信
eventSink?.success("Android")
のように引数を添えてイベント通知します。
イベント送信終了
全てのイベントを送信完了し、これ以上イベント送信が不要な場合にはendOfStreamを利用します。これをコールすることで、プラットフォーム側からのトリガーで通信を終了することが出来ます。コール後、onCancelのイベントリスナーがコールされます。
エラー発生時
想定外のエラーが発生するなどのこれ以上通信が不可になった場合など、EventSink#errorをコールすればDart側のStreamSubscription#onErrorがコールされます。
void error(String errorCode, String errorMessage, Object errorDetails)
サンプルコード
class MainActivity: FlutterActivity() {
private lateinit var channel: EventChannel
var eventSink: EventSink? = null
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
channel = EventChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.eventchannel/interop")
channel.setStreamHandler(
object : StreamHandler {
override fun onListen(arguments: Any?, events: EventSink) {
eventSink = events
Log.d("Android", "EventChannel onListen called")
Handler().postDelayed({
eventSink?.success("Android")
//eventSink?.endOfStream()
//eventSink?.error("error code", "error message","error details")
}, 500)
}
override fun onCancel(arguments: Any?) {
Log.w("Android", "EventChannel onCancel called")
}
})
}
}