Flutterアプリで、ネイティブ側(バックエンド)からイベントを発行してUI側(フロントエンド)を更新する方法について説明します。以前、Flutterプラグインの実装方法の一部としてEventChannel
を実装する方法は書いたことがあります、今回はプラグインを使わないで同じ機能実装する方法です。
フロントエンドとバックエンドで通信を行う方法として、MethodChannel
とEventChannel
大きく2つの方法があります。今回は、EventChannel
を使った方法を説明します。MethodChannel
を使う方法は、公式ドキュメントに詳しく記述されています。何故か、EventChannel
を使う方法は公式ページでは見当たらない。。
図1 Platform Channelsの全体像(Flutter公式ページより)
バックエンドの実装
iOSの場合は、AppDelegate上でMethodChannel
と同じように、MethodChannel
を作ってbinaryMessengerと繋げます。さらに、そのMethodChannel
にFlutterStreamHandler
を実装したクラスを紐付けます。
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let measurementEvent = FlutterEventChannel(name: "com.sample.app/event", binaryMessenger: controller.binaryMessenger) measurementEvent.setStreamHandler(HomeStreamHandler())
FlutterStremHandler
プロトコルを実装したクラスは、-onListen(withArguments:)
をonCancel(withArguments:)
を実装する必要があります。フロントエンドから、紐付けたEventChannel
をListenするリクエストが届くと、-onListen(withArguments:)
メソッドが呼ばれる。引数として、コールバック用のFlutterEventSink
が渡されるので、それをクラス内で保存し、新しいイベントが発生した際に、先ほどのFlutterEventSink
を用いてフロントエンドに値を渡す。
public classHomeStreamHandler:NSObject, FlutterStreamHandler {
var eventSink:FlutterEventSink?
public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
self.eventSink = events
// TODO: イベントが発生したら、eventSink(Any)メソッドに任意の値を与えることで、UI側に値を引き渡すことができる。
// NOTE: サポートしてデータ形式は、表1を参照してください。
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
eventSink = nil
return nil
}
}
基本的なデータ形式は、バックエンドからフロントエンドに流せるようになっている。今回はDictionary型を使った。
表1 Platform Channelsでサポートされているデータ形式(Flutter公式ページより)
フロントエンドの実装
通常通り、EventをListenするだけ。Swiftで値を引き渡した場合、Map変換されると思いきや、LinkedHashMapになったので注意。もしかしたらMapにする方法は別にあるかも。
import 'package:flutter/services.dart';
import 'dart:collection';
class _MyHomePageState extends State<MyHomePage> {
static const eventChannel = const EventChannel('com.sample.app/event');
StreamSubscription _onChangedSubscription;
@override
void initState() {
// TODO: implement initState
super.initState();
this._onChangedSubscription = eventChannel.receiveBroadcastStream().listen((event) {
// NOTE: eventはdynamicなので、送信元の型に応じてキャストが必要
// NOTE: Dictionary型の場合、Mapになるかと思いきや、LinkedHashMapになっていた。
var data = event as LinkedHashMap;
var value = data['key'];
setState((){
// TODO: do something
});
});
}
以上です。これで、バックエンドかフロントエンドに任意の値を、バックエンド側から送信できるようになったと思います。
参考資料
- https://flutter.dev/docs/development/platform-integration/platform-channels