Flutter + Rust ffi 开发跨平台 UI 程序入门

Flutter + Rust ffi 开发跨平台 UI 程序入门
枫安Maplean
已于 2022-04-28 21:01:55 修改 1491
收藏 1
文章标签: flutter rust
版权
Flutter + Rust ffi 开发跨平台 UI 程序入门

最近一直使用 Rust 开发程序,就研究了一下如何使用 rust 进行桌面程序的开发,发现有两个比较流行的方法,其一是使用 Tauri,基于 WebVie;其二则是使用 Flutter,因为担心 web 的性能问题,所以研究了一下 Flutter。在这里记录一下基本方法。
先做一个简单的实现

Flutter 的示例小程序是一个计数器,通过点击按钮来使屏幕上的数字自增。本文的目的就是改变这个小程序,使得自增这个过程在 rust 中完成。
RUST 后端程序

首先我们先来写 RUST 后端,使用 cargo new 创建一个新的 RUST 程序。

cargo new rust-ffi-backend

1

编译符合 C ABI 的链接库

接下来我们来写点代码,编辑 lib.rs 文件,实现调用一次函数,全局变量自增,为了和 Flutter 的示例程序做出区别,我们调用一次自增 2。

static mut COUNT: u32 = 0;

#[no_mangle]
pub unsafe extern "C" fn count_add_self() -> u32 {
COUNT += 2;
COUNT
}

 

现在添加需要编辑 Cargo.toml 文件,将目标生成为符合 C ABI 的动态链接库。

[lib]
name = "rust_ffi_backend"
crate-type = ["cdylib"]

1
2
3

我们先来编译一下我们的 RUST 程序:

cargo build --release

1

生成 C 语言头文件

接下来我们可以使用一个开源项目 cbindgen,他会帮助我们生成 C 语言的头文件。

首先我们安装它:

cargo install --force cbindgen

1

注意 cbingen 可能依赖于 LLVM 等许多其他程序,如果安装失败请详细看提示,安装缺少的组件。

然后在我们的项目目录下创建一个 cbindgen.toml 文件,比如它可能是下面这个样子:

# This is a template cbindgen.toml file with all of the default values.
# Some values are commented out because their absence is the real default.
#
# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
# for detailed documentation of every option here.

language = "C"

############## Options for Wrapping the Contents of the Header #################

header = "/* Text to put at the beginning of the generated file. Probably a license. */"
no_includes = true
after_includes = "typedef unsigned int uint32_t;"

############################ Code Style Options ################################

braces = "SameLine"
line_length = 100
tab_width = 4
documentation = true
documentation_style = "auto"
documentation_length = "full"
line_endings = "LF" # also "CR", "CRLF", "Native"

############################# Codegen Options ##################################

style = "both"
sort_by = "Name" # default for `fn.sort_by` and `const.sort_by`
usize_is_size_t = true


[export]
include = []
exclude = []
item_types = []
renaming_overrides_prefixing = false

[fn]
rename_args = "None"
args = "auto"
sort_by = "Name"

[struct]
rename_fields = "None"
derive_constructor = false
derive_eq = false
derive_neq = false
derive_lt = false
derive_lte = false
derive_gt = false
derive_gte = false

[enum]
rename_variants = "None"
add_sentinel = false
prefix_with_name = false
derive_helper_methods = false
derive_const_casts = false
derive_mut_casts = false
derive_tagged_enum_destructor = false
derive_tagged_enum_copy_constructor = false
enum_class = true
private_default_tagged_enum_constructor = false

[const]
allow_static_const = true
allow_constexpr = false
sort_by = "Name"

[macro_expansion]
bitflags = false

############## Options for How Your Rust library Should Be Parsed ##############

[parse]
parse_deps = false
exclude = []
clean = false
extra_bindings = []

[parse.expand]
crates = []
all_features = false
default_features = true
features = []

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

现在我们尝试将他转换为 C 语言的头文件吧, 使用:

cbindgen --config cbindgen.toml --crate rust-ffi-backend --output target/release/rust-ffi-backend.h

1

可以在 target/release/rust-ffi-backend-ffi.h 文件内看到 cbindgen 问我们生成的 C 语言头文件了,他可能长下面这个样子:

/* Text to put at the beginning of the generated file. Probably a license. */

typedef unsigned int uint32_t;
uint32_t count_add_self();

1
2
3
4

Flutter 前端

现在我们来完成基于 Flutter 的前端桌面程序,首先新创建一个 flutter 工程:

flutter create flutter_frontend

1

打开它,我们就能看到默认的计数器小程序了,先不着急修改程序,我们先把我们的 RUST 后端导入进来。
先来导入 RUST 后端程序

新建一个名叫 rust-ffi 的文件夹存放 RUST 的库和头文件:

mkdir rust-ffi

1

将刚才生成的文件都拷贝过来:

cp ../rust-ffi-backend/target/release/rust-ffi-backend.so rust-ffi/
cp ../rust-ffi-backend/target/release/rust-ffi-backend.h rust-ffi/

1
2

现在,我们要依赖 dart 官方的一个名为 ffigen 的程序,它可以帮助我们把 C ABI 的头文件生成 dart 源程序。

首先编辑 pubspec.yaml 文件,在最后添加下面内容:

ffigen:
output: 'lib/rust-ffi-backend.dart'
headers:
entry-points:
- 'rust-ffi/rust-ffi-backend.h'

1
2
3
4
5

然后使用工具生成 dart 文件:

dart run ffigen

1

dart 的 ffigen 工具同样需要依赖 LLVM 等程序,如果报错请详细查看错误原因,安装必要的组件。

这是我们看到在 lib 文件夹下应该生成了我们的 rust-ffi-backend.dart 文件,它可能是长这样的:

// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
import 'dart:ffi' as ffi;

class NativeLibrary {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;

/// The symbols are looked up in [dynamicLibrary].
NativeLibrary(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;

/// The symbols are looked up with [lookup].
NativeLibrary.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;

int count_add_self(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Uint32)>> process,
) {
return _count_add_self(
process,
);
}

late final _count_add_selfPtr = _lookup<
ffi.NativeFunction<
ffi.Uint32 Function(
ffi.Pointer<
ffi.NativeFunction<ffi.Void Function(ffi.Uint32)>>)>>(
'count_add_self');
late final _count_add_self = _count_add_selfPtr.asFunction<
int Function(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Uint32)>>)>();

看到 ffigen 为我们生成了一个 NativeLibrary 类,它拥有一个 count_add_self 方法,对应就是我们 RUST 中的同名方法。

制作 Flutter 前端程序调用 RUST

接下来我们就来使用它。

打开 main.dart 文件,找到 _MyHomePageState 类,首先引入我们的库,将我们的库作为一个类的成员使用,在 class 内添加如下内容:

NativeLibrary nativelib =
NativeLibrary(DynamicLibrary.open('rust-ffi/rust-ffi-backend.so'));

1
2

添加好以后,Flutter 就会在每次初始化 _MyHomePageState 类是加载我们的名为 rust-ffi-backend.so 动态链接库了。

找到 _incrementCounter 这个函数,可以看到如下内容:

void _incrementCounter() async {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.

_counter++;
});

 

将其中的 _counter++ 这行代码,替换为如下代码:

_counter = nativelib.count_add_self();

1

这样,在 setState 方法中就不会在直接将计数自增,而是会调用我们 RUST 中的方法将 RUST 中的全局变量 COUNT 自增,再返回 COUNT 的值,也就是说,实际上代码到 RUST 中兜了一圈。

现在运行我们的 Flutter 工程:

flutter run

1

可以看到,每当我们点击一下,计数自增 2, 说明是调用到了 RUST 的函数,实现了 Flutter 和 RUST 的结合。
这里记录一些复杂的用法

先写到这吧,后面补上。
————————————————
版权声明:本文为CSDN博主「枫安Maplean」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hyklose/article/details/124482491

posted @   商君治国安邦之张莽  阅读(1354)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示