Introduction
Talon - A lightweight offline-first sync layer for Flutter
Talon
A lightweight, dependency-free sync layer for building offline-first Flutter apps.
Talon handles the hard parts of offline-first: local persistence, sync, and conflict resolution. You focus on building features.
What is Talon?
Talon is a thin sync layer that handles the complexity of offline-first synchronization. Every change you make is:
- Applied locally immediately - Zero latency, works offline
- Stored as a message - For tracking and sync
- Synced to the server - When connectivity is available
- Conflict-resolved automatically - Using Hybrid Logical Clocks
Why Talon?
Building offline-first apps is hard. You need to handle:
| Challenge | Talon's Solution |
|---|---|
| Storing changes offline | Messages stored locally with sync tracking |
| Syncing when back online | Automatic background sync with batching |
| Conflict resolution | Hybrid Logical Clocks (HLC) for last-write-wins |
| Real-time updates | Stream-based change notifications |
| Multi-device support | User + Client ID based message routing |
Quick Example
// Create a Talon instance
final talon = Talon(
userId: 'user-123',
clientId: 'device-456',
serverDatabase: myServerDb,
offlineDatabase: myOfflineDb,
createNewIdFunction: () => uuid.v4(),
);
// Enable sync
talon.syncIsEnabled = true;
// Save a change - applied locally, synced automatically
await talon.saveChange(
table: 'todos',
row: 'todo-1',
column: 'name',
value: 'Buy milk', // Accepts any type
);
// Listen for changes (local and server)
talon.changes.listen((change) {
if (change.affectsTable('todos')) {
refreshTodoList();
}
});
Philosophy
Talon is intentionally a thin sync layer, not an ORM.
Talon handles:
- Message creation and storage
- Sync orchestration
- Conflict resolution
- Change notifications
You handle:
- Database schema design
- Query building
- Data access patterns
- UI updates
This gives you full control and understanding of your data layer. No magic, no hidden complexity.
How It Works
Every change becomes a Message:
Message(
table: 'todos', // Which table
row: 'todo-123', // Which row
column: 'name', // Which field
value: 'Buy milk', // New value
localTimestamp: '...', // HLC timestamp for conflict resolution
userId: 'user-123', // Who made the change
clientId: 'device-456', // Which device
hasBeenSynced: false, // Sync status
)
Messages are stored locally, applied to your data tables, then synced to the server. When messages arrive from the server, they're compared using HLC timestamps - the newest wins.
Backend Agnostic
Talon works with any backend:
- Supabase - Real-time PostgreSQL
- Firebase - Firestore or Realtime Database
- Custom REST API - Any HTTP endpoint
- GraphQL - With subscriptions
You implement two interfaces: OfflineDatabase and ServerDatabase.
Database Agnostic
Talon works with any local database:
- sqflite - SQLite for Flutter
- Drift - Type-safe SQLite
- Hive - NoSQL boxes
- Isar - High-performance NoSQL
Next Steps
- Installation - Add Talon to your project
- Quick Start - Build your first offline-first feature
- How It Works - Understand the architecture
- Supabase Example - Complete Supabase implementation