Flutter: Adding custom platform-specific code (EventChannel)

April 30, 2020 Yuuki Nishiyama 0 Comments

Flutterアプリで、ネイティブ側(バックエンド)からイベントを発行してUI側(フロントエンド)を更新する方法について説明します。以前、Flutterプラグインの実装方法の一部としてEventChannelを実装する方法は書いたことがあります、今回はプラグインを使わないで同じ機能実装する方法です。

 

フロントエンドとバックエンドで通信を行う方法として、MethodChannelEventChannel大きく2つの方法があります。今回は、EventChannelを使った方法を説明します。MethodChannelを使う方法は、公式ドキュメントに詳しく記述されています。何故か、EventChannelを使う方法は公式ページでは見当たらない。。

図1 Platform Channelsの全体像(Flutter公式ページより)

 

バックエンドの実装

iOSの場合は、AppDelegate上でMethodChannelと同じように、MethodChannelを作ってbinaryMessengerと繋げます。さらに、そのMethodChannelFlutterStreamHandlerを実装したクラスを紐付けます。

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

Leave a Reply:

Your email address will not be published. Required fields are marked *