资讯动态

Dart FFI 完全指南:从入门到实战

发布时间:2026/8/26 18:26:51 来源:尧图企业网站定制
Dart FFI 完全指南从入门到实战Dart FFIForeign Function Interface是 Dart 官方提供的高性能互操作机制允许 Dart 代码直接调用 C/C 编写的原生代码。对于 Flutter 开发者来说当遇到计算密集型任务如图像处理、复杂算法或需要复用现有的 C/C 代码库时FFI 是不可或缺的工具。本文将详细讲解 Dart FFI 的使用方法从最简单的示例到实际项目集成一步步带你掌握这门技术。官方教程使用 dart:ffi 进行 C 语言互操作一、FFI 的基本概念在开始写代码之前先理解几个核心概念Dart FFI 是什么它是 Dart 与 C 语言之间的“桥梁”让 Dart 代码能调用 C 语言的函数读写 C 语言的内存。注意FFI 只能直接绑定C 符号所以 C 函数必须用extern C包裹。两种集成方式源码集成 vs 预编译库方式说明适用场景源码集成将.c/.cpp源码放在项目中由构建系统编译并链接进 App第一方代码需要方便调试和修改预编译库用DynamicLibrary.open()加载.so/.framework等动态库使用闭源的第三方库动态链接 vs 静态链接动态链接库以独立文件分发按需加载用DynamicLibrary.open()打开静态链接库嵌入 App 可执行文件应用启动时加载用DynamicLibrary.executable或DynamicLibrary.process解析符号二、从零开始示例要在 Dart 中使用dart:ffi调用 C 代码核心思路是通过extern C将 C 函数暴露为 C 接口然后在 Dart 中加载动态库并绑定这些函数。对于更复杂的消息传递场景也可以借助flutter_cpp_bridge这样的工具包来简化开发。1在 C 侧暴露函数FFI 只能与 C 语言的符号交互所以 C 代码必须使用extern C来阻止名称修饰Name Mangling。为了防止链接器在优化时丢弃这些函数最好加上可见性和使用的属性声明。创建一个 C 源文件例如src/native_add.cpp#includecstdint// 关键点使用 extern C 包裹并添加属性防止被优化掉externC__attribute__((visibility(default)))__attribute__((used))int32_tadd(int32_ta,int32_tb){returnab;}2编译为动态库你需要将 C 代码编译成对应平台的动态库如 Android 的libnative_add.soiOS 的.frameworkWindows 的.dllmacOS/Linux 的.dylib或.so。最简单的入门方式是使用 Flutter 官方提供的plugin_ffi模板创建项目见下文它会帮你配置好所有平台的编译脚本flutter create--platformsandroid,ios,macos,windows,linux--templateplugin_ffi native_add这个模板的native_add/src目录就是放置 C/C 源码的地方。3在 Dart 侧加载并调用在 Dart 代码中1、使用dart:ffi的DynamicLibrary.open()加载动态库2、然后通过.lookup()获取函数指针并转换为 Dart 函数。importdart:ffi;importdart:typed_data;voidmain(){// 1. 加载动态库// 在 Flutter 插件中库的路径会被正确配置// 此处以直接加载为例实际开发中路径会更复杂finaldylibDynamicLibrary.open(libnative_add.so);// 2. 查找并绑定 C 函数// 声明函数类型NativeFunctionInt32 Function(Int32, Int32)finaladdNativedylib.lookupNativeFunctionInt32Function(Int32,Int32)(add);// 将 Native 函数转换为 Dart 可调用的函数finaladdaddNative.asFunctionintFunction(int,int)();// 3. 调用finalresultadd(10,20);print(10 20 $result);}三、更复杂的示例传递参数和返回值3.1 C 代码带参数的加法函数// src/native_add.c#includestdint.h// 关键extern C 防止 C 名称修饰// __attribute__ 防止链接器优化时丢弃符号externC__attribute__((visibility(default)))__attribute__((used))int32_tadd(int32_ta,int32_tb){returnab;}3.2 Dart 绑定importdart:ffiasffi;// C 函数签名int32_t add(int32_t a, int32_t b)typedefAddFuncffi.Int32Function(ffi.Int32a,ffi.Int32b);typedefAddintFunction(int a,int b);finaldylibffi.DynamicLibrary.open(libnative_add.so);finalAddadddylib.lookupffi.NativeFunctionAddFunc(add).asFunction();voidmain(){print(1 2 ${add(1,2)});// 输出1 2 3}3.3 常用 FFI 类型映射C 类型Dart FFI 类型Dart 普通类型voidffi.Voidvoidintffi.Int32/ffi.Int64intfloatffi.Floatdoubledoubleffi.Doubledoublechar*ffi.Pointerffi.CharPointerCharuint8_t*ffi.Pointerffi.Uint8PointerUint8四、字符串和内存操作4.1 从 C 返回字符串C 代码#includestdlib.h#includestring.hexternC__attribute__((visibility(default)))char*get_message(){char*msg(char*)malloc(20);strcpy(msg,Hello from C!);returnmsg;}externC__attribute__((visibility(default)))voidfree_message(char*msg){free(msg);}Dart 代码importdart:ffiasffi;importpackage:ffi/ffi.dart;// 提供 toDartString() 等工具typedefGetMessageFuncffi.Pointerffi.CharFunction();typedefGetMessageffi.Pointerffi.CharFunction();typedefFreeMessageFuncffi.VoidFunction(ffi.Pointerffi.Char);typedefFreeMessagevoidFunction(ffi.Pointerffi.Char);finalgetMessagedylib.lookupffi.NativeFunctionGetMessageFunc(get_message).asFunction();finalfreeMessagedylib.lookupffi.NativeFunctionFreeMessageFunc(free_message).asFunction();voidmain(){finalptrgetMessage();print(ptr.castUtf8().toDartString());// 输出Hello from C!freeMessage(ptr);// 重要释放 C 分配的内存}4.2 Dart 传递字符串给 Cimportpackage:ffi/ffi.dart;// C 函数void process_string(const char* str)typedefProcessStringFuncffi.VoidFunction(ffi.Pointerffi.Char);typedefProcessStringvoidFunction(ffi.Pointerffi.Char);voidmain(){finalProcessStringprocessdylib.lookupffi.NativeFunctionProcessStringFunc(process_string).asFunction();// 使用 toNativeUtf8() 将 Dart 字符串转为 C 字符串finalstrHello Dart!.toNativeUtf8();process(str);calloc.free(str);// 释放分配的内存}五、在 Flutter 项目中集成 C 源码官方推荐使用package_ffi模板来创建 FFI 包它通过build.dart构建钩子自动编译原生代码无需手动配置各平台的构建文件。5.1 创建 FFI 包flutter create--templatepackage_ffi native_addcdnative_add生成的目录结构native_add/ ├── lib/ │ ├── native_add.dart # 公开 API │ └── native_add_bindings_generated.dart # ffigen 生成的绑定 ├── src/ │ ├── native_add.c # C 源码 │ └── native_add.h # C 头文件 ├── hook/ │ └── build.dart # 构建钩子自动编译原生代码 ├── ffigen.yaml # ffigen 配置 └── pubspec.yaml5.2 C 源码的写法// src/native_add.cpp#includestdint.h// 必须用 extern C// 必须添加可见性属性防止符号被丢弃externC__attribute__((visibility(default)))__attribute__((used))int32_tadd(int32_ta,int32_tb){returnab;}externC__attribute__((visibility(default)))__attribute__((used))int32_tmultiply(int32_ta,int32_tb){returna*b;}5.3 使用 ffigen 自动生成绑定ffigen.yaml配置name:NativeAddBindingsdescription:Bindings for native_addoutput:lib/native_add_bindings_generated.dartheaders:entry-points:-src/native_add.hinclude-directives:-src/native_add.hcompiler-opts:--I.--Isrc生成绑定dart run ffigen生成的代码会包含类型安全的 Dart 函数可以直接调用。5.4 在 Flutter 应用中引用// lib/native_add.dartimportnative_add_bindings_generated.dart;classNativeAdd{staticfinalNativeAddBindings_bindingsNativeAddBindings();staticintadd(int a,int b)_bindings.add(a,b);staticintmultiply(int a,int b)_bindings.multiply(a,b);}六、旧版 plugin_ffi 模板如需使用 Flutter Plugin API如果你使用 plugin_ffi 模板创建项目通常不需要手动编译flutter create--platformsandroid,ios,macos,windows,linux--templateplugin_ffi native_add这个模板会生成各平台的构建文件Androidnative_add/android/build.gradle调用 CMakeAndroidC 源码放在 android/src/main/cpp/Gradle 会自动编译iOS/macOSnative_add/ios/native_add.podspeciOS/macOS通过 Xcode 项目配置构建时自动编译Linuxnative_add/linux/CMakeLists.txtWindowsnative_add/windows/CMakeLists.txtLinux/Windows通过 CMake 配置flutter build 时自动编译你只需要flutter run# 就会自动完成编译 运行七、常见问题与注意事项7.1 为什么 C 必须用 extern “C”C 编译器会对函数名进行“名称修饰”name mangling改变符号名。extern C告诉编译器使用 C 语言的命名规则这样 Dart 才能通过函数名字符串找到它。7.2 符号找不到怎么办添加__attribute__((visibility(default))) __attribute__((used))确保符号被导出且不会被链接器优化掉。7.3 Android 只支持动态链接因为 Android 的主可执行文件是 JVMFlutter 无法静态链接到它所以必须使用动态库.so文件。7.4 iOS 加载动态库的注意事项iOS 上动态库以.framework形式分发需要正确签名并嵌入 Xcode 项目。7.5 性能优化建议对于执行时间较长的算法建议在辅助 Isolate中执行 FFI 调用避免阻塞 UI 主线程。八、总结Dart FFI 的核心流程可以用这张图概括Dart 代码 → 定义类型签名 → 加载动态库 → 查找符号 → 转换为 Dart 函数 → 调用最佳实践使用package_ffi模板创建新的 FFI 包Flutter 3.38 推荐利用ffigen自动生成绑定减少手动工作量C 函数必须用extern C导出并添加可见性属性注意内存管理C 分配的内存需要由 C 的释放函数释放耗时操作放到 Isolate中执行掌握了这些知识你就可以在 Flutter 项目中自由调用 C/C 算法库享受原生性能带来的优势了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价