This guide covers how to initialize Datum and use the global API for managing your data synchronization.
Overview#
The Datum class is the central entry point for all Datum operations. It manages entity registration, synchronization, and provides global access to managers.
Initialization#
Before using any Datum features, you must initialize the singleton instance:
import 'package:datum/datum.dart';
void main() async {
// Initialize Datum
final datum = await Datum.initialize(
config: DatumConfig(
// Configuration options
autoSyncInterval: Duration(minutes: 15),
enableLogging: true,
),
connectivityChecker: MyConnectivityChecker(),
registrations: [
DatumRegistration<Task>(
localAdapter: HiveTaskAdapter(),
remoteAdapter: SupabaseTaskAdapter(),
),
DatumRegistration<User>(
localAdapter: HiveUserAdapter(),
remoteAdapter: SupabaseUserAdapter(),
),
],
);
// Now you can use Datum
runApp(MyApp());
}
Configuration Options#
final config = DatumConfig(
// Synchronization
autoSyncInterval: Duration(minutes: 15), // How often to auto-sync
autoStartSync: true, // Start sync automatically
syncTimeout: Duration(seconds: 30), // Sync operation timeout
defaultSyncDirection: SyncDirection.pushThenPull, // Default sync behavior
// Conflict Resolution
defaultConflictResolver: LastWriteWinsResolver(), // Default resolver
// User Management
defaultUserSwitchStrategy: UserSwitchStrategy.syncThenSwitch,
initialUserId: null, // User to sync on startup
// Performance
remoteEventDebounceTime: Duration(milliseconds: 50),
changeCacheDuration: Duration(seconds: 5),
// Error Handling
errorRecoveryStrategy: DatumErrorRecoveryStrategy(
maxRetries: 3,
backoffStrategy: ExponentialBackoffStrategy(),
),
// Schema Management
schemaVersion: 1,
migrations: [MyMigration()],
// Execution Strategies
syncExecutionStrategy: SequentialStrategy(),
syncRequestStrategy: SequentialRequestStrategy(),
);
Connectivity Checker#
You must provide a connectivity checker implementation:
import 'package:connectivity_plus/connectivity_plus.dart';
class MyConnectivityChecker implements DatumConnectivityChecker {
final _connectivity = Connectivity();
@override
Future<bool> get isConnected async {
final results = await _connectivity.checkConnectivity();
return !results.contains(ConnectivityResult.none);
}
@override
Stream<bool> get onStatusChange {
return _connectivity.onConnectivityChanged.map((results) {
return !results.contains(ConnectivityResult.none);
});
}
}
Entity Registration#
Register your entities with their adapters:
final registrations = [
DatumRegistration<Task>(
localAdapter: HiveTaskAdapter(),
remoteAdapter: SupabaseTaskAdapter(),
conflictResolver: MergeResolver(),
middlewares: [EncryptionMiddleware()],
observers: [TaskObserver()],
config: DatumConfig(
// Entity-specific config overrides
autoSyncInterval: Duration(minutes: 5),
),
),
];
Global CRUD Operations#
Once initialized, you can perform CRUD operations globally:
// Create
final newTask = await Datum.create(Task(
id: 'task-1',
userId: 'user-123',
title: 'Learn Datum',
createdAt: DateTime.now(),
modifiedAt: DateTime.now(),
));
// Read
final task = await Datum.read<Task>('task-1', userId: 'user-123');
final allTasks = await Datum.readAll<Task>(userId: 'user-123');
// Update
final updatedTask = await Datum.update(task.copyWith(title: 'Learn Datum v2'));
// Delete
final deleted = await Datum.delete<Task>(id: 'task-1', userId: 'user-123');
// Batch operations
final newTasks = await Datum.createMany<Task>(
items: [task1, task2, task3],
userId: 'user-123',
);
Manager Access#
Access specific managers for advanced operations:
// Get manager for a type
final taskManager = Datum.manager<Task>();
// Perform manager-specific operations
await taskManager.startAutoSync('user-123');
final pendingCount = await taskManager.getPendingCount('user-123');
Global Synchronization#
Trigger synchronization across all registered entities:
// Sync all entities for a user
final result = await Datum.instance.synchronize('user-123');
// Sync with custom options
final result = await Datum.instance.synchronize(
'user-123',
options: DatumSyncOptions(
direction: SyncDirection.pullThenPush,
includeDeletes: true,
),
);
Reactive Streams#
Watch for changes globally:
// Watch all tasks
final taskStream = Datum.watchAll<Task>(userId: 'user-123');
// Watch specific task
final taskStream = Datum.watchById<Task>('task-1', 'user-123');
// Watch with pagination
final paginatedStream = Datum.watchAllPaginated<Task>(
PaginationConfig(limit: 20, offset: 0),
userId: 'user-123',
);
Querying#
Perform queries across entities:
// Simple query
final completedTasks = await Datum.query<Task>(
DatumQuery(
filters: [Filter('isCompleted', FilterOperator.equals, true)],
sorting: [SortDescriptor('createdAt', SortDirection.descending)],
),
source: DataSource.local,
userId: 'user-123',
);
// Complex query with relationships
final postsWithAuthors = await Datum.query<Post>(
DatumQuery(
filters: [Filter('createdAt', FilterOperator.greaterThan, DateTime.now().subtract(Duration(days: 7)))],
withRelated: ['author'],
sorting: [SortDescriptor('createdAt', SortDirection.descending)],
limit: 50,
),
source: DataSource.remote,
userId: 'user-123',
);
Health Monitoring#
Check the health of all managers:
// Check health of all managers
final allHealths = await Datum.instance.allHealths.first;
// Check health of specific manager
final taskHealth = await Datum.checkHealth<Task>();
Metrics and Monitoring#
Access global metrics and status:
// Global metrics stream
final metricsStream = Datum.instance.metrics;
// Current metrics
final currentMetrics = Datum.instance.currentMetrics;
// User-specific status
final userStatus = Datum.instance.statusForUser('user-123');
Error Handling#
Handle initialization and runtime errors:
try {
final datum = await Datum.initialize(/* ... */);
} on DatumException catch (e) {
switch (e.code) {
case 'entity_already_registered':
print('Entity already registered');
break;
case 'network_error':
print('Network connectivity issue');
break;
default:
print('Initialization failed: ${e.message}');
}
}
Lifecycle Management#
Properly dispose of resources when shutting down:
// Pause all operations
Datum.instance.pauseSync();
// Resume operations
Datum.instance.resumeSync();
// Complete shutdown
await Datum.instance.dispose();
Best Practices#
-
Initialize early: Call
Datum.initialize()as early as possible in your app lifecycle - Handle connectivity: Always provide a reliable connectivity checker
- Configure appropriately: Tune configuration options based on your app's needs
- Monitor health: Regularly check health status and handle degraded states
- Clean up: Always dispose of Datum when shutting down your app
- Error handling: Implement proper error handling for all Datum operations