cloud_sync 0.4.0-rc.2
cloud_sync: ^0.4.0-rc.2 copied to clipboard
A Dart library for synchronizing files between local and cloud storage using customizable metadata and file handlers, with progress callbacks and error handling.
CloudSync #
A robust, type-safe synchronization solution for Dart applications
๐ Overview #
CloudSync is a flexible, bidirectional sync engine for Dart that helps keep your local and cloud data perfectly in sync. It supports adapter-based or functional APIs, progress tracking, concurrent operations, and robust cancellation.
๐ Features #
- ๐ Bidirectional Sync โ Sync in both directions (local โ cloud)
- โฑ Conflict Resolution โ Timestamp-based "latest wins" strategy
- ๐ Detailed State Tracking โ 12 sync states for full visibility
- ๐ Adapter or Functional API โ Choose what suits your architecture
- โก Concurrent Processing โ Parallel operations for better performance
- โณ Auto-Sync Support โ Periodic background syncing
- โ Cancelable Syncs โ Graceful cancellation at any stage
- ๐งน Lifecycle Management โ
dispose()cleanup support - ๐ก Error Handling โ Built-in reporting and recovery
๐ฆ Installation #
In your pubspec.yaml:
dependencies:
cloud_sync: ^<latest_version>
Then run:
flutter pub get
๐งญ Quick Start #
Using Adapters #
final cloudSync = CloudSync<FileMetadata, FileData>.fromAdapters(
localAdapter,
cloudAdapter,
);
await cloudSync.sync(
progressCallback: (state) {
if (state is SyncCompleted) {
print('โ
Sync completed!');
} else if (state is SyncError) {
print('โ Sync failed: ${state.error}');
}
},
);
Enable Auto-Sync #
cloudSync.autoSync(
interval: Duration(minutes: 5),
progressCallback: handleSyncProgress,
);
Clean Up #
await cloudSync.dispose();
โ๏ธ Core Architecture #
SyncMetadata Model #
abstract class SyncMetadata {
final String id;
final DateTime modifiedAt;
final bool isDeleted;
}
Sync Flow #
- Metadata Fetching โ Get metadata from both sources
- Diff Detection โ Timestamp-based comparison
- Conflict Resolution โ Apply "latest wins" logic
- Sync Execution โ Upload/download data accordingly
- State Updates โ Progress tracked via
SyncState
๐งฑ Implementation Options #
1. Adapter Pattern (Recommended) #
class LocalHiveAdapter implements SyncAdapter<NoteMetadata, Note> {
@override
Future<List<NoteMetadata>> fetchMetadataList() => metadataBox.values.toList();
@override
Future<Note> fetchDetail(NoteMetadata meta) async => notesBox.get(meta.id)!;
@override
Future<void> save(NoteMetadata meta, Note note) async {
await notesBox.put(meta.id, note);
await metadataBox.put(meta.id, meta);
}
}
2. Functional Injection #
final cloudSync = CloudSync<PhotoMetadata, Photo>(
fetchLocalMetadataList: localDb.getPhotoMetadata,
fetchCloudMetadataList: cloudApi.getPhotoMetadata,
fetchLocalDetail: (m) => localDb.getPhoto(m.id),
fetchCloudDetail: (m) => cloudApi.downloadPhoto(m.id),
saveToLocal: localDb.savePhoto,
saveToCloud: cloudApi.uploadPhoto,
);
๐ถ Sync States #
| State | Description |
|---|---|
| InProgress | Sync already running |
| FetchingLocalMetadata | Fetching local metadata |
| FetchingCloudMetadata | Fetching cloud metadata |
| ScanningLocal | Scanning local for changes |
| ScanningCloud | Scanning cloud for changes |
| SavingToLocal | Downloading to local |
| SavedToLocal | Local save complete |
| SavingToCloud | Uploading to cloud |
| SavedToCloud | Cloud save complete |
| SyncCompleted | All sync steps finished |
| SyncError | An error occurred |
| SyncCancelled | Sync was cancelled |
๐ง Advanced Usage #
Progress Tracking #
void handleSyncState(SyncState state) {
switch (state) {
case SavingToCloud(metadata: final meta):
showUploading(meta.filename);
case SyncError(error: final err):
logError(err.toString());
case SyncCompleted():
showSuccess('Sync complete!');
// handle other states...
}
}
Concurrent Sync #
await cloudSync.sync(
useConcurrentSync: true,
progressCallback: handleSyncState,
);
Auto-Sync Control #
cloudSync.autoSync(
interval: Duration(minutes: 15),
progressCallback: handleSyncState,
);
await cloudSync.stopAutoSync(); // Stop it
Cancel Ongoing Sync #
await cloudSync.cancelSync(); // Triggers SyncCancelled
๐งช Best Practices #
- Always call
dispose()when done - Handle all
SyncStates in your UI for clear feedback - Enable
useConcurrentSyncfor large datasets - Wrap sync in a
try/catchfor reliability
try {
await cloudSync.sync();
} on SyncDisposedError {
// Already disposed
} catch (e) {
handleUnexpectedError(e);
}
๐งพ Example: Metadata Class #
class DocumentMetadata extends SyncMetadata {
final String title;
final int version;
DocumentMetadata({
required super.id,
required super.modifiedAt,
required this.title,
this.version = 1,
super.isDeleted = false,
});
@override
DocumentMetadata copyWith({
String? id,
DateTime? modifiedAt,
String? title,
int? version,
bool? isDeleted,
}) {
return DocumentMetadata(
id: id ?? this.id,
modifiedAt: modifiedAt ?? this.modifiedAt,
title: title ?? this.title,
version: version ?? this.version,
isDeleted: isDeleted ?? this.isDeleted,
);
}
}
๐ License #
MIT License โ See LICENSE for full details.
๐ค Contributing #
Issues and PRs are welcome! Open a discussion or submit a fix anytime.