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
