概要
Flutterアプリを作成している際、ネイティブコードを使いたい時があります。
ここではAndroidでの実装方法を2パターンご紹介します。
外部パッケージの導入は保守や脆弱性対応の対象も増えます。
実装量もそれほど多くないため、選択肢として把握しておくと便利です。
パターン1 Flutterからネイティブコードを呼ぶ
Flutterからネイティブコードを呼ぶ場合、MethodChannelを使用します。
Android側で入り口(MethodChannel)を名前付きで用意しておき、名前を元にFlutter側で呼ぶイメージです。
Android側
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class MainActivity(): FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "jp.co.jwork.sample.app/sample")
.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
if (methodCall.method == "input") {
val args: List<Any> = methodCall.arguments as List<Any>
// ... Androidでやりたい処理 ...
result.success("ok") // "ok"部がレスポンスになります。エラーの場合、result.errorも使えます。
} else {
result.notImplemented()
}
}
}
}
Flutter側
import 'dart:async';
import "package:flutter/services.dart";
class Caller {
static const MethodChannel _methodChannel = const MethodChannel('jp.co.jwork.sample.app/sample');
Future run(String param) async {
try {
return await _methodChannel.invokeMethod("input", [ param ]);
} on PlatformException {
// エラーハンドリング
}
}
}
パターン2 AndroidからイベントをFlutter側に流す
AndroidからFlutterへ渡す場合、EventChannelを使用できます。
Flutter側で入り口(EventChannel)を名前付きで用意しておき、名前を元にAndroid側で呼ぶイメージです。
なお、1回きりで良い場合は、同じ流れでパターン1のMethodChannelを使うことでも実現可能です。
Android側
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
class MainActivity(): FlutterActivity() {
private var mInputEventSink: EventChannel.EventSink? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
EventChannel(flutterEngine.dartExecutor.binaryMessenger, "jp.co.jwork.sample.app/sampleEvent")
.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
mInputEventSink = events
}
override fun onCancel(arguments: Any?) {
mInputEventSink = null
}
})
}
// 何らかのAndroid側処理の値
private fun onInput(value: String) {
mInputEventSink?.success(value)
}
}
Flutter側
import 'dart:async';
import "package:flutter/services.dart";
class Receiver {
static const EventChannel _eventChannel = const EventChannel("jp.co.jwork.sample.app/sampleEvent");
StreamSubscription? _listenEventStreamSubs;
// 引数で関数を受けていますが、単純にStreamSubscriptionを返す作りでも良いと思います。
Future<void> listen(void Function(String value) onListen) async {
await _listenEventStreamSubs?.cancel();
_listenEventStreamSubs = null;
_listenEventStreamSubs = _eventChannel.receiveBroadcastStream().listen((event) {
if (event is String) {
onListen(event);
}
}, onError: (error) {
// エラーハンドリング
}, cancelOnError: true);
}
// 不要になったら閉じます。
Future<void> cancel() async {
await _listenEventStreamSubs?.cancel();
_listenEventStreamSubs = null;
}
}
まとめ
MethodChannel、EventChannelで簡単にAndroidコードとやりとりが出来ました。
汎用的なものならpluginとして切り出し、使いまわせるようにするのも良いと思います。