flutter_offline_sync_engine 1.0.0
flutter_offline_sync_engine: ^1.0.0 copied to clipboard
Optimistic UI offline-first data synchronization engine for Flutter with configurable conflict resolution strategies.
import 'package:flutter/material.dart';
import 'package:flutter_offline_sync_engine/flutter_offline_sync_engine.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Offline Sync Engine Demo',
theme: ThemeData.dark(useMaterial3: true),
home: const SyncScreen(),
);
}
}
class SyncScreen extends StatefulWidget {
const SyncScreen({super.key});
@override
State<SyncScreen> createState() => _SyncScreenState();
}
class _SyncScreenState extends State<SyncScreen> {
late final OfflineSyncEngine _engine;
final List<String> _localItems = ['Initial Item 1', 'Initial Item 2'];
@override
void initState() {
super.initState();
_engine = OfflineSyncEngine(
actionHandler: (action) async {
// Simulate network API delay
await Future<void>.delayed(const Duration(milliseconds: 600));
return true; // Sync success
},
);
}
void _addItem() {
final newItem = 'Offline Item #${_localItems.length + 1}';
setState(() {
_localItems.add(newItem);
});
_engine.recordMutation(SyncAction(
id: 'mutation_${DateTime.now().millisecondsSinceEpoch}',
entityType: 'items',
actionType: SyncActionType.create,
payload: {'title': newItem},
));
}
Future<void> _syncNow() async {
final synced = await _engine.synchronize();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Successfully synced $synced pending items!')),
);
setState(() {});
}
}
@override
void dispose() {
_engine.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('flutter_offline_sync_engine Demo'),
actions: [
IconButton(
icon: const Icon(Icons.sync),
onPressed: _engine.pendingCount > 0 ? _syncNow : null,
),
],
),
body: Column(
children: [
StreamBuilder<SyncStatus>(
stream: _engine.statusStream,
initialData: _engine.currentStatus,
builder: (context, snapshot) {
final status = snapshot.data ?? SyncStatus.idle;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
color: status == SyncStatus.syncing
? Colors.orange
: status == SyncStatus.success
? Colors.green.shade800
: Colors.blueGrey.shade800,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Engine Status: ${status.name.toUpperCase()}'),
Text('Pending Queue: ${_engine.pendingCount}'),
],
),
);
},
),
Expanded(
child: ListView.builder(
itemCount: _localItems.length,
itemBuilder: (context, index) => ListTile(
leading: const Icon(Icons.check_circle_outline),
title: Text(_localItems[index]),
),
),
),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: _addItem,
icon: const Icon(Icons.add),
label: const Text('Add Offline Item'),
),
);
}
}