Skip to content

FRB Codegen

Flutter Rust Bridge (FRB) codegen regenerates all Dart bindings from the Rust API surface. It is a deterministic, safe process that should be run after every change to the public Rust API.

Terminal window
cd packages/modality
flutter_rust_bridge_codegen generate

Always run this from the packages/modality/ directory. The command reads flutter_rust_bridge.yaml for configuration.

Run codegen after any change to the public Rust API surface:

  • Renaming functions or types
  • Changing function signatures (parameters, return types)
  • Adding or removing FRB-exposed types
  • Changing FRB annotations (non_opaque, opaque, sync, ignore)
  • Adding or removing enum variants on exposed types

Codegen is safe and idempotent. When in doubt, run it.

Codegen regenerates the following files:

FileLocationPurpose
frb_generated.rscrates/api/src/Rust FFI bridge implementation
frb_generated.dartpackages/modality/lib/src/rust/Dart FFI bindings
frb_generated.io.dartpackages/modality/lib/src/rust/Native platform FFI
frb_generated.web.dartpackages/modality/lib/src/rust/WASM platform FFI
third_party/packages/modality/lib/src/rust/Bindings for types from transitively scanned crates

All of these files are fully managed by codegen. Do not edit them manually.

FRB provides two execution modes that control where Rust code runs relative to the Dart isolate:

Dart SideRust SideBehavior
Async (default)Sync fnDart non-blocking, Rust runs in thread pool
Sync #[frb(sync)]Sync fnDart blocking, Rust runs on main thread

The default mode. Dart gets a Future and the Rust function executes on FRB’s thread pool. Dart’s UI thread is never blocked.

// Rust -- no annotation needed
pub fn compile_whiteboard(session_id: String) -> Vec<WhiteboardElement> {
// runs on thread pool
}
// Dart -- returns Future
final elements = await compileWhiteboard(sessionId: id);

Use async for anything O(log n) or higher: compilation, persistence, network calls, bulk operations.

Marked with #[frb(sync)] (or /// flutter_rust_bridge:sync outside crates/api). Dart calls the function synchronously and it executes on the main thread. The Dart isolate blocks until the Rust function returns.

#[frb(sync)]
pub fn get_session_version(session_id: String) -> u64 {
// runs on main thread, returns immediately
}
// Dart -- returns value directly (no Future)
final version = getSessionVersion(sessionId: id);

Use sync only for O(1) operations: getters, constructors, simple field access.

OperationModeReason
new() / constructorSyncO(1), no I/O
Field getterSyncO(1) read
dispatch()AsyncTriggers reduce + recompile
open_*_session()AsyncLoads from backend, creates session
close_session()AsyncPersists state
len(), is_empty()SyncO(1)
all_schemas()AsyncMay clone large collections