MacOS 12.0
CMake:: cmake version 3.21.2
Make: GNU Make 3.81
Dart: Dart SDK version: 2.15.0÷÷
FFI (Foreign Function Interface)表示 外部功能接口,类似 JAVA 的 JNI。项目升级到 Flutter 2.0 之后,我们就可以使用 dart:ffi 库来调用 C 语言编写的代码。
在 Flutter 2.0 中的 Dart 2.12 已发布,其中包含健全的空安全和 Dart FFI 的稳定版, 并且提供了一套类型绑定生成工具 ffigen,可以自动生成 Dart Wrapper 加快开发效率。
如何将用 C/C++ 编写的代码带入到 Flutter 应用中

关于Dart语言FFI使用 ,大概步骤
解决的问题
方式
Flutter->原生JNI->C
Flutter->FFI->C
原始FFI
将已有的 c/cpp 通过交叉编译各个平台,windows的dll,unix系统的so库,然后通过DynamicLibrary的open函数去打开这个库,来获得其中函数的动态链接。随即按套路编写dart调用ffi的代码即可。
ffgen
每个平台都有一些已经有的so库,如果我们的 c/cpp 语言实现的都是一些所有unix设备或者windows设备底层自带so、dll中的函数,我们不妨将里面每一个函数都转换成dart端可以对应调用的函数,也就是生成一个dart可用的 ".h" 头,当然这个扩展名还是 ".dart" 。
这样一来,我们无需再为了 windows、macos、linux、android、ios 这5个平台分别交叉编译一份动态库,我们只需要写一份通用的dart代码即可。
Mac电脑确保安装llvm
添加依赖pubspec.yaml
添加c文件 ios/Runner/native.cpp ios/Runner/native.h
使用ffigen生成dart代码 - 终端执行

Dart与C语言,Dart FFI提供了很多方法
import 'package:ffi'
它可以加载动态链接库
它可以用于在iOS及MacOS中加载应用程序已经自动加载好的动态链接库,也可以解析静态链接到应用的二进制文件符号。需要注意的是,它不能用于windows平台
它可用于加载静态链接库
NativeType是在Dart中表示C语言中的数据结构
它是C语言中指针在Dart中的映射
它用于在DynamicLibrary中查找到对应的符号并返回其内存地址。
根据内存地址获取C对象指针
根据一个Dart函数,创建一个Native函数指针,一般用于将Dart函数传给C,使C有调用Dart函数的能力
获取指针的内存地址
将Native指针对象,转换为Dart函数
返回具体类型的内存占用
例
开辟一块大小byteCount的空间
例
释放内存
| Dart 中定义的NativeType | C语言中的类型 | 说明 |
|---|---|---|
| Opaque | opaque | 不暴露其成员类型,一般用于表示C++中的类 |
| Int8 | int8_t 或 char | 有符号8位整数 |
| Int16 | int16_t 或 short | 有符号16位整数 |
| Int32 | int32_t 或 int | 有符号32位整数 |
| Int64 | int64_t 或 long long | 有符号64位整数 |
| Uint8 | uint8_t 或 unsigned char | 无符号8位整数 |
| Uint16 | uint16_t 或 unsigned short | 无符号16位整数 |
| Uint32 | int32_t 或 unsigned int | 无符号32位整数 |
| Uint64 | uint64_t 或 unsigned long long | 无符号64位整数 |
| IntPtr | int* | 整数类型指针 |
| Float | float | 单精度浮点类型 |
| Double | double | 双精度浮点类型 |
| Void | void | void类型 |
| Handle | Dart_Handle Dart | 句柄在C中的表示形式 |
| NativeFunction | 函数 | 函数类型 |
| Struct | struct | 结构体类型 |
| Union | union | 共同体类型 |
| Pointer | * | 指针类型 |
| nullptr | NULL | 空指针 |
| dynamic | Dart_CObject | Dart对象在C中的表现 |
如何找到 C++ 中的库
通过 DynamicLibrary.open() dlopen 加载动态链接库,并返回句柄。
ffigen库来根据C/C++头文件自动生成dart 函数
注意
ffigen只能自动生成C风格的头文件,如果你的头文件中包含了C++风格代码如class,需要使用#ifdef __cplusplus #endif包裹起来
用到的工具库
文档
https://dart.cn/guides/libraries/c-interop
https://api.dart.cn/stable/2.15.1/dart-ffi/dart-ffi-library.html
MacOS 12.0
CMake:: cmake version 3.21.2
Make: GNU Make 3.81
Dart: Dart SDK version: 2.15.0÷÷
FFI (Foreign Function Interface)表示 外部功能接口,类似 JAVA 的 JNI。项目升级到 Flutter 2.0 之后,我们就可以使用 dart:ffi 库来调用 C 语言编写的代码。
在 Flutter 2.0 中的 Dart 2.12 已发布,其中包含健全的空安全和 Dart FFI 的稳定版, 并且提供了一套类型绑定生成工具 ffigen,可以自动生成 Dart Wrapper 加快开发效率。
如何将用 C/C++ 编写的代码带入到 Flutter 应用中

关于Dart语言FFI使用 ,大概步骤
解决的问题
方式
Flutter->原生JNI->C
Flutter->FFI->C
原始FFI
将已有的 c/cpp 通过交叉编译各个平台,windows的dll,unix系统的so库,然后通过DynamicLibrary的open函数去打开这个库,来获得其中函数的动态链接。随即按套路编写dart调用ffi的代码即可。
ffgen
每个平台都有一些已经有的so库,如果我们的 c/cpp 语言实现的都是一些所有unix设备或者windows设备底层自带so、dll中的函数,我们不妨将里面每一个函数都转换成dart端可以对应调用的函数,也就是生成一个dart可用的 ".h" 头,当然这个扩展名还是 ".dart" 。
这样一来,我们无需再为了 windows、macos、linux、android、ios 这5个平台分别交叉编译一份动态库,我们只需要写一份通用的dart代码即可。
Mac电脑确保安装llvm
添加依赖pubspec.yaml
添加c文件 ios/Runner/native.cpp ios/Runner/native.h
使用ffigen生成dart代码 - 终端执行

Dart与C语言,Dart FFI提供了很多方法
import 'package:ffi'
它可以加载动态链接库
它可以用于在iOS及MacOS中加载应用程序已经自动加载好的动态链接库,也可以解析静态链接到应用的二进制文件符号。需要注意的是,它不能用于windows平台
它可用于加载静态链接库
NativeType是在Dart中表示C语言中的数据结构
它是C语言中指针在Dart中的映射
它用于在DynamicLibrary中查找到对应的符号并返回其内存地址。
根据内存地址获取C对象指针
根据一个Dart函数,创建一个Native函数指针,一般用于将Dart函数传给C,使C有调用Dart函数的能力
获取指针的内存地址
将Native指针对象,转换为Dart函数
返回具体类型的内存占用
例
开辟一块大小byteCount的空间
例
释放内存
| Dart 中定义的NativeType | C语言中的类型 | 说明 |
|---|---|---|
| Opaque | opaque | 不暴露其成员类型,一般用于表示C++中的类 |
| Int8 | int8_t 或 char | 有符号8位整数 |
| Int16 | int16_t 或 short | 有符号16位整数 |
| Int32 | int32_t 或 int | 有符号32位整数 |
| Int64 | int64_t 或 long long | 有符号64位整数 |
| Uint8 | uint8_t 或 unsigned char | 无符号8位整数 |
| Uint16 | uint16_t 或 unsigned short | 无符号16位整数 |
| Uint32 | int32_t 或 unsigned int | 无符号32位整数 |
| Uint64 | uint64_t 或 unsigned long long | 无符号64位整数 |
| IntPtr | int* | 整数类型指针 |
| Float | float | 单精度浮点类型 |
| Double | double | 双精度浮点类型 |
| Void | void | void类型 |
| Handle | Dart_Handle Dart | 句柄在C中的表示形式 |
| NativeFunction | 函数 | 函数类型 |
| Struct | struct | 结构体类型 |
| Union | union | 共同体类型 |
| Pointer | * | 指针类型 |
| nullptr | NULL | 空指针 |
| dynamic | Dart_CObject | Dart对象在C中的表现 |
如何找到 C++ 中的库
通过 DynamicLibrary.open() dlopen 加载动态链接库,并返回句柄。
ffigen库来根据C/C++头文件自动生成dart 函数
注意
ffigen只能自动生成C风格的头文件,如果你的头文件中包含了C++风格代码如class,需要使用#ifdef __cplusplus #endif包裹起来
用到的工具库
文档
https://dart.cn/guides/libraries/c-interop
https://api.dart.cn/stable/2.15.1/dart-ffi/dart-ffi-library.html
brew install --force-bottle llvm@3.9
/usr/local/opt/llvm # 确保可以找到
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
# FFI
ffi: ^1.1.2
ffigen: ^4.1.2
ffigen:
name: NativeLibrary
description: Bindings to `library/hello.h`.
output: 'lib/c.dart'
headers:
entry-points:
- 'ios/Runner/native.h'
➜ ffi dart run ffigen
import 'dart:ffi' as ffi;
import 'dart:io' show Platform, Directory;
import 'package:ffi/ffi.dart';
external factory DynamicLibrary.open(String path);
import 'dart:ffi' as ffi;
final dylib = ffi.DynamicLibrary.open("libnative_add.so");
import 'dart:io' show Platform;
import 'dart:ffi' as ffi;
late ffi.DynamicLibrary native;
native = Platform.isAndroid
? ffi.DynamicLibrary.open("libnative_add.so")
: ffi.DynamicLibrary.process();
external factory DynamicLibrary.executable();
external Pointer<T> lookup<T extends NativeType>(String symbolName);
final dylib = DynamicLibrary.open(libraryPath);
late final _hello_worldPtr =
dylib.lookup<NativeFunction<Void Function()>>('hello_world');
late final _hello_world = _hello_worldPtr.asFunction<void Function()>();
_hello_world();
// 创建一个指向NULL的Native指针
final Pointer<Never> nullptr = Pointer.fromAddress(0);
void globalCallback(int src, int result) {
print("globalCallback src=$src, result=$result");
}
Pointer.fromFunction(globalCallback);
ffi.sizeOf<ffi.Int64>(); // 8
复制代码
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment});
复制代码
Pointer<Uint8> bytes = malloc.allocate<Uint8>(ffi.sizeOf<ffi.Uint8>());
复制代码
malloc.free(bytes);
dart run ffigen
/// 两个数相加
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}
brew install --force-bottle llvm@3.9
/usr/local/opt/llvm # 确保可以找到
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
# FFI
ffi: ^1.1.2
ffigen: ^4.1.2
ffigen:
name: NativeLibrary
description: Bindings to `library/hello.h`.
output: 'lib/c.dart'
headers:
entry-points:
- 'ios/Runner/native.h'
➜ ffi dart run ffigen
import 'dart:ffi' as ffi;
import 'dart:io' show Platform, Directory;
import 'package:ffi/ffi.dart';
external factory DynamicLibrary.open(String path);
import 'dart:ffi' as ffi;
final dylib = ffi.DynamicLibrary.open("libnative_add.so");
import 'dart:io' show Platform;
import 'dart:ffi' as ffi;
late ffi.DynamicLibrary native;
native = Platform.isAndroid
? ffi.DynamicLibrary.open("libnative_add.so")
: ffi.DynamicLibrary.process();
external factory DynamicLibrary.executable();
external Pointer<T> lookup<T extends NativeType>(String symbolName);
final dylib = DynamicLibrary.open(libraryPath);
late final _hello_worldPtr =
dylib.lookup<NativeFunction<Void Function()>>('hello_world');
late final _hello_world = _hello_worldPtr.asFunction<void Function()>();
_hello_world();
// 创建一个指向NULL的Native指针
final Pointer<Never> nullptr = Pointer.fromAddress(0);
void globalCallback(int src, int result) {
print("globalCallback src=$src, result=$result");
}
Pointer.fromFunction(globalCallback);
ffi.sizeOf<ffi.Int64>(); // 8
复制代码
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment});
复制代码
Pointer<Uint8> bytes = malloc.allocate<Uint8>(ffi.sizeOf<ffi.Uint8>());
复制代码
malloc.free(bytes);
dart run ffigen
/// 两个数相加
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}