Kalam Sync
kalam_sync keeps a local Drift cache so Flutter UI works offline, then syncs live SQL through kalam_link.
Need a server first? kalam init or the server quick start.
Fastest path
That writes a Flutter starter, schema.sql, and KalamTables.*. Or add the package yourself:
dependencies:kalam_sync: ^0.6.0-rc.0Open one account-scoped cache
subject is required. The server URL, namespace, and authenticated subject form the local database identity, so one user’s cached rows and queued actions cannot be opened or flushed as another user.
Production apps should pass the signed-in user id and return Auth.jwt(...) from authProvider.
Kalam.open() initializes the Rust runtime and opens SQLite immediately. The WebSocket stays lazy until the first consumer or queued action needs it. KalamScope pauses and resumes sync with the Flutter app lifecycle.
Bind a table and subscribe
kalam schema gen --languages dart writes row classes and KalamTables.* specs from schema.sql. Bind that spec — do not invent a second model:
insert, update, and delete change the local row and enqueue one generic Kalam DML action in the same SQLite transaction. todos.watch() is always local and keeps working offline.
Live SQL must stay SELECT ... FROM ... WHERE .... Do not put ORDER BY or LIMIT in the consumer SQL.
Watch rows in the UI
watch() returns decoded rows only. Use watchWithSyncState() when the UI needs pending, retry, failure, or awaiting-server-echo state beside each row.
That’s enough for a first Flutter list. The rest of this page is optional.
Subscribe inside a widget
Start only the query the screen needs, and cancel it when the widget unloads:
Use KalamScope.read(context) in initState. KalamScope.of(context) rebuilds when the session changes.
consumer() accepts the same params list as query():
Later: catch-up, table modes, durability
Catch up in a headless isolate
The same live query can drain a bounded backlog, then disconnect:
Catch-up resumes from: the SQLite-committed checkpoint and uses batchSize as the row limit.
Table modes
| Mode | Local writes | Typical use |
|---|---|---|
KalamSyncMode.bidirectional | insert / update / delete queue generic DML | Todos, settings, conversations |
KalamSyncMode.replicaOnly | Direct DML throws; enqueue a custom action | Messages, workflows the backend owns |
Stream tables generate as replicaOnly. User and shared tables generate as bidirectional. Override with spec.copyWith(mode: ...) when the backend must stay authoritative.
Custom actions, generated queues, and named steps are in Offline Actions.
What stays durable
- Local mirror row + outbox enqueue are one Drift transaction.
- Applied server row + sequence checkpoint are one Drift transaction.
- Server deliveries are acknowledged only after that transaction commits.
- Duplicate or older events are ignored.
- Actions, retry metadata, optimistic rows, and checkpoints survive process restart.
pause()/resume()cancel and reopen subscriptions from the SQLite-committed checkpoint.
kalam_sync uses kalam_link.liveEventsWithAck. Existing liveEvents() callers keep automatic progress; sync subscriptions resume from the committed cursor after reconnect or process restart.