1. pigeon
Pigeon 是一个代码生成器工具,用于使 Flutter 和宿主平台之间的通信类型安全、更轻松、更快捷
2. 定义接口
创建pigeons/message.dart(lib同级目录创建)
import 'package:pigeon/pigeon.dart'; // 输出配置 // 控制台执行:dart run pigeon --input pigeons/message.dart @ConfigurePigeon(PigeonOptions( dartOut: 'lib/messages.g.dart', dartOptions: DartOptions(), kotlinOut: 'android/app/src/main/kotlin/com/app/studyplugin2/Messages.g.kt', kotlinOptions: KotlinOptions(), swiftOut: 'ios/Runner/Messages.g.swift', swiftOptions: SwiftOptions(), objcHeaderOut: 'macos/Runner/messages.g.h', objcSourceOut: 'macos/Runner/messages.g.m', // Set this to a unique prefix for your plugin or application, per Objective-C naming conventions. objcOptions: ObjcOptions(prefix: 'PGN'), // copyrightHeader: 'pigeons/copyright.txt', // dartPackageName: 'pigeon_example_package', )) // #enddocregion config // This file and ./messages_test.dart must be identical below this line. // #docregion host-definitions enum Code { one, two } class MessageData { MessageData({required this.code, required this.data}); String? name; String? description; Code code; Map<String?, String?> data; } @HostApi() abstract class ExampleHostApi { String getHostLanguage(); // These annotations create more idiomatic naming of methods in Objc and Swift. @ObjCSelector('addNumber:toNumber:') @SwiftFunction('add(_:to:)') int add(int a, int b); @async bool sendMessage(MessageData message); } // #enddocregion host-definitions // #docregion flutter-definitions @FlutterApi() abstract class MessageFlutterApi { String flutterMethod(String? aString); } // #enddocregion flutter-definitions
3.定义sh文件 pigeon.sh(lib同级目录创建)
java_package 目录可以随便填写, 目录如果创建失败 就手动创建目录
flutter pub run pigeon \ --input pigeons/message.dart
4. 运行sh文件 pigeon.sh 会生成一下文件
android/app/src/main/kotlin/com/app/studyplugin2/Messages.g.kt
ios/Runner/Messages.g.swift
lib/messages.g.dart
5. andorid使用
package com.app.studyplugin2 import ExampleHostApi import FlutterError import MessageData import MessageFlutterApi import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.plugins.FlutterPlugin // #docregion kotlin-class private class PigeonApiImplementation : ExampleHostApi { override fun getHostLanguage(): String { return "Kotlin" } override fun add(a: Long, b: Long): Long { if (a < 0L || b < 0L) { throw FlutterError("code", "message", "details") } return a + b } override fun sendMessage(message: MessageData, callback: (Result<Boolean>) -> Unit) { if (message.code == Code.ONE) { callback(Result.failure(FlutterError("code", "message", "details"))) return } callback(Result.success(true)) } } // #enddocregion kotlin-class // #docregion kotlin-class-flutter private class PigeonFlutterApi { var flutterApi: MessageFlutterApi? = null constructor(binding: FlutterPlugin.FlutterPluginBinding) { flutterApi = MessageFlutterApi(binding.getBinaryMessenger()) } fun callFlutterMethod(aString: String, callback: (Result<String>) -> Unit) { flutterApi!!.flutterMethod(aString) { echo -> callback(echo) } } } // #enddocregion kotlin-class-flutter class MainActivity : FlutterActivity() { override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) val api = PigeonApiImplementation() ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api) } }
6. Ios 使用
// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import Flutter import UIKit // #docregion swift-class // This extension of Error is required to do use FlutterError in any Swift code. extension FlutterError: Error {} enum MyErrors: Error { case missingValue } private class PigeonApiImplementation: ExampleHostApi { func getHostLanguage() throws -> String { return "Swift" } func add(_ a: Int64, to b: Int64) throws -> Int64 { if a < 0 || b < 0 { throw FlutterError(code: "code", message: "message", details: "details") } return a + b } func sendMessage(message: MessageData, completion: @escaping (Result<Bool, Error>) -> Void) { if message.code == Code.one { completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) return } completion(.success(true)) } } // #enddocregion swift-class // #docregion swift-class-flutter private class PigeonFlutterApi { var flutterAPI: MessageFlutterApi init(binaryMessenger: FlutterBinaryMessenger) { flutterAPI = MessageFlutterApi(binaryMessenger: binaryMessenger) } func callFlutterMethod( aString aStringArg: String?, completion: @escaping (Result<String, Error>) -> Void ) { func optionalStringToResult(optionalString: String?) -> Result<String, Error> { guard let nonOptionalString = optionalString else { return .failure(MyErrors.missingValue) } return .success(nonOptionalString) } flutterAPI.flutterMethod(aString: aStringArg) { result in switch result { case .success(let value): completion(.success(value)) case .failure(let error): completion(.failure(error)) } } } } // #enddocregion swift-class-flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) let controller = window?.rootViewController as! FlutterViewController let api = PigeonApiImplementation() ExampleHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
7. flutter 使用
class _ExampleFlutterApi implements MessageFlutterApi { @override String flutterMethod(String? aString) { return aString ?? ''; } } class _MyHomePageState extends State<MyHomePage> { final ExampleHostApi _hostApi = ExampleHostApi(); String? _hostCallResult; // #docregion main-dart final ExampleHostApi _api = ExampleHostApi(); /// Calls host method `add` with provided arguments. Future<int> add(int a, int b) async { try { return await _api.add(a, b); } catch (e) { // handle error. return 0; } } /// Sends message through host api using `MessageData` class /// and api `sendMessage` method. Future<bool> sendMessage(String messageText) { final MessageData message = MessageData( code: Code.two, data: <String?, String?>{'header': 'this is a header'}, description: 'uri text', ); try { return _api.sendMessage(message); } catch (e) { // handle error. return Future<bool>(() => true); } } // #enddocregion main-dart @override void initState() { super.initState(); MessageFlutterApi.setUp(_ExampleFlutterApi()); _hostApi.getHostLanguage().then((String response) { setState(() { _hostCallResult = 'Hello from $response!'; }); }).onError<PlatformException>((PlatformException error, StackTrace _) { setState(() { _hostCallResult = 'Failed to get host language: ${error.message}'; }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), actions: [ TextButton(onPressed: (){ add(3, 2).then((value) => print("t=$value")); }, child: Text("add")), TextButton(onPressed: (){ sendMessage("sendMessage").then((value) => print("sendMessage=$value")); }, child: Text("sendMessage")), ], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( _hostCallResult ?? 'Waiting for host language...', ), if (_hostCallResult == null) const CircularProgressIndicator(), ], ), ), ); } }