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:

  1. Applied locally immediately - Zero latency, works offline
  2. Stored as a message - For tracking and sync
  3. Synced to the server - When connectivity is available
  4. Conflict-resolved automatically - Using Hybrid Logical Clocks

Why Talon?

Building offline-first apps is hard. You need to handle:

ChallengeTalon's Solution
Storing changes offlineMessages stored locally with sync tracking
Syncing when back onlineAutomatic background sync with batching
Conflict resolutionHybrid Logical Clocks (HLC) for last-write-wins
Real-time updatesStream-based change notifications
Multi-device supportUser + 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