flutter-rust-bridge — Dart 调 Rust 像调本地函数
是什么
flutter_rust_bridge 是一个代码生成器:你写普通 Rust 函数,它帮你生成全套 Dart 绑定,让 Flutter 像调本地 Dart 方法一样调用 Rust。日常类比:像一个实时翻译官——Rust 工程师用母语说话,Dart 工程师听到的已经是地道的 Dart。
你在 Rust 里写:
pub fn greet(name: String) -> String { format!("Hello, {}!", name)}运行一次代码生成后,Dart 侧就能这样调:
final result = await greet(name: 'World');print(result); // Hello, World!你没写一行 FFI 代码。FFI(Foreign Function Interface,外语调用接口)是不同编程语言之间互相调用代码的通道——就像两个说不同语言的人需要一个翻译协议。工具帮你生成了类型转换、内存管理、线程切换的全部胶水层。这个”胶水层自动化”就是 flutter_rust_bridge 的核心价值——它是 Flutter 官方 Favorite 认证包,累计 5000+ 星。
为什么重要
不理解 flutter_rust_bridge,下面这些事都没法解释:
- 为什么 Flutter App 能调用 Rust 写的密码学/图像处理/音视频库,同时保持 UI 60fps 不掉帧
- 为什么手写
dart:ffi绑定如此痛苦(类型映射、内存对齐、指针生命周期),而 flutter_rust_bridge 能消灭这些 - 为什么跨语言绑定”要么能用,要么安全”的困境在这里可以同时解决
- 为什么在移动端实现”高性能计算 + 精美 UI”的最优解往往是 Flutter + Rust,而不是 Flutter + C++
核心要点
-
代码生成而非运行时反射:flutter_rust_bridge_codegen 在构建阶段解析 Rust AST,生成对应 Dart 类和 Rust C ABI 包装层。类比:不是运行时口译,而是提前印好双语对照手册。好处是零运行时开销,坏处是改了 Rust API 必须重新生成。
-
任意类型穿越语言边界:普通 FFI 只能传整数、指针(原始内存地址);flutter_rust_bridge 支持
String、Vec<T>(动态数组)、复杂struct/enum、Result(成功/错误二选一)、Stream(数据流)、甚至闭包和 trait 对象(接口)。背后是自动序列化/反序列化(SSE 编解码器,把 Rust 数据打包成 Dart 能读的字节序列)和 Opaque 类型——复杂类型在 Rust 内存里活着,Dart 只拿一个”门牌号”(句柄),不用管里面怎么存。 -
双向调用 + 多异步模式:不只是 Dart→Rust;Rust 也能通过
StreamSink回调 Dart。异步模式有四种组合(async Dart + sync Rust、sync Dart + async Rust 等),CPU 密集任务可以丢进 Rust 线程池,IO 任务可以用 async Rust,互不阻塞。
实践案例
案例 1:图像滤镜——把计算搬进 Rust
Flutter 图像处理如果全写在 Dart,大图会卡顿。把核心算法搬到 Rust:
pub async fn apply_grayscale(pixels: Vec<u8>) -> Vec<u8> { pixels .chunks(4) .flat_map(|rgba| { let gray = (rgba[0] as u16 * 299 + rgba[1] as u16 * 587 + rgba[2] as u16 * 114) / 1000; [gray as u8, gray as u8, gray as u8, rgba[3]] }) .collect()}运行 flutter_rust_bridge_codegen generate 后,Dart 侧:
// Dart: 完全不用管内存,await 等结果即可final grayPixels = await applyGrayscale(pixels: rawBytes);逐部分解释:
async fn让 Rust 在独立线程跑,不阻塞 Flutter 主线程Vec<u8>自动序列化为 DartUint8List,零手动转换- Dart 侧看到的就是普通的 Future,和调内置 Dart 异步函数一模一样
案例 2:Rust 密码学库封装成 Flutter 插件
假设要在 Flutter 里用 Rust 的 ring 密码学库做 HMAC 签名:
use ring::hmac;
pub fn hmac_sign(key: Vec<u8>, message: Vec<u8>) -> Vec<u8> { let key = hmac::Key::new(hmac::HMAC_SHA256, &key); let sig = hmac::sign(&key, &message); sig.as_ref().to_vec()}Dart 调用:
final signature = await hmacSign( key: Uint8List.fromList(secretKey), message: Uint8List.fromList(payload),);要点:无需手写 ffi.DynamicLibrary.open、无需 Pointer<Uint8> 操作,框架包办了指针生命周期和内存释放。
案例 3:Rust 持有状态 + Stream 推送给 Dart
Rust 端维护一个长连接,把收到的消息流式推给 Dart:
pub struct ChatClient { /* ... */ }
impl ChatClient { pub fn new(server_url: String) -> ChatClient { /* ... */ }
pub async fn subscribe(&self, sink: StreamSink<String>) { // 每收到一条消息就 push 给 Dart // self.recv() 是伪代码,实际替换为你的 WebSocket/channel 接收逻辑 while let Some(msg) = self.recv().await { sink.add(msg); } }}Dart 侧:
final client = await ChatClient.newInstance(serverUrl: 'ws://...');await for (final msg in client.subscribe()) { setState(() => messages.add(msg));}要点:StreamSink<String> 是 flutter_rust_bridge 提供的特殊类型,Rust 往里 push,Dart 侧自动变成 Stream<String>,完美对接 Flutter 响应式 UI。
踩过的坑
-
忘记重新生成绑定:改了 Rust API(加参数、改返回类型)后,必须重跑
flutter_rust_bridge_codegen generate,否则 Dart 旧 binding 和新 Rust ABI 不匹配,报模糊的链接错误或运行时 crash。 -
Rust 初始化时机:必须在
runApp()之前调用await RustLib.init()。漏掉这一步,第一次调 Rust 函数时就会 panic,错误信息是让人困惑的Null check operator used on a null value。 -
lifetime 参数的限制:带
'a生命周期的 Rust 类型(如&'a [u8])无法直接跨 FFI——借用语义在语言边界消失了。解法是改成 owned 类型(Vec<u8>)或用#[frb(opaque)]让 Dart 持不透明句柄。 -
Web 平台的限制:flutter_rust_bridge 在 Web 上通过 WASM 运行,不支持多线程(Wasm 线程尚不稳定),Rust 端的
rayon并行代码在 Web 上会退化为单线程;对性能敏感的 Web 场景需另行评估。
适用 vs 不适用场景
适用:
- Flutter App 需要调用已有的 Rust 库(密码学、音视频解码、嵌入式算法)
- 性能瓶颈在 Dart 侧的计算密集逻辑(图像处理、数据压缩、机器学习推理)
- 需要在 Flutter 里复用 Rust 生态(比 C/C++ FFI 更安全,比 Dart 写更快)
- 已有 Rust 后端逻辑,想在 App 侧复用(shared logic 模式)
不适用:
- 逻辑很简单,Dart 自身足够快——引入 Rust 会增加构建复杂度(需要 Rust 工具链、cargo 依赖管理)
- 需要大量操作 Flutter Widget 树——那本来就是 Dart 的主场
- 团队没有 Rust 经验——flutter_rust_bridge 简化了绑定,但 Rust 本身的学习曲线依然存在
- Web 是主要平台且对多线程性能依赖很高——WASM 线程支持不完整
历史小故事(可跳过)
- 2021 年:fzyzcjy 发布 flutter_rust_bridge v1,目标是”让 Dart 调 Rust 像调 Dart”。v1 限制较多:只支持单 Rust 文件作为输入,不支持 async Rust,类型支持有限。
- 2022 年:成为 Flutter 官方 Favorite 包,第一批 7 个包之一,社区关注度快速增长,星数突破千级。
- 2023-2024 年:v2 大重构,支持整目录输入、async Rust、Rust→Dart 回调、SSE 高速编解码器、trait 对象、lifetimes(实验性)。核心架构从”单文件解析”升级为”完整 AST 遍历 + 插件化代码生成”。
- 现在:5000+ 星,134 位贡献者,被 livekit-flutter、matrix-rust-sdk 等知名项目采用,是 Flutter 生态里 Rust 集成的事实标准。
学到什么
- 胶水代码可以被自动化:FFI 绑定的本质是类型映射 + 内存约定,这是机械性工作,代码生成比手写更可靠——flutter_rust_bridge 证明了这一点
- 语言边界不是性能边界:真正的瓶颈在算法;正确的跨语言方式(批量传数据、减少跨界次数)可以让 Dart+Rust 比纯 Dart 快一个数量级
- 双向调用是关键设计:只支持单向(Dart→Rust)的桥接限制了很多场景;flutter_rust_bridge v2 的
StreamSink把 Rust 推数据的能力还给了 Flutter,解锁了实时流式场景 - 官方生态认可很重要:Flutter Favorite 认证降低了采用者的风险感知,说明工程质量和维护活跃度达到了官方的门槛
延伸阅读
- 官方文档:flutter_rust_bridge 快速上手
- 官方文档:What’s New in V2
- flutter —— Flutter 跨平台 UI 框架,flutter_rust_bridge 的宿主环境
- matrix-rust-sdk —— 用 flutter_rust_bridge 把 Rust Matrix SDK 暴露给 Flutter 的真实案例
- warp —— Rust Web 框架,展示 Rust 生态的表达力——同一套 Rust 代码可同时服务后端和移动端
关联
- flutter —— Flutter 是 flutter_rust_bridge 的调用方,两者共同构成”精美 UI + 高性能计算”架构
- livekit-flutter —— LiveKit Flutter SDK 内部用 flutter_rust_bridge 封装了 Rust 音视频引擎
- matrix-rust-sdk —— Matrix Rust SDK 通过 flutter_rust_bridge 提供官方 Flutter 绑定
- tauri —— Tauri 是 Desktop 版”WebView + Rust”方案,与 flutter_rust_bridge 的”Flutter + Rust”思路同源
- warp —— Warp 展示了 Rust 类型安全 + 高性能的风格,说明为什么把算法写在 Rust 值得
反向链接
(暂无反向链接)