flutter_universal_sync_core
Core contracts for the flutter_universal_sync offline-first sync package family. Pure Dart — no Flutter SDK dependency.
Status: 0.1.0 — contracts may evolve as adapter and engine packages are built. Pin exactly in your pubspec.yaml until 1.0.0.
Install
dependencies:
flutter_universal_sync_core: ^0.1.0
You won't usually depend on _core directly — depend on an adapter (for example flutter_universal_sync_sqflite) which re-exports these types.
What's here
Pure contracts, no execution. Every downstream adapter and the sync engine depend on these types.
| Type | Role |
|---|---|
SyncEntity |
Abstract base class — every synced domain model extends it. Carries id, createdAt, updatedAt, deletedAt, isSynced, syncStatus. |
SyncOperation |
insert / update / delete |
SyncStatus |
pending / syncing / synced / failed |
SyncQueueEntry |
One pending local mutation awaiting remote push. |
SyncColumns |
Canonical sync column names every synced table must declare. |
LocalDatabaseAdapter |
Port implemented by sqflite / drift / hive / objectbox adapter packages. |
RemoteSyncAdapter |
Port implemented by firebase / supabase / appwrite / graphql / rest adapter packages. |
ConflictResolver + LastWriteWinsResolver / ServerPriorityResolver / ClientPriorityResolver |
Strategies for reconciling concurrent row versions. |
SyncException + SchemaValidationException / SyncPushException / SyncPullException / ConflictResolutionException |
Exception hierarchy. |
IdGenerator + UuidV4Generator |
Swappable id factory (UUIDv4 by default). |
Family topology
flutter_universal_sync_core ← you are here (contracts only)
├── flutter_universal_sync_engine ← sync engine (drains the queue)
├── flutter_universal_sync_background ← WorkManager / isolates
├── flutter_universal_sync_sqflite ← LocalDatabaseAdapter: sqflite
├── flutter_universal_sync_drift ← LocalDatabaseAdapter: drift
├── flutter_universal_sync_hive ← LocalDatabaseAdapter: hive
├── flutter_universal_sync_objectbox ← LocalDatabaseAdapter: objectbox
├── flutter_universal_sync_firebase ← RemoteSyncAdapter: firebase
├── flutter_universal_sync_supabase ← RemoteSyncAdapter: supabase
├── flutter_universal_sync_appwrite ← RemoteSyncAdapter: appwrite
├── flutter_universal_sync_graphql ← RemoteSyncAdapter: graphql
├── flutter_universal_sync_rest ← RemoteSyncAdapter: rest
└── flutter_universal_sync_bloc ← BLoC/Cubit helpers, repository base
Known v1 limitations
These trade-offs are deliberate for 0.1.0. Each will either be addressed in a later package or stay as documented caveats.
- Wall-clock conflicts are skew-sensitive. Last-Write-Wins compares device
updated_at. A device with a wrong clock "wins" incorrectly. - Local DB grows unbounded. Soft-deleted rows are never hard-removed locally. Garbage collection is a future enhancement.
- Schema typos are runtime, not compile-time.
LocalDatabaseAdapter.validateSchemacatches them at init. - No multi-row atomicity across the sync boundary. Queue is per-op; aggregate roots (order + line items) can partially sync.
- One failing push wedges the queue. Stop-on-first-failure — one bad op blocks every op behind it until resolved. Dead-lettering is a future sync-engine concern.
ConflictResolverhas no context. Resolver sees two row maps; no table/operation metadata and no abort signal.- No aggregate-root FK ordering guarantees. Consequence of (4).
- Backends must accept client-supplied UUID PKs.
SERIALPKs are unsupported.
Dependencies note
package:test is declared as a runtime dependency, not a dev dependency. This is intentional: the shared LocalDatabaseAdapterContract test suite ships from lib/testing.dart so adapter packages can run it against their own implementations. Putting the suite under lib/ requires test at runtime.
If you depend on flutter_universal_sync_core from a production package, test will appear in your transitive dependency graph but is only loaded if you import package:flutter_universal_sync_core/testing.dart. The cost is one extra resolved package; nothing is loaded at runtime unless you opt in.
A future major release may extract the contract suite into a sibling package (flutter_universal_sync_core_testing) following the drift/drift_dev model.
Implementing LocalDatabaseAdapter
Use the shared contract test suite:
// test/my_adapter_test.dart
import 'package:flutter_universal_sync_core/testing.dart';
void main() {
runLocalDatabaseAdapterContract(
factory: MyAdapter.new,
adapterName: 'MyAdapter',
createTestTable: (a) async { /* create a `things` table with the sync columns + `name` */ },
createBrokenTable: (a) async { /* create a `broken` table missing 4 sync columns */ },
);
}
Passing the suite means your adapter conforms to the contract.
License
MIT — see LICENSE.
Libraries
- flutter_universal_sync_core
- Core contracts for the flutter_universal_sync offline-first package family.
- testing
- Test utilities for consumers of
flutter_universal_sync_core.