isar_sync 0.1.0-dev.3 copy "isar_sync: ^0.1.0-dev.3" to clipboard
isar_sync: ^0.1.0-dev.3 copied to clipboard

Offline-first, event-sourced sync for Isar apps: capture local changes and converge them across devices through pluggable cloud adapters (Drive, iCloud).

isar_sync #

Offline-first synchronization framework for Isar-based apps.

Isar stays your local source of truth. isar_sync records every change as an event, ships those events through a pluggable remote adapter (Google Drive, iCloud, or your own), and applies remote events back into your store — so the same data converges across a user's devices, with no server to run.

Install #

dependencies:
  isar_sync: ^0.1.0-dev.3

or

dart pub add isar_sync

Quick Start #

import 'package:isar_sync/isar_sync.dart';

const deviceId = 'device-A'; // stable per install

final queue = InMemorySyncQueue();
final store = InMemorySyncStore();
final cursorStore = InMemorySyncCursorStore();

final applyEngine = DefaultApplyEngine(
  store: store,
  conflictResolver: const LastWriteWinsConflictResolver(),
  // Drop this device's own events echoed back from shared cloud storage.
  // REQUIRED for the Google Drive / iCloud adapters.
  localDeviceId: deviceId,
);

// Replace with a real adapter: GoogleDriveSyncAdapter or ICloudSyncAdapter.
final adapter = MySyncAdapter();

final core = SyncCore(
  queue: queue,
  adapter: adapter,
  applyEngine: applyEngine,
  cursorStore: cursorStore,
);

// Capture a local change...
final capture = QueueBackedChangeCapture(queue: queue, deviceId: deviceId);
await capture.capture(
  SyncMutation(
    collection: 'notes',
    entityId: 'n1',
    operation: SyncOperation.upsert,
    payload: {
      'title': 'hello',
      // A UTC updatedAt is required for correct Last-Write-Wins.
      'updatedAt': DateTime.now().toUtc().toIso8601String(),
    },
  ),
);

// ...then run one push + pull + apply cycle.
final report = await core.syncOnce();
print('pushed=${report.pushedCount}, pulled=${report.pulledCount}');

Two conventions are load-bearing: every upsert payload must carry a UTC updatedAt, and DefaultApplyEngine.localDeviceId must be set (and equal to the capture deviceId) for shared-folder cloud adapters. See doc/usage.md §10.

Flutter companion #

For ready-made platform integrations (Google Sign-In, icloud_storage), use the companion package:

dependencies:
  isar_sync: ^0.1.0-dev.3
  isar_sync_flutter: ^0.1.0-dev.3

Adapter & integration guides #

Sign in on another device with the same Google account or Apple ID and use the same namespace, and the two devices share events through that remote store.

Why #

Isar is an excellent local database, but as an app grows the gaps show:

  • no multi-device sync,
  • no change log,
  • collaboration/sharing is hard to build.

Teams usually end up adding a backend (more complexity), hand-rolling sync (low reuse, high maintenance), or running servers they didn't want. The core problem:

Local-first is great, but the next step is missing.

isar_sync does not replace Isar. It keeps Isar as-is and adds sync on top: start with no server, extend only when needed, and change as little existing code as possible.

Conceptual model #

  • State vs Change — Isar holds the current state; isar_sync records the changes. That separation is what makes debugging, reprocessing, and re-syncing possible.
  • Event-driven — every mutation is captured as an event: state change → event → sync.
  • Eventually consistent — the goal is convergence, not instantaneous agreement.

End-to-end flow #

  1. A local change happens (user edits data).
  2. Change capture — the Isar write and the SyncEvent are created in one transaction boundary, so data and event are atomic.
  3. The event is queued in a SyncQueue (not yet on the remote).
  4. PushSyncQueue → adapter → remote storage.
  5. Pull — remote storage → adapter → incoming events.
  6. Events are de-duplicated and ordered by timestamp.
  7. Apply — events are applied to the store (upsert/delete), with conflicts resolved by the ConflictResolver.
  8. UI updates via Isar's watch.
flowchart LR
  U[User action] --> W[Isar write]
  W --> C[Change capture]
  C --> E[SyncEvent]
  E --> Q[SyncQueue]
  Q --> P[Push]
  P --> A1[Adapter]
  A1 --> R[(Remote storage)]

  R --> A2[Adapter]
  A2 --> PL[Pull]
  PL --> F[Dedup / order]
  F --> AP[Apply engine]
  AP --> I[Isar update]
  I --> UI[UI refresh]

Components #

Component Responsibility
SyncCore Orchestrates one push → pull → apply cycle (syncOnce())
QueueBackedChangeCapture Turns local mutations into queued SyncEvents
SyncQueue Outbound queue with retry/backoff (InMemorySyncQueue, FileSyncQueue)
SyncAdapter Abstracts the remote store (GoogleDriveSyncAdapter, ICloudSyncAdapter)
ApplyEngine Applies remote events to local state
ConflictResolver Conflict policy (default LastWriteWinsConflictResolver)
SyncCursorStore Persists the per-adapter pull cursor (FileSyncCursorStore)

Design principles #

  • Simplicity first — an understandable pipeline over complex CRDTs.
  • Predictability — explicit overwrite rules and apply ordering.
  • Debuggability — an event log you can trace and reprocess.
  • Incremental — v1 is simple sync; advanced policies/optimizations come later.

Scope #

In v1: the event pipeline (capture → queue → push → pull → apply), extensible ports (SyncAdapter, SyncStore, ConflictResolver, SyncCursorStore), default Last-Write-Wins resolution, and explicit syncOnce() execution.

Not in v1: fully automatic real-time sync, CRDT-based concurrent editing, global total ordering, and backend-standardized auth/permissions.

Status #

Early preview (0.1.0-dev). The API is still evolving.

Docs #

Publishing guide: doc/publish-to-pub.md · Version history: CHANGELOG.md

0
likes
160
points
12
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Offline-first, event-sourced sync for Isar apps: capture local changes and converge them across devices through pluggable cloud adapters (Drive, iCloud).

Repository (GitHub)
View/report issues
Contributing

Topics

#isar #flutter #sync #offline-first #drive

License

MIT (license)

Dependencies

googleapis, http, meta, path

More

Packages that depend on isar_sync