Upgrade to Pro — share decks privately, control downloads, hide ads and more …

pigeonでネイティブ連携

 pigeonでネイティブ連携

FlutterKaigi mini #2 @Ishikawa でLTしたものです。

noboru ishikura

September 14, 2024
Tweet

More Decks by noboru ishikura

Other Decks in Programming

Transcript

  1. イメージ Flutter data class interface iOS data class interface implementation

    Android data class interface implementation 全部自分で実装
  2. Data class Flutter (Dart) Android (Kotlin) iOS (Swift) @JsonSerializable() class

    BatteryResult { BatteryResult({ required this.level, required this.isCharging, }); final int level; final bool isCharging; // fromJson, toJson の定義 } @Serializable data class BatteryResult ( val level: Int val isCharging: Boolean ) struct BatteryResult: Codable { var level: Int var isCharging: Bool }
  3. Interface / Implementation (1/3) invokeMethodでネイティブコードを実行した結果を、JSON文字列で受 け取ってクラスに変換する。 Flutter (Dart) class BatteryApi

    { static const platform = MethodChannel('samples.flutter.dev/battery'); Future<BatteryResult> getBatteryLevel() async { // ex. {"level": 89, "isCharging": true} final resultJson = await platform.invokeMethod<String>('getBatteryLevel'); return BatteryResult.fromJson(jsonDecode(resultJson)); } }
  4. Interface / Implementation (2/3) 同じchannel文字列で、同じmethod名であれば、処理して返す。 Android (Kotlin) private val CHANNEL

    = "samples.flutter.dev/battery" MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getBatteryLevel") { val batteryLevel = getBatteryLevel() val isCharging = getIsCharging() val json = Json.encodeToString(BatteryResult(batteryLevel, isCharging)) result.success(json) } }
  5. Interface / Implementation (3/3) iOS (Swift) let batteryChannel = FlutterMethodChannel(name:

    "samples.flutter.dev/battery", binaryMessenger: controller.binaryMessenger) batteryChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in if call.method == "getBatteryLevel" { let batteryLevel = getBatteryLevel() let isCharging = getIsCharging() let batteryResult = BatteryResult(level: batteryLevel, isCharging: isCharging) let encoder = JSONEncoder() result(encoder.encode(batteryResult)) } })
  6. そこでpigeon Pigeon is a code generator tool to make communication

    between Flutter and the host platform type-safe, easier, and faster. Google翻訳 Pigeon は、Flutter とホスト プラットフォーム間の通信を タイプセーフ 、 簡単 、 高速 にするコード生成ツールです。 https://pub.dev/packages/pigeon
  7. pigeonを使う手順 1. インターフェースをDartで書く 2. dart run pigeon を実行(コードが自動生成される) 3. 出力されたinterfaceを、Kotlin/Swiftで実装

    4. Flutter側から、出力されたDart interfaceを実行する つまり、定義部分を書くのはDartだけで、KotlinとSwiftは実装するだけ。
  8. イメージ Flutter data class interface iOS data class interface implementation

    Android data class interface implementation これだけ不要になる。
  9. Dart interface pigeonへの入力となるコード class BatteryResult { BatteryResult({ required this.level, required

    this.isCharging, }); final int level; final bool isCharging; } @HostApi() abstract class BatteryApi { BatteryResult getBatteryLevel(); }
  10. dart run pigeon 実行 ファイルが出力される dart run pigeon \ --input

    pigeons/messages.dart \ --dart_out lib/gen/messages.dart \ --swift_out ios/Classes/messages.g.swift \ --kotlin_out android/app/src/main/kotlin/com/example/pigeon_plugin/Messages.g.kt \ --kotlin_package "com.example.pigeon_plugin"
  11. 実装するKotlinコード class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine)

    { super.configureFlutterEngine(flutterEngine) BatteryApi.setUp( flutterEngine.dartExecutor.binaryMessenger, BatteryApiImplementation(context) ) } } class BatteryApiImplementation(private val context: Context) : BatteryApi { override fun getBatteryLevel(): BatteryResult { val level: Int = ... val isCharging: Boolean = ... return BatteryResult(level, isCharging) } }
  12. 実装するSwiftコード @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application(

    _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any ) -> Bool { GeneratedPluginRegistrant.register(with: self) let controller: FlutterViewController = window?.rootViewController as! FlutterView BatteryApiSetup.setUp(binaryMessenger: controller as! FlutterBinaryMessenger, api return super.application(application, didFinishLaunchingWithOptions: launchOptions } } class BatteryApiImplementation: BatteryApi { func getBatteryLevel() throws -> BatteryResult { let level = ... let isCharging = ... return BatteryResult(level: Int64(level), isCharging: isCharging)