OfflineSync
OfflineSync is a Flutter package that provides offline-first data management and synchronization. It ensures smooth functionality even without an internet connection and syncs data once connectivity is restored.
Features
- Local data storage and retrieval
- Automatic synchronization with server when online
- Conflict resolution
- Encryption of sensitive data
- Batch syncing for improved performance
- Error handling and retry mechanisms
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
offline_sync: ^1.0.0
Usage
Initialization
First, initialize the OfflineSync instance in your app:
import 'package:offline_sync/offline_sync.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final offlineSync = OfflineSync(
config: OfflineSyncConfig(
apiEndpoint: 'https://your-custom-api.com',
// encryptionKey is optional and will be generated automatically if not provided
),
);
await offlineSync.initialize();
runApp(MyApp());
}
Setting Custom API Endpoint
Set a custom endpoint for your specific server:
final offlineSync = OfflineSync();
offlineSync.setApiEndpoint('https://your-custom-api.com');
Saving Data
To save data locally and queue it for syncing:
final offlineSync = OfflineSync();
await offlineSync.saveLocalData('user_1', {
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
});
Reading Data
To read locally stored data:
final userData = await offlineSync.readLocalData('user_1');
if (userData != null) {
print('User name: ${userData['name']}');
} else {
print('User not found');
}
Syncing with Server
The package automatically syncs data when an internet connection is available. However, you can manually trigger a sync:
try {
await offlineSync.updateFromServer();
print('Data updated from server successfully');
} catch (e) {
print('Failed to update from server: $e');
}
Handling Authentication
Set the authentication token for API requests:
await offlineSync.setAuthToken('your_auth_token_here');
Advanced Usage:
Conflict Resolution
The package includes basic conflict resolution. You can customize this by extending the OfflineSync class:
class CustomOfflineSync extends OfflineSync {
@override
Future<Map<String, dynamic>> resolveConflict(
String id,
Map<String, dynamic> localData,
Map<String, dynamic> serverData
) async {
// Implement your custom conflict resolution strategy here
// This example prefers local changes
final resolvedData = Map<String, dynamic>.from(serverData);
localData.forEach((key, value) {
if (value != serverData[key]) {
resolvedData[key] = value;
}
});
return resolvedData;
}
}
Batch Processing
The package processes sync queue in batches. You can adjust the batch size:
class CustomOfflineSync extends OfflineSync {
@override
int get batchSize => 100; // Default is 50
}
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.
Using with Flutter Provider
Wrap your app with the OfflineSyncProvider
to make the sync instance and its status available throughout the widget tree:
final offlineSync = OfflineSync(config: OfflineSyncConfig(
apiEndpoint: 'https://your-api.com',
// encryptionKey is now optional
));
await offlineSync.initialize();
runApp(
OfflineSyncProvider(
offlineSync: offlineSync,
child: MyApp(),
),
);
Then, anywhere in your widget tree, you can access the sync instance and status:
final offlineSync = OfflineSyncProvider.of(context);
ValueListenableBuilder<SyncStatus>(
valueListenable: offlineSync.syncStatus,
builder: (context, status, _) {
// Show sync status in UI
return Text('Sync status: $status');
},
)
API Reference
OfflineSyncConfig
apiEndpoint
(String, required): The server endpoint for syncing.batchSize
(int, default: 50): Number of items to sync per batch.encryptionKey
(String, optional): 32-character key for AES encryption. If not provided, a secure key is generated and stored automatically.encryptionIV
(IV, optional): Custom IV for encryption.conflictResolver
(callback, optional): Custom function for resolving data conflicts.logger
(callback, optional): Function for debug or error logging.
OfflineSync
OfflineSync({required OfflineSyncConfig config, ...})
: Create a new instance with config and optional injected dependencies.Future<void> initialize()
: Initialize the database and connectivity.Future<void> setAuthToken(String token)
: Set the auth token for API requests.Future<void> saveLocalData(String id, Map<String, dynamic> data)
: Save data locally and queue for sync.Future<Map<String, dynamic>?> readLocalData(String id)
: Read local data by ID.Future<void> updateFromServer()
: Manually trigger sync from server.ValueNotifier<SyncStatus> syncStatus
: Listen for sync status changes.ValueNotifier<SyncErrorType?> lastError
: Listen for error changes.ValueNotifier<double> syncProgress
: Listen for sync progress (0.0 to 1.0).
Migration Guide
From Singleton to Instance API
- Before:
final offlineSync = OfflineSync(); await offlineSync.initialize(); offlineSync.setApiEndpoint('https://api.com');
- After:
final offlineSync = OfflineSync( config: OfflineSyncConfig( apiEndpoint: 'https://api.com', // encryptionKey is now optional ), ); await offlineSync.initialize();
- Use the new
OfflineSyncProvider
to expose the instance to your widget tree. - Set all configuration options via
OfflineSyncConfig
instead of setters.