使用 dart:ffi 绑定到原生 macOS 代码
Flutter 移动和桌面应用可以使用 dart:ffi 库来调用原生 C API。FFI 代表 foreign function interface(外部函数接口)。其他表示类似功能的术语包括 native interface(原生接口)和 language bindings(语言绑定)。
在你的库或程序可以使用 FFI 库绑定到原生代码之前,你必须确保原生代码已加载并且其符号对 Dart 可见。本页重点介绍如何在 Flutter 插件或应用中编译、打包和加载 macOS 原生代码。
本教程演示了如何在 Flutter 插件中捆绑 C/C++ 源代码,并使用 macOS 上的 Dart FFI 库绑定到它们。在本教程中,你将创建一个实现 32 位加法的 C 函数,然后通过名为“native_add”的 Dart 插件将其公开。
动态链接与静态链接
#原生库可以动态或静态地链接到应用中。静态链接库嵌入到应用的 executables 映像中,并在应用启动时加载。
静态链接库中的符号可以使用 DynamicLibrary.executable
或 DynamicLibrary.process
加载。
相比之下,动态链接库以独立文件或文件夹的形式在应用内部分发,并按需加载。在 macOS 上,动态链接库以 .framework
文件夹的形式分发。
动态链接库可以使用 DynamicLibrary.open
加载到 Dart 中。
API 文档可从 Dart API 参考文档 获取。
创建 FFI 插件
#如果你已经有插件,请跳过此步骤。
要创建名为“native_add”的插件,请执行以下操作
flutter create --platforms=macos --template=plugin_ffi native_add
cd native_add
这将在 native_add/src
中创建一个包含 C/C++ 源代码的插件。这些源代码由不同操作系统构建文件夹中的原生构建文件构建。
FFI 库只能绑定 C 符号,因此在 C++ 中,这些符号会被标记为 extern "C"
。
您还应该添加属性来指示这些符号是从 Dart 引用的,以防止链接器在链接时优化期间丢弃这些符号。__attribute__((visibility("default"))) __attribute__((used))
。
在 iOS 上,native_add/macos/native_add.podspec
用于链接代码。
原生代码在 lib/native_add_bindings_generated.dart
中从 Dart 调用。
绑定是使用 package:ffigen 生成的。
其他用例
#iOS 和 macOS
#动态链接库在应用启动时由动态链接器自动加载。它们的组成符号可以使用 DynamicLibrary.process
解析。你也可以使用 DynamicLibrary.open
获取库句柄以限制符号解析的范围,但目前尚不清楚 Apple 的审查流程如何处理此问题。
静态链接到应用程序二进制文件中的符号可以使用 DynamicLibrary.executable
或 DynamicLibrary.process
解析。
平台库
#要链接到平台库,请使用以下说明
- 在 Xcode 中,打开
Runner.xcworkspace
。 - 选择目标平台。
- 在“**Linked Frameworks and Libraries**”(链接的框架和库)部分中点击 **+**。
- 选择要链接的系统库。
第一方库
#第一方原生库可以作为源代码或(签名)的 .framework
文件包含在内。可能也可以包含静态链接的归档文件,但这需要测试。
源代码
#要直接链接到源代码,请使用以下说明
在 Xcode 中,打开
Runner.xcworkspace
。将 C/C++/Objective-C/Swift 源文件添加到 Xcode 项目中。
为导出的符号声明添加以下前缀,以确保它们对 Dart 可见
C/C++/Objective-C
objcextern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used))
Swift
swift@_cdecl("myFunctionName")
已编译(动态)库
#要链接到已编译的动态库,请使用以下说明
- 如果存在正确签名的
Framework
文件,请打开Runner.xcworkspace
。 - 将框架文件添加到“**Embedded Binaries**”(嵌入式二进制文件)部分。
- 同时将其添加到 Xcode 中目标的“**Linked Frameworks & Libraries**”(链接的框架和库)部分。
已编译(动态)库 (macOS)
#要将闭源库添加到 Flutter macOS 桌面 应用中,请使用以下说明
- 按照 Flutter 桌面应用的说明创建一个 Flutter 桌面应用。
- 在 Xcode 中打开
yourapp/macos/Runner.xcworkspace
。- 将你的预编译库 (
libyourlibrary.dylib
) 拖到Runner/Frameworks
中。 - 点击
Runner
并转到Build Phases
选项卡。- 将
libyourlibrary.dylib
拖到Copy Bundle Resources
列表中。 - 在
Embed Libraries
下,勾选Code Sign on Copy
。 - 在
Link Binary With Libraries
下,将状态设置为Optional
。(我们使用动态链接,无需静态链接。)
- 将
- 点击
Runner
并转到General
选项卡。- 将
libyourlibrary.dylib
拖到“**Frameworks, Libraries and Embedded Content**”(框架、库和嵌入式内容)列表中。 - 选择“**Embed & Sign**”(嵌入并签名)。
- 将
- 点击“**Runner**”并转到“**Build Settings**”(构建设置)选项卡。
- 在“**Search Paths**”(搜索路径)部分,配置“**Library Search Paths**”(库搜索路径)以包含
libyourlibrary.dylib
所在的路径。
- 在“**Search Paths**”(搜索路径)部分,配置“**Library Search Paths**”(库搜索路径)以包含
- 将你的预编译库 (
- 编辑
lib/main.dart
。- 使用
DynamicLibrary.open('libyourlibrary.dylib')
动态链接到符号。 - 在某个 widget 中调用你的原生函数。
- 使用
- 运行
flutter run
并检查你的原生函数是否被调用。 - 运行
flutter build macos
来构建一个独立的发布版本应用。
其他资源
#要了解更多关于 C 互操作性的信息,请查看以下视频