Flutter Data Sync Manager
A Flutter package for seamless offline-first data synchronization between local and remote storage.
Features
- 🔄 Two-way sync (local ↔ remote)
- ⚡ Conflict resolution strategies (lastWriteWins, manual, merge, localWins, remoteWins)
- 🔁 Background sync with retry on network restore
- 🗄️ Multiple storage adapters (Hive, SQLite, SharedPreferences, and more)
- ⚙️ Configurable sync behavior
- 🚀 Easy to use with simple API
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
flutter_data_sync_manager: ^1.0.1
Quick Start
import 'package:flutter_data_sync_manager/flutter_data_sync_manager.dart';
// Create sync manager
final sync = SyncManager(
localDb: HiveAdapter(),
remoteApi: FirestoreAdapter(), // Your custom adapter
);
// Initialize and sync
await sync.initialize();
await sync.sync(); // Automatically merges changes, handles conflicts
Usage
Basic Setup
import 'package:flutter_data_sync_manager/flutter_data_sync_manager.dart';
class MyApp {
late SyncManager syncManager;
Future<void> initialize() async {
syncManager = SyncManager(
localDb: HiveAdapter(),
remoteApi: MyCustomRemoteAdapter(),
config: SyncConfig.offlineFirst(),
);
await syncManager.initialize();
}
Future<void> syncData() async {
final result = await syncManager.syncWithRetry();
if (result.success) {
print('Sync successful: ${result.localToRemoteCount} local→remote, '
'${result.remoteToLocalCount} remote→local, '
'${result.conflictCount} conflicts');
} else {
print('Sync failed: ${result.error}');
}
}
}
Configuration Options
final config = SyncConfig(
conflictResolution: ConflictResolutionStrategy.lastWriteWins,
maxRetries: 3,
retryDelay: Duration(seconds: 2),
backgroundSync: true,
backgroundSyncInterval: Duration(minutes: 5),
syncOnStart: true,
syncOnNetworkRestore: true,
batchSize: 100,
operationTimeout: Duration(seconds: 30),
);
// Or use predefined configurations
final offlineFirstConfig = SyncConfig.offlineFirst();
final realTimeConfig = SyncConfig.realTime();
Conflict Resolution Strategies
Last Write Wins (Default)
final config = SyncConfig(
conflictResolution: ConflictResolutionStrategy.lastWriteWins,
);
Manual Resolution
final strategy = ManualResolutionStrategy(
onConflict: (conflict) async {
// Show UI to user for manual resolution
final resolvedData = await showConflictResolutionDialog(conflict);
return ConflictResolutionResult.success(resolvedData);
},
);
Merge Strategy
final config = SyncConfig(
conflictResolution: ConflictResolutionStrategy.merge,
);
Local/Remote Wins
final config = SyncConfig(
conflictResolution: ConflictResolutionStrategy.localWins, // or remoteWins
);
Custom Adapters
Local Adapter
class MyLocalAdapter implements LocalAdapter {
@override
Future<void> initialize() async {
// Initialize your local storage
}
@override
Future<Map<String, Map<String, dynamic>>> getAll() async {
// Return all items from local storage
}
@override
Future<void> save(String key, Map<String, dynamic> data) async {
// Save item to local storage
}
// Add remaining methods as needed
}
Remote Adapter
class MyRemoteAdapter implements RemoteAdapter {
@override
Future<void> initialize() async {
// Initialize connection to remote storage
}
@override
Future<Map<String, Map<String, dynamic>>> getAll() async {
// Return all items from remote storage
}
@override
Future<bool> isAvailable() async {
// Check network connectivity
return true;
}
// Add remaining methods as needed
}
Background Sync
// Start background sync
syncManager.startBackgroundSync();
// Stop background sync
syncManager.stopBackgroundSync();
Sync Specific Items
final result = await syncManager.syncItems(['item1', 'item2', 'item3']);
Built-in Adapters
Hive Adapter
final localDb = HiveAdapter(boxName: 'my_app_data');
SQLite Adapter
final localDb = SqliteAdapter(databaseName: 'my_app.db');
SharedPreferences Adapter
final localDb = SharedPrefsAdapter(prefix: 'my_app_');
Advanced Usage
Custom Conflict Resolution
class CustomConflictStrategy extends ConflictResolutionStrategy {
@override
String get name => 'custom';
@override
Future<ConflictResolutionResult> resolve(Conflict conflict) async {
// Handle the conflict resolution result
final resolvedData = {
...conflict.localData,
'merged_at': DateTime.now().toIso8601String(),
};
return ConflictResolutionResult.success(resolvedData);
}
}
Error Handling
try {
final result = await syncManager.sync();
if (!result.success) {
// Handle sync failure
print('Sync failed: ${result.error}');
}
} catch (e) {
// Handle unexpected errors
print('Unexpected error: $e');
}
Example App
Check out the example/
directory for a complete Flutter app demonstrating the package usage.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
See CHANGELOG.md for a list of changes.
Issues
If you find any issues or have feature requests, please file them at GitHub Issues.