在移动端跨平台开发领域,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 是最流行的状态管理库。它包含两种模式:BlocCubit

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 更新。

  1. 初始化:Dart 在初始化 Rust 服务时,通过 FFI 传入一个 SendPort.nativePort (int64)。Rust 端将其转换为 Dart_Port 并保存。
  2. UI 交互 (Dart -> Rust):Cubit 调用 Rust 导出的 FFI 函数。这些函数通常是非阻塞的,或者在 Rust 内部的线程池中运行,仅返回一个简短的 Result(如 “RequestAccepted”)。
  3. 状态更新 (Rust -> Dart):Rust 业务完成后(或有主动推送时),通过保存的 Port 发送序列化后的数据。
  4. UI 渲染 (Dart Stream -> UI):Dart 端的 ReceivePort 监听到消息,转换为应用状态,Cubit emit 新状态,UI 重绘。

与 MethodChannel 类似,状态更新也需要一次 Rust 序列化,Dart 反序列化的过程,然而与 MethodChannel 不同的是我们可以根据 State 结构选择更高性能的序列化方式,如 Flatbuffers,Protobuf,Simple SErialization(Flutter Rust Bridge)。更重要的是UI状态更新是一个相对低频的过程,因为就算业务逻辑与状态在高频变化,我们也可以在 Rust 端控制 State 更新频率在 120 fps 以下。