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.executableDynamicLibrary.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.executableDynamicLibrary.process 解析。

平台库

#

要链接到平台库,请使用以下说明

  1. 在 Xcode 中,打开 Runner.xcworkspace
  2. 选择目标平台。
  3. 在“**Linked Frameworks and Libraries**”(链接的框架和库)部分中点击 **+**。
  4. 选择要链接的系统库。

第一方库

#

第一方原生库可以作为源代码或(签名)的 .framework 文件包含在内。可能也可以包含静态链接的归档文件,但这需要测试。

源代码

#

要直接链接到源代码,请使用以下说明

  1. 在 Xcode 中,打开 Runner.xcworkspace

  2. 将 C/C++/Objective-C/Swift 源文件添加到 Xcode 项目中。

  3. 为导出的符号声明添加以下前缀,以确保它们对 Dart 可见

    C/C++/Objective-C

    objc
    extern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used))

    Swift

    swift
    @_cdecl("myFunctionName")

已编译(动态)库

#

要链接到已编译的动态库,请使用以下说明

  1. 如果存在正确签名的 Framework 文件,请打开 Runner.xcworkspace
  2. 将框架文件添加到“**Embedded Binaries**”(嵌入式二进制文件)部分。
  3. 同时将其添加到 Xcode 中目标的“**Linked Frameworks & Libraries**”(链接的框架和库)部分。

已编译(动态)库 (macOS)

#

要将闭源库添加到 Flutter macOS 桌面 应用中,请使用以下说明

  1. 按照 Flutter 桌面应用的说明创建一个 Flutter 桌面应用。
  2. 在 Xcode 中打开 yourapp/macos/Runner.xcworkspace
    1. 将你的预编译库 (libyourlibrary.dylib) 拖到 Runner/Frameworks 中。
    2. 点击 Runner 并转到 Build Phases 选项卡。
      1. libyourlibrary.dylib 拖到 Copy Bundle Resources 列表中。
      2. Embed Libraries 下,勾选 Code Sign on Copy
      3. Link Binary With Libraries 下,将状态设置为 Optional。(我们使用动态链接,无需静态链接。)
    3. 点击 Runner 并转到 General 选项卡。
      1. libyourlibrary.dylib 拖到“**Frameworks, Libraries and Embedded Content**”(框架、库和嵌入式内容)列表中。
      2. 选择“**Embed & Sign**”(嵌入并签名)。
    4. 点击“**Runner**”并转到“**Build Settings**”(构建设置)选项卡。
      1. 在“**Search Paths**”(搜索路径)部分,配置“**Library Search Paths**”(库搜索路径)以包含 libyourlibrary.dylib 所在的路径。
  3. 编辑 lib/main.dart
    1. 使用 DynamicLibrary.open('libyourlibrary.dylib') 动态链接到符号。
    2. 在某个 widget 中调用你的原生函数。
  4. 运行 flutter run 并检查你的原生函数是否被调用。
  5. 运行 flutter build macos 来构建一个独立的发布版本应用。

其他资源

#

要了解更多关于 C 互操作性的信息,请查看以下视频