Flutter外部函数接口插件flutter3_ffi的使用
Flutter外部函数接口插件flutter3_ffi的使用
简介
2024-04-30
本文介绍了如何使用 flutter3_ffi
插件来实现 Dart 和 Rust 之间的互操作性。通过 safer_ffi
和 ffigen
工具,可以生成 C 头文件并将其转换为 Flutter 的绑定代码。
使用 safer_ffi
在 Rust 工程中引入 safer_ffi
在 Cargo.toml
文件中添加以下配置:
[lib]
crate-type = [
"staticlib", # 确保编译为静态 C 库
"cdylib", # 如果需要动态库(高级用法)
]
[dependencies]
safer-ffi.version = "0.1.7"
safer-ffi.features = []
[features]
headers = ["safer-ffi/headers"]
在 lib.rs
文件中生成头文件
#[test]
#[cfg(feature = "headers")] // 配置特性以生成头文件
pub fn generate_headers() -> std::io::Result<()> {
safer_ffi::headers::builder()
.to_file("rust_headers.h")? // 输出到指定文件
.generate(); // 生成头文件
}
在 api.rs
文件中定义导出接口
/// 测试输入一个字符串, 返回对应的字符串
#[ffi_export]
fn test_string(str: &safer_ffi::String) -> safer_ffi::String {
format!("Hello, {}", str).into()
}
/// 测试输入一个字节数组, 返回对应的字节长度
#[ffi_export]
fn test_bytes(bytes: &safer_ffi::Vec<u8>) -> usize {
bytes.len()
}
/// 导出结构体
#[derive_ReprC]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Point {
x: f64,
y: f64,
}
/// 计算两点之间的中点
#[ffi_export]
fn mid_point(a: &Point, b: &Point) -> Point {
Point {
x: (a.x + b.x) / 2.,
y: (a.y + b.y) / 2.,
}
}
/// 打印点信息
#[ffi_export]
fn print_point(point: &Point) {
println!("{:?}", point);
}
生成头文件
运行以下命令生成头文件:
cargo test --package rust --lib generate_headers --features headers
使用 ffigen
创建 ffigen.yaml
配置文件
name: FfigenSaferFfiDemoBindings
description: |
Bindings for `src/ffigen_safer_ffi_demo.h`.
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
output: 'lib/ffigen_safer_ffi_demo_bindings_generated.dart'
headers:
entry-points:
- 'src/ffigen_safer_ffi_demo.h'
- 'rust/rust_headers.h'
include-directives:
- 'src/ffigen_safer_ffi_demo.h'
- 'rust/rust_headers.h'
- '*.h'
preamble: |
// ignore_for_file: always_specify_types
// ignore_for_file: camel_case_types
// ignore_for_file: non_constant_identifier_names
comments:
style: any
length: full
生成 Flutter 绑定文件
运行以下命令生成 Dart 绑定代码:
dart run ffigen --config ffigen.yaml
示例代码
Vec_uint8_t
、String
和 Uint8List
的转换
将 String
转换为 Vec_uint8_t
/// 测试输入一个字符串, 返回对应的字符串
String? testString(String str) {
final bytes = utf8.encode(str); // 将字符串编码为字节数组
final Pointer<Uint8> bytesPtr = calloc.allocate<Uint8>(bytes.length); // 分配内存
final Uint8List nativeBytes = bytesPtr.asTypedList(bytes.length); // 转换为 Dart List
nativeBytes.setAll(0, bytes); // 填充数据
final ptr = calloc<Vec_uint8_t>(); // 分配 Vec_uint8_t 指针
ptr.ref.len = bytes.length; // 设置长度
ptr.ref.cap = bytes.length; // 设置容量
ptr.ref.ptr = bytesPtr; // 指向字节数组
try {
final resultVec = _bindings.test_string(ptr); // 调用 Rust 函数
final result = resultVec.ptr; // 获取返回值指针
final reversedBytes = result.asTypedList(resultVec.len); // 转换为字节数组
final reversedString = utf8.decode(reversedBytes); // 解码为字符串
return reversedString;
} catch (e) {
print(e);
return null;
} finally {
calloc.free(ptr); // 释放内存
calloc.free(bytesPtr);
}
}
将 Uint8List
转换为字节数组长度
/// 测试输入一个字节数组, 返回对应的字节长度
int testBytes(Uint8List bytes) {
final watchGenerate = Stopwatch()..start();
final Pointer<Uint8> bytesPtr = malloc.allocate<Uint8>(bytes.length); // 分配内存
final Uint8List nativeBytes = bytesPtr.asTypedList(bytes.length); // 转换为 Dart List
nativeBytes.setAll(0, bytes); // 填充数据
watchGenerate.stop();
print("allocate: ${watchGenerate.elapsedMilliseconds}ms");
final ptr = malloc<Vec_uint8_t>(); // 分配 Vec_uint8_t 指针
ptr.ref.len = bytes.length; // 设置长度
ptr.ref.cap = bytes.length; // 设置容量
ptr.ref.ptr = bytesPtr; // 指向字节数组
try {
final watchBinding = Stopwatch()..start();
final result = _bindings.test_bytes(ptr); // 调用 Rust 函数
watchBinding.stop();
print("bindings: ${watchBinding.elapsedMilliseconds}ms");
return result;
} catch (e) {
print(e);
return 0;
} finally {
malloc.free(ptr); // 释放内存
malloc.free(bytesPtr);
}
}
更多关于Flutter外部函数接口插件flutter3_ffi的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter外部函数接口插件flutter3_ffi的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
flutter3_ffi
是一个用于在 Flutter 中调用外部 C/C++ 代码的插件,它通过 Flutter 的 Foreign Function Interface (FFI) 机制来实现与原生代码的交互。使用 flutter3_ffi
可以让你在 Flutter 应用中直接调用 C/C++ 函数,从而实现高性能的计算或访问底层系统功能。
以下是如何使用 flutter3_ffi
插件的基本步骤:
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 flutter3_ffi
插件的依赖:
dependencies:
flutter:
sdk: flutter
flutter3_ffi: ^1.0.0 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 创建 C/C++ 代码
在 lib
目录下创建一个 native
文件夹,并在其中编写你的 C/C++ 代码。例如,创建一个 native/add.c
文件:
#include <stdint.h>
int32_t add(int32_t a, int32_t b) {
return a + b;
}
3. 编译 C/C++ 代码
你需要将 C/C++ 代码编译为动态库(.so
或 .dll
文件)。具体编译步骤取决于你的平台。
例如,在 Linux 上,你可以使用以下命令编译:
gcc -shared -o libadd.so -fPIC native/add.c
在 Windows 上,你可以使用 MinGW 或 Visual Studio 来编译。
4. 在 Flutter 中加载动态库
在 Flutter 中,使用 dart:ffi
来加载动态库并调用其中的函数。首先,在 lib
目录下创建一个 ffi.dart
文件:
import 'dart:ffi';
import 'package:flutter3_ffi/flutter3_ffi.dart';
// 加载动态库
final DynamicLibrary nativeAddLib = Platform.isAndroid
? DynamicLibrary.open('libadd.so')
: DynamicLibrary.process();
// 定义 C 函数签名
typedef AddFunction = Int32 Function(Int32, Int32);
typedef Add = int Function(int, int);
// 获取 C 函数
final Add add = nativeAddLib
.lookup<NativeFunction<AddFunction>>('add')
.asFunction<Add>();
5. 在 Flutter 中调用 C 函数
现在你可以在 Flutter 中调用 C 函数了。例如,在 main.dart
中:
import 'package:flutter/material.dart';
import 'ffi.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter FFI Example'),
),
body: Center(
child: Text('Result: ${add(3, 5)}'),
),
),
);
}
}