Flutter外部函数接口插件flutter3_ffi的使用

发布于 1周前 作者 caililin 来自 Flutter

Flutter外部函数接口插件flutter3_ffi的使用

简介

2024-04-30

本文介绍了如何使用 flutter3_ffi 插件来实现 Dart 和 Rust 之间的互操作性。通过 safer_ffiffigen 工具,可以生成 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_tStringUint8List 的转换

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

1 回复

更多关于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)}'),
        ),
      ),
    );
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!