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.
Running Codegen
Section titled “Running Codegen”cd packages/modalityflutter_rust_bridge_codegen generateAlways run this from the packages/modality/ directory. The command reads flutter_rust_bridge.yaml for configuration.
When to Run
Section titled “When to Run”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.
Generated Files
Section titled “Generated Files”Codegen regenerates the following files:
| File | Location | Purpose |
|---|---|---|
frb_generated.rs | crates/api/src/ | Rust FFI bridge implementation |
frb_generated.dart | packages/modality/lib/src/rust/ | Dart FFI bindings |
frb_generated.io.dart | packages/modality/lib/src/rust/ | Native platform FFI |
frb_generated.web.dart | packages/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.
Threading Model
Section titled “Threading Model”FRB provides two execution modes that control where Rust code runs relative to the Dart isolate:
| Dart Side | Rust Side | Behavior |
|---|---|---|
| Async (default) | Sync fn | Dart non-blocking, Rust runs in thread pool |
Sync #[frb(sync)] | Sync fn | Dart blocking, Rust runs on main thread |
Async (default)
Section titled “Async (default)”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 neededpub fn compile_whiteboard(session_id: String) -> Vec<WhiteboardElement> { // runs on thread pool}// Dart -- returns Futurefinal 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.
Choosing the right mode
Section titled “Choosing the right mode”| Operation | Mode | Reason |
|---|---|---|
new() / constructor | Sync | O(1), no I/O |
| Field getter | Sync | O(1) read |
dispatch() | Async | Triggers reduce + recompile |
open_*_session() | Async | Loads from backend, creates session |
close_session() | Async | Persists state |
len(), is_empty() | Sync | O(1) |
all_schemas() | Async | May clone large collections |
Related
Section titled “Related”FRB Patterns— annotation decisions and impl block splittingFRB API Layer— the thin open/dispatch/close surface