Website · Documentation · Report Bug
PocketSync enables seamless data synchronization across devices without managing your own backend infrastructure. It works with SQLite databases and handles all the complexities of data synchronization for you.
Note: PocketSync is currently in alpha. The system is under active development and should not be considered reliable for production use. We are still yet to test it in production like conditions.
- Offline-first architecture: Continue working with your data even when offline
- Automatic synchronization: Changes are automatically synchronized when connectivity is restored
- Conflict resolution: Multiple built-in strategies for handling conflicts
- Optimized change tracking: Efficiently tracks and batches changes to minimize network usage
- SQLite integration: Built on top of SQLite for reliable local data storage
- Customizable: Flexible configuration options to suit your specific needs
dependencies:
pocketsync_flutter: latest
Then run:
flutter pub get
You'll need to create a PocketSync project in the PocketSync dashboard and get your project ID, auth token, and server URL.
import 'package:pocketsync_flutter/pocketsync_flutter.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
Future<void> initPocketSync() async {
// Get the database path
String dbPath = join(await getDatabasesPath(), 'my_app_database.db');
// Initialize PocketSync
await PocketSync.initialize(
options: PocketSyncOptions(
projectId: 'YOUR_PROJECT_ID',
authToken: 'YOUR_AUTH_TOKEN',
serverUrl: 'https://api.pocketsync.dev',
// Optional configurations
conflictResolutionStrategy: ConflictResolutionStrategy.lastWriteWins,
verbose: true,
),
databaseOptions: DatabaseOptions(
dbPath: dbPath,
version: 1,
schema: DatabaseSchema(
tables: [
TableSchema(
name: 'todos',
columns: [
TableColumn.primaryKey(name: 'id', type: ColumnType.INTEGER),
TableColumn.text(name: 'title'),
TableColumn.boolean(name: 'isCompleted'),
],
),
],
),
),
);
// Authenticate
PocketSync.instance.setUserId('my-user-id');
// Start the sync engine
await PocketSync.instance.start();
}
// Get a reference to the database
final db = PocketSync.instance.database;
// Insert data
await db.insert('todos', {
'title': 'Buy groceries',
'isCompleted': 0,
});
// Query data
List<Map<String, dynamic>> todos = await db.query('todos');
// Update data
await db.update(
'todos',
{'isCompleted': 1},
where: 'id = ?',
whereArgs: [1],
);
// Delete data
await db.delete('todos', where: 'id = ?', whereArgs: [1]);
// Watch database changes
final stream = db.watch('SELECT * FROM todos');
stream.listen((event) {
print(event);
});
Read more: PocketSync Database
// Manually trigger sync
await PocketSync.instance.scheduleSync();
// Pause synchronization
await PocketSync.instance.stop();
// Resume synchronization
await PocketSync.instance.start();
PocketSync provides several strategies for resolving conflicts:
- Last Write Wins: The most recent change based on timestamp wins (default)
- Server Wins: Server changes always take precedence
- Client Wins: Local changes always take precedence
- Custom: Provide your own conflict resolution logic
// Using a custom conflict resolver
await PocketSync.initialize(
options: PocketSyncOptions(
// ... other options
conflictResolutionStrategy: ConflictResolutionStrategy.custom,
customResolver: (localChange, remoteChange) async {
// Your custom logic to decide which change wins
return localChange.timestamp > remoteChange.timestamp
? localChange
: remoteChange;
},
),
// ... database options
);
Set the user ID:
PocketSync.instance.setUserId('user123');
Note that this is required for sync to actually work. On the server side, the user ID is used to identify the user and their data.
Clear all sync tracking data (use with caution):
await PocketSync.instance.reset();
Note: Call
PocketSync.instance.reset()
before callingPocketSync.instance.start()
to reset the sync engine (for existing apps. Be cautious when using this method as it will clear all change tracking data). It runs once per plugin version (the goal is to provide a smooth transition for people that were using the sdk prior to version 0.3.0)
Clean up resources when the app is closing:
await PocketSync.instance.dispose();
- PocketSync now uses the new schema system to define your database schema. This change is interesting because it enables a set of features that were not possible before. Our your end, it helps you to define your database schema in a more type-safe way.
- PocketSync now uses SQLite FFI to fix issues with JSON_OBJECT function not being available on some Android devices
- The implementation uses sqflite_common_ffi and sqlite3_flutter_libs packages to provide a more recent version of SQLite with JSON function support
- A SqliteFfiHelper class was created to initialize the FFI implementation before database operations
- Call
PocketSync.instance.reset()
before callingPocketSync.instance.start()
to reset the sync engine.
- Initialize early: Initialize PocketSync during app startup
- Handle conflicts: Choose an appropriate conflict resolution strategy for your app
- Batch operations: Group related database operations to optimize sync performance
- User authentication: Set the user ID when the user logs in (sync will not work without a user ID)
If you have any questions or need help, please open an issue on the GitHub repository.
If you need to talk to the PocketSync team, please reach out to hello@pocketsync.dev.
This project is licensed under the MIT License - see the LICENSE file for details.