在移动端跨平台开发领域,Flutter 凭借其优秀的渲染引擎和开发体验独树一帜。然而,当面临密集型计算、底层系统交互或需要复用现有的 C/C++ 库时,单纯依靠 Dart 往往力不从心。
Flutter 虽然支持通过 MethodChannel 与原生平台(Android/iOS)通讯,但在处理跨平台通用业务逻辑时,我们更希望有一种“一次编写,到处运行”且性能强劲的方案。本文将介绍一种 Flutter + Rust + Cubit 的高性能应用开发模式:利用 Rust 统一业务逻辑与底层服务,利用 Cubit 桥接 UI 与数据状态。
官方方案:Flutter + MethodChannel
Flutter 官方提供的标准与原生代码交互的方式:MethodChannel。MethodChannel 是 Flutter 端(Dart)与宿主平台端之间的桥梁。 Dart 端发送一个异步消息调用,平台端接收并处理,然后返回结果。由于 Dart 对象与原生对象内存布局不同,数据传输需要经过序列化(Encode)和反序列化(Decode)。Flutter 使用 StandardMessageCodec 支持 JSON 等常见数据类型的转换。
尽管 MethodChannel 功能强大,但在构建高性能应用时存在问题。同一套业务逻辑(如复杂的加密算法、本地数据库封装),需要分别为 Android 和 iOS 等编写多套代码。频繁的跨语言序列化与反序列化在大数据量传输时会成为性能瓶颈。
Flutter + Rust + Cubit
为了解决上述问题,我们将架构演进为 Flutter + Rust + Cubit。
2.1 核心思想
- Rust:负责核心业务逻辑、网络请求、文件 IO、密集计算。利用 Rust 的内存安全和零成本抽象,实现真正的逻辑跨平台。
- Flutter:只负责 UI 渲染和用户交互,保持轻量级。
- Cubit:作为中间层,负责管理 UI 状态,调用 Rust 接口,并响应 Rust 推送的数据变化。
2.2 架构优势
- 高性能:Rust 编译为机器码,通过 FFI (Foreign Function Interface) 与 Dart 直接内存交互,配合 Rust 的异步模型,可以实现高性能。
- 代码复用:90% 的非 UI 代码只需用 Rust 编写一次,即可在 Android, iOS, Windows, Linux, macOS 上运行。
- 响应式流:结合 Dart 的 Stream 和 Rust 的 Channel,形成完美的数据驱动 UI 模式。
状态管理:为什么选择 Cubit?
在 Flutter 生态中,flutter_bloc 是最流行的状态管理库。它包含两种模式:Bloc 和 Cubit。
Bloc vs Cubit
-
Bloc (Business Logic Component):
- 基于 Events (事件) 驱动。
- 流程:UI 添加 Event -> Bloc 处理 Event -> Bloc 发送 State 变更。
- 优点:可追溯性强,适合极其复杂的逻辑流。
- 缺点:样板代码多(需定义各种 Event 类),相对繁琐。
-
Cubit:
- 基于 Functions (函数) 驱动。
- 流程:UI 调用 Cubit 方法 -> Cubit 执行逻辑 -> Cubit 发送 State 变更。
- 优点:代码简洁,直观,易于理解。
在 Flutter + Rust 模式下,Dart 端的逻辑主要是“转发”:UI 触发操作,Dart 转发给 Rust。Cubit 的函数式调用可以完美契合 FFI 的调用方式(Dart 直接调用 Rust 导出的 C ABI 函数)。我们不需要复杂的 Event 定义,因为真正的复杂逻辑已经被下沉到了 Rust 层。
实现:FFI 与 Isolate 通讯机制
这是本模式的核心实现细节。UI 通过 Cubit 触发 Rust 操作,Rust 通过 Isolate Port 机制异步通知 UI 更新。
- 初始化:Dart 在初始化 Rust 服务时,通过 FFI 传入一个
SendPort.nativePort(int64)。Rust 端将其转换为Dart_Port并保存。 - UI 交互 (Dart -> Rust):Cubit 调用 Rust 导出的 FFI 函数。这些函数通常是非阻塞的,或者在 Rust 内部的线程池中运行,仅返回一个简短的 Result(如 “RequestAccepted”)。
- 状态更新 (Rust -> Dart):Rust 业务完成后(或有主动推送时),通过保存的 Port 发送序列化后的数据。
- UI 渲染 (Dart Stream -> UI):Dart 端的
ReceivePort监听到消息,转换为应用状态,Cubitemit新状态,UI 重绘。
与 MethodChannel 类似,状态更新也需要一次 Rust 序列化,Dart 反序列化的过程,然而与 MethodChannel 不同的是我们可以根据 State 结构选择更高性能的序列化方式,如 Flatbuffers,Protobuf,Simple SErialization(Flutter Rust Bridge)。更重要的是UI状态更新是一个相对低频的过程,因为就算业务逻辑与状态在高频变化,我们也可以在 Rust 端控制 State 更新频率在 120 fps 以下。