先决条件
- 对学习新 🔥 🚀 事物充满热情
- 一些使用 Flutter 💙 构建应用的经验
- 基本的编程知识 👾
什么是 Pigeon?
Pigeon 是一个代码生成器工具,使 Flutter 和主机平台之间的通信类型安全、更轻松、更快捷。— pub
Pigeon 是一个代码生成工具,通过在常见的 Flutter 的外函数接口(FFI)上生成类型安全的惯用代码,使它们更容易、更快速地进行类型安全(具有更高的错误处理保真度)之间的通信,它们也被称为方法通道、模式。
Pigeon 无需编写任何接口或方法通道代码来在主机平台之间进行通信,因为它生成了我们需要的所有代码。本文中涉及 Pigeon 从设置到使用,但不会涵盖它的所有功能,要了解更多信息,请访问官方文档。
在撰写本文时,Pigeon 版本是 17.1.2。
Pigeon 配置
在 lib 之外创建一个文件夹,用于放置项目所需的任意数量 Pigeon,我们称之为 Pigeons。在该文件夹中创建一个 native_moible_pigeon.dart
文件,在其中写入 Pigeon 的构建配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(PigeonOptions(
dartOut: 'lib/native_api/native_mobile_ui.g.dart',
kotlinOut: 'android/app/src/main/kotlin/com/example/mobile_declarative_ui/NativeMobileUi.g.kt',
javaOut: 'android/app/src/main/java/io/flutter/plugins/NativeMobileUi.java',
swiftOut: 'ios/Runner/NativeMobileUi.g.swift',
dartPackageName: 'native_mobile_ui',
))
@HostApi()
abstract class NativeMobileHostApi {
@async
String getNativeUiResult();
}
|
下面我们将介绍 ConfigurePigeon 注解的每个选项:
dartOut
:生成文件的输出路径,此处我们将其指定为 lib/native_api/native_mobile_ui.g.dart
告诉 Pigeon 在 lib 文件夹中创建文件,以便我们的项目可以访问它,文件扩展名前的 .g 是 flutter/dart 约定,用于区分自动生成的文件。
kotlinOut
:如果项目使用 kotlin,则指定输出 kotlin 生成文件的路径。
javaOut
:指定我们要输出为项目生成的 java 代码的路径。
swiftOut
:指定生成的 swift 文件的创建路径。
dartPackagename
: Pigeon 所生成软件包的包名
HostApi
注解用于告诉 Pigeon 我们需要生成主机平台代码,因为 Pigeon 可以从两端生成代码(flutter <-> host)。async
注解告诉 Pigeon 该方法是一个耗时的调用,因此它将其作为系统两端的 Future/Async
任务处理。
使用以下命令生成代码:
1
|
dart run pigeon --input pigeons/native_mobile_pigeon.dart
|
Flutter
生成的文件现在在 lib 文件夹中创建,为了更简洁的 API 实现,我们将在与生成的文件相同的文件夹中创建一个 native_mobile_ui.dart
文件,其中代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
import 'native_mobile_ui.g.dart';
final _api = NativeMobileHostApi();
Future<String> getNativeUiResult() async {
try {
return await _api.getNativeUiResult();
} catch (e) {
return 'Failed to retrieve result';
}
}
|
现在我们从 main.dart 中直接调用 getNativeUiResult
,就是这样!!!
Android
检查 android 文件夹中生成的代码,可以看到 Pigeon 为我们的 NativeMobileHostApi
生成了一个接口,该接口有一个配套对象类,用于处理通信通道的设置。我们可以使用 setUp 函数调用该通道,该函数采用 binaryMessenger 和可为 null 的 NativeMobileHostApi。
由于它是一个接口,我们只需要实现它即可,就像下面代码所做的一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
package com.example.mobile_declarative_ui
import FlutterError
import NativeMobileHostApi
import android.content.Intent
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
typealias FlutterResultCallback = (Result<String>) -> Unit
class MainActivity : FlutterActivity(), NativeMobileHostApi {
private var nativeUiResultCallback: FlutterResultCallback? = null
private val composeActivityRequestCode: Int = 4
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
NativeMobileHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, this)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (nativeUiResultCallback == null) return@onActivityResult
if (requestCode == composeActivityRequestCode && resultCode == RESULT_OK) {
val value = data?.getStringExtra(ComposeActivity.REPLY_MESSAGE)
if (value == null) {
nativeUiResultCallback?.invoke(
Result.failure(FlutterError("code", "message", "details"))
)
return@onActivityResult
}
nativeUiResultCallback?.invoke(Result.success(value))
}
}
override fun getNativeUiResult(callback: (Result<String>) -> Unit) {
nativeUiResultCallback = callback
val intent = Intent(this, ComposeActivity::class.java)
startActivityForResult(intent, composeActivityRequestCode)
}
}
|
结论
Pigeno 通过一种类型安全并且惯用的方法来改进我们的 Flutter 应用程序与原生 API 的交互,其官方文档和示例项目是进行更多探索的好地方。
快乐编码 🚀
Referent