深色模式
使用pigeon开发Flutter插件
说明
使用pigeon,从0开发一个Flutter插件,支持Android、iOS平台,插件提供2个功能:
- 获取系统版本号。
- 在原生层,弹出一个Dialog。
新建项目
创建插件项目
通过flutter create
命令,创建一个插件项目:
sh
flutter create --template plugin --platforms android,ios --project-name cute --org com.yuluyao cute-plugin
项目结构:
hello_plugin/
├── android/
│ └── src/
│ └── main/
│ └── kotlin/com/yuluyao/cute/
│ │ └── CutePlugin.kt
│ └── AndroidManifest.xml
├── example/
├── ios/
│ └── Classes/
│ └── CutePlugin.swift
├── lib/
│ │── cute_method_channel.dart
│ │── cute_platform_interface.dart
│ └── cute.dart
├── pubspec.yaml
添加pigeon
添加pigeon
依赖:
sh
flutter pub add dev:pigeon
添加以后,pubspec.yaml
中会多出1行:
yaml
dev_dependencies:
pigeon: ^22.5.0
使用pigeon
定义插件API
在项目根目录中,新建pigeon_host_api.dart
文件,在其中定义2个API:
getSystemVersionInfo
popDialog
代码如下:
dart
import 'package:pigeon/pigeon.dart';
class SystemVersionInfo {
SystemVersionInfo({required this.platform, required this.version});
final String platform;
final String version;
}
@ConfigurePigeon(
PigeonOptions(
// API
dartOut: 'lib/pigeon/cute_api.g.dart',
dartOptions: DartOptions(),
dartPackageName: 'cute',
// Android
kotlinOut: 'android/src/main/kotlin/com/yuluyao/cute/Cute.g.kt',
kotlinOptions: KotlinOptions(
package: "com.yuluyao.cute",
),
// iOS or macOS
swiftOut: 'ios/Classes/Cute.g.swift',
swiftOptions: SwiftOptions(),
),
)
@HostApi()
abstract class CuteHostApi {
SystemVersionInfo getSystemVersionInfo();
@async
@SwiftFunction('popDialog(_:_:)')
bool popDialog(String title, String message);
}
生成代码
命令:
sh
dart run pigeon --input pigeon_host_api.dart
会生成3端的代码:
hello_plugin/
├── android/
│ └── src/
│ └── main/
│ └── kotlin/com/yuluyao/cute/
│ │ ├── Cute.g.kt
│ │ └── CutePlugin.kt
│ └── AndroidManifest.xml
├── example/
├── ios/
│ └── Classes/
│ ├── Cute.g.swift
│ └── CutePlugin.swift
├── lib/
│ ├── pigeon/
│ │ └── cute_api.g.dart
│ │── cute_method_channel.dart
│ │── cute_platform_interface.dart
│ └── cute.dart
├── pubspec.yaml
编译example项目
编辑原生项目代码之前,要先编译一次Flutter,确保Android Studio或XCode正确打开项目。
进入example目录:
sh
flutter build apk --config-only
sh
flutter build ios --no-codesign --config-only
Flutter端
生成的cute_api.g.dart
文件中,有一个CuteHostApi
,它已经处理了通过Method Channel调用原生的逻辑。
CuteHostApi
即是本插件提供的API,可以供其它项目调用。
在example项目中调用:
dart
final api = CuteHostApi();
final info = await api.getSystemVersionInfo();
final ok = api.popDialog("title", "message");
所以,新建项目时,生成的Dart代码就不需要了,可以删除:
├── lib/
│ ├── pigeon/
│ │ └── cute_api.g.dart
│ │── cute_method_channel.dart
│ │── cute_platform_interface.dart
│ └── cute.dart
Android端
打开Android项目
用Android Studio打开example/android/build.gradle
,打开以后要进行Gradle Sync。
INFO
Gradle Sync可能很慢,或连接失败,可以给Android Studio设置代理。
Sync完了以后,还要经过Build,也很慢。😅😅
项目结构与普通Android项目有点不一样:
(注意:这里是Android视图,不是Project视图!)
Android/
├── app/
│ │── manifests/
│ │── kotlin/
│ │ └── com.yuluyao.cute_example/
│ │ └── MainActivity.kt // 此文件没什么用
│ │── kotlin+java/
│ └── res/
├── cute/
│ │── manifests/
│ └── kotlin+java/
│ └── com.yuluyao.cute/
│ │── Cute.g.kt
│ └── CutePlugin.kt
├── integration_test/
├── gradle/
疑问:
既然上面的MainActivity
没什么用,为什么还要用Android视图打开它呢?为什么不仅仅打开项目的android/build.gradle
呢?毕竟CutePlugin.kt
就在这个目录中。
答案是:
仅打开android/build.gradle
,会到处报红色错误,无法正常编辑Kotlin代码。
代码分析
先看一下代码:
kotlin
package com.yuluyao.cute
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** CutePlugin */
class CutePlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cute")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
kotlin
// Autogenerated from Pigeon (v22.5.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
package com.yuluyao.cute
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
private fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
exception.message,
exception.details
)
} else {
listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code.
* @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec.
*/
class FlutterError (
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
/** Generated class from Pigeon that represents data sent in messages. */
data class SystemVersionInfo (
val platform: String,
val version: String
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): SystemVersionInfo {
val platform = pigeonVar_list[0] as String
val version = pigeonVar_list[1] as String
return SystemVersionInfo(platform, version)
}
}
fun toList(): List<Any?> {
return listOf(
platform,
version,
)
}
}
private open class CutePigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SystemVersionInfo.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is SystemVersionInfo -> {
stream.write(129)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface CuteHostApi {
fun getSystemVersionInfo(): SystemVersionInfo
fun popDialog(title: String, message: String, callback: (Result<Boolean>) -> Unit)
companion object {
/** The codec used by CuteHostApi. */
val codec: MessageCodec<Any?> by lazy {
CutePigeonCodec()
}
/** Sets up an instance of `CuteHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: CuteHostApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.cute.CuteHostApi.getSystemVersionInfo$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
listOf(api.getSystemVersionInfo())
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.cute.CuteHostApi.popDialog$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val titleArg = args[0] as String
val messageArg = args[1] as String
api.popDialog(titleArg, messageArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
CutePlugin.kt
:它是与Flutter端通信的入口,我们要实现Cute.g.kt
中的CuteHostApi
接口,并在CutePlugin.kt
中调用它,以连接Android端与Flutter端。Cute.g.kt
:此文件由pigeon生成,不必修改,它帮我们处理了Method Channel通信、消息编解码、错误处理。
去掉默认代码
由于我们通过pigeon处理Method Channel通信,这里先去掉CutePlugin.kt
中的Method Channel相关代码:
kotlin
package com.yuluyao.cute
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** CutePlugin */
class CutePlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cute")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
kotlin
package com.yuluyao.cute
import io.flutter.embedding.engine.plugins.FlutterPlugin
/** CutePlugin */
class CutePlugin: FlutterPlugin {
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
}
}
实现功能
代码:
kotlin
package com.yuluyao.cute
import android.app.Activity
import android.app.AlertDialog
import android.os.Build
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
/** CutePlugin */
class CutePlugin : FlutterPlugin, ActivityAware {
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
CuteHostApi.setUp(flutterPluginBinding.binaryMessenger, api)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
}
private val api = object : CuteHostApi {
override fun getSystemVersionInfo(): SystemVersionInfo {
return SystemVersionInfo(platform = "Android", version = Build.VERSION.SDK)
}
override fun popDialog(title: String, message: String, callback: (Result<Boolean>) -> Unit) {
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton("ok") { dialog, which ->
callback.invoke(Result.success(true))
}
.setNegativeButton("cancel") { dialog, which ->
callback.invoke(Result.success(false))
}
.show()
}
}
private var activity: Activity? = null
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
activity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivity() {
activity = null
}
}
分析:
获取宿主
Activity
:让
CutePlugin
实现ActivityAware
接口,以获取当前的宿主Activity
。kotlinclass CutePlugin : FlutterPlugin, ActivityAware { // ... private var activity: Activity? = null override fun onAttachedToActivity(binding: ActivityPluginBinding) { activity = binding.activity } override fun onDetachedFromActivityForConfigChanges() { activity = null } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { activity = binding.activity } override fun onDetachedFromActivity() { activity = null } }
实现
CuteHostApi
接口kotlinclass CutePlugin : FlutterPlugin, ActivityAware { private val api = object : CuteHostApi { override fun getSystemVersionInfo(): SystemVersionInfo { return SystemVersionInfo(platform = "Android", version = Build.VERSION.SDK) } override fun popDialog(title: String, message: String, callback: (Result<Boolean>) -> Unit) { AlertDialog.Builder(activity) .setTitle(title) .setMessage(message) .setPositiveButton("ok") { dialog, which -> callback.invoke(Result.success(true)) } .setNegativeButton("cancel") { dialog, which -> callback.invoke(Result.success(false)) } .show() } } // ... }
连接Method Channel
kotlin
class CutePlugin : FlutterPlugin, ActivityAware {
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
CuteHostApi.setUp(flutterPluginBinding.binaryMessenger, api)
}
// ...
}
iOS端
打开iOS项目
用XCode打开example/ios/Runner.xcworkspace
。
项目结构:
Project navigator/
├── Runner/
│ │── Flutter/
│ │── Runner/
│ │── Products/
│ │── RunnerTests/
│ │── Pods/
│ └── Frameworks/
├── Pods/
│ │── Podfile
│ └── Development Pods/
│ └── cute/
│ └── ../
│ └── ../
│ └── example/
│ └── ios/
│ └── symlinks/
│ └── plugins/
│ └── cute/
│ └── ios/
│ └── Classes/
│ │── Cute.g.swift
│ └── CutePlugin.swift
├── Frameworks/
├── Products/
├── Targets Support Files/
代码分析
先看一下代码:
swift
import Flutter
import UIKit
public class CutePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "cute", binaryMessenger: registrar.messenger())
let instance = CutePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
default:
result(FlutterMethodNotImplemented)
}
}
}
swift
// Autogenerated from Pigeon (v22.5.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
#if os(iOS)
import Flutter
#elseif os(macOS)
import FlutterMacOS
#else
#error("Unsupported platform.")
#endif
/// Error class for passing custom error details to Dart side.
final class PigeonError: Error {
let code: String
let message: String?
let details: Any?
init(code: String, message: String?, details: Any?) {
self.code = code
self.message = message
self.details = details
}
var localizedDescription: String {
return
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
}
}
private func wrapResult(_ result: Any?) -> [Any?] {
return [result]
}
private func wrapError(_ error: Any) -> [Any?] {
if let pigeonError = error as? PigeonError {
return [
pigeonError.code,
pigeonError.message,
pigeonError.details,
]
}
if let flutterError = error as? FlutterError {
return [
flutterError.code,
flutterError.message,
flutterError.details,
]
}
return [
"\(error)",
"\(type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)",
]
}
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func nilOrValue<T>(_ value: Any?) -> T? {
if value is NSNull { return nil }
return value as! T?
}
/// Generated class from Pigeon that represents data sent in messages.
struct SystemVersionInfo {
var platform: String
var version: String
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> SystemVersionInfo? {
let platform = pigeonVar_list[0] as! String
let version = pigeonVar_list[1] as! String
return SystemVersionInfo(
platform: platform,
version: version
)
}
func toList() -> [Any?] {
return [
platform,
version,
]
}
}
private class CutePigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
case 129:
return SystemVersionInfo.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
}
}
}
private class CutePigeonCodecWriter: FlutterStandardWriter {
override func writeValue(_ value: Any) {
if let value = value as? SystemVersionInfo {
super.writeByte(129)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
}
}
private class CutePigeonCodecReaderWriter: FlutterStandardReaderWriter {
override func reader(with data: Data) -> FlutterStandardReader {
return CutePigeonCodecReader(data: data)
}
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
return CutePigeonCodecWriter(data: data)
}
}
class CutePigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
static let shared = CutePigeonCodec(readerWriter: CutePigeonCodecReaderWriter())
}
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol CuteHostApi {
func getSystemVersionInfo() throws -> SystemVersionInfo
func popDialog(_ title: String, _ message: String, completion: @escaping (Result<Bool, Error>) -> Void)
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
class CuteHostApiSetup {
static var codec: FlutterStandardMessageCodec { CutePigeonCodec.shared }
/// Sets up an instance of `CuteHostApi` to handle messages through the `binaryMessenger`.
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: CuteHostApi?, messageChannelSuffix: String = "") {
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
let getSystemVersionInfoChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.cute.CuteHostApi.getSystemVersionInfo\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
getSystemVersionInfoChannel.setMessageHandler { _, reply in
do {
let result = try api.getSystemVersionInfo()
reply(wrapResult(result))
} catch {
reply(wrapError(error))
}
}
} else {
getSystemVersionInfoChannel.setMessageHandler(nil)
}
let popDialogChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.cute.CuteHostApi.popDialog\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
popDialogChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let titleArg = args[0] as! String
let messageArg = args[1] as! String
api.popDialog(titleArg, messageArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
popDialogChannel.setMessageHandler(nil)
}
}
}
CutePlugin.swift
:它是与Flutter端通信的入口,我们要实现Cute.g.swift
中的CuteHostApi
接口,并在CutePlugin.swift
中调用它,以连接iOS端与Flutter端。Cute.g.swift
:此文件由pigeon生成,不必修改,它帮我们处理了Method Channel通信、消息编解码、错误处理。
去掉默认代码
由于我们通过pigeon处理Method Channel通信,这里先去掉CutePlugin.swift
中的Method Channel相关代码:
kotlin
import Flutter
import UIKit
public class CutePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "cute", binaryMessenger: registrar.messenger())
let instance = CutePlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
default:
result(FlutterMethodNotImplemented)
}
}
}
kotlin
import Flutter
import UIKit
public class CutePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
}
}
实现功能
代码:
swift
import Flutter
import UIKit
public class CutePlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let api = CuteHostApiImpl()
CuteHostApiSetup.setUp(binaryMessenger: registrar.messenger(), api: api)
}
private class CuteHostApiImpl : CuteHostApi {
func getSystemVersionInfo() throws -> SystemVersionInfo {
return SystemVersionInfo(platform: "iOS", version: UIDevice.current.systemVersion)
}
func popDialog(_ title: String, _ message: String, completion: @escaping (Result<Bool, Error>) -> Void) {
if let viewController = UIApplication.shared.keyWindow?.rootViewController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { (action) in
completion(.success(true))
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
completion(.success(false))
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
viewController.present(alertController, animated: true, completion: nil)
}
}
}
}
分析:
- 获取UIViewController
swift
if let viewController = UIApplication.shared.keyWindow?.rootViewController {
// ...
}
- 实现
CuteHostApi
接口
swift
public class CutePlugin: NSObject, FlutterPlugin {
private class CuteHostApiImpl : CuteHostApi {
func getSystemVersionInfo() throws -> SystemVersionInfo {
return SystemVersionInfo(platform: "iOS", version: UIDevice.current.systemVersion)
}
func popDialog(_ title: String, _ message: String, completion: @escaping (Result<Bool, Error>) -> Void) {
if let viewController = UIApplication.shared.keyWindow?.rootViewController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { (action) in
completion(.success(true))
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
completion(.success(false))
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
viewController.present(alertController, animated: true, completion: nil)
}
}
}
}
- 连接Method Channel
swift
public static func register(with registrar: FlutterPluginRegistrar) {
let api = CuteHostApiImpl()
CuteHostApiSetup.setUp(binaryMessenger: registrar.messenger(), api: api)
}
使用此插件
可以在example/
中使用此插件的API。
example/lib/main.dart
:
dart
import 'package:cute/pigeon/cute_api.g.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late final CuteHostApi hostApi;
SystemVersionInfo? _systemVersionInfo;
bool clickOK = false;
@override
void initState() {
super.initState();
hostApi = CuteHostApi();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Cute plugin example app'),
),
body: Center(
child: Column(
children: [
const SizedBox(height: 40),
Text("系统版本号:${_systemVersionInfo?.platform}, ${_systemVersionInfo?.version}"),
OutlinedButton(
onPressed: () async {
final s = await hostApi.getSystemVersionInfo();
setState(() {
_systemVersionInfo = s;
});
},
child: const Text('获取系统版本号'),
),
const SizedBox(height: 40),
Text("用户已选OK? $clickOK"),
OutlinedButton(
onPressed: () async {
final ok = await hostApi.popDialog("title", "来自Flutter的message");
setState(() {
clickOK = ok;
});
},
child: const Text('弹出原生Dialog'),
),
],
),
),
),
);
}
}