Back to Documentation
Examples
Real-world code examples and use cases for SyncLayer
Todo App with Offline Support
A complete todo application with offline-first architecture
dart
import 'package:flutter/material.dart';
import 'package:synclayer/synclayer.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SyncLayer.init(
SyncConfig(
baseUrl: 'https://api.example.com',
collections: ['todos'],
conflictStrategy: ConflictStrategy.lastWriteWins,
),
);
runApp(TodoApp());
}
class TodoApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo App',
home: TodoListScreen(),
);
}
}
class TodoListScreen extends StatefulWidget {
@override
_TodoListScreenState createState() => _TodoListScreenState();
}
class _TodoListScreenState extends State<TodoListScreen> {
final _textController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Todos'),
actions: [
IconButton(
icon: Icon(Icons.sync),
onPressed: () => SyncLayer.syncNow(),
),
],
),
body: StreamBuilder<List<Map<String, dynamic>>>(
stream: SyncLayer.collection('todos').watch(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
final todos = snapshot.data!;
return ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos[index];
return ListTile(
leading: Checkbox(
value: todo['done'] ?? false,
onChanged: (value) async {
await SyncLayer.collection('todos').save(
{'done': value},
id: todo['id'],
);
},
),
title: Text(todo['text'] ?? ''),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
await SyncLayer.collection('todos').delete(todo['id']);
},
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _showAddDialog(context),
child: Icon(Icons.add),
),
);
}
void _showAddDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Add Todo'),
content: TextField(
controller: _textController,
decoration: InputDecoration(hintText: 'Enter todo text'),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
),
TextButton(
onPressed: () async {
if (_textController.text.isNotEmpty) {
await SyncLayer.collection('todos').save({
'text': _textController.text,
'done': false,
});
_textController.clear();
Navigator.pop(context);
}
},
child: Text('Add'),
),
],
),
);
}
}Firebase Integration
Using SyncLayer with Firebase Firestore
dart
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:synclayer/synclayer.dart';
import 'package:synclayer/adapters/firebase_adapter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
// Initialize SyncLayer with Firebase adapter
await SyncLayer.init(
SyncConfig(
collections: ['todos', 'users'],
customBackendAdapter: FirebaseAdapter(
firestore: FirebaseFirestore.instance,
),
conflictStrategy: ConflictStrategy.lastWriteWins,
),
);
runApp(MyApp());
}Supabase Integration
Using SyncLayer with Supabase
dart
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:synclayer/synclayer.dart';
import 'package:synclayer/adapters/supabase_adapter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Supabase
await Supabase.initialize(
url: 'YOUR_SUPABASE_URL',
anonKey: 'YOUR_SUPABASE_ANON_KEY',
);
// Initialize SyncLayer with Supabase adapter
await SyncLayer.init(
SyncConfig(
collections: ['todos', 'users'],
customBackendAdapter: SupabaseAdapter(
client: Supabase.instance.client,
),
conflictStrategy: ConflictStrategy.serverWins,
),
);
runApp(MyApp());
}Event Monitoring
Listen to sync events and handle errors
dart
import 'package:synclayer/synclayer.dart';
class SyncMonitor {
StreamSubscription? _subscription;
void startMonitoring() {
_subscription = SyncLayer.events.listen((event) {
if (event is SyncStartedEvent) {
print('🔄 Sync started at ${event.timestamp}');
} else if (event is SyncCompletedEvent) {
print('✅ Sync completed: ${event.itemsSynced} items');
} else if (event is SyncErrorEvent) {
print('❌ Sync error: ${event.error}');
// Handle error (show notification, retry, etc.)
} else if (event is ConflictDetectedEvent) {
print('⚠️ Conflict in ${event.collection}: ${event.entityId}');
} else if (event is ConnectivityChangedEvent) {
if (event.isOnline) {
print('🌐 Back online - syncing...');
SyncLayer.syncNow();
} else {
print('📴 Offline mode');
}
}
});
}
void stopMonitoring() {
_subscription?.cancel();
}
}Custom Backend Adapter
Create a custom adapter for your own backend
dart
import 'package:synclayer/core/backend_adapter.dart';
import 'package:dio/dio.dart';
class CustomBackendAdapter implements BackendAdapter {
final Dio _dio;
final String _baseUrl;
CustomBackendAdapter({
required String baseUrl,
String? authToken,
}) : _baseUrl = baseUrl,
_dio = Dio(BaseOptions(
baseUrl: baseUrl,
headers: authToken != null
? {'Authorization': 'Bearer $authToken'}
: null,
));
@override
Future<Map<String, dynamic>> pull(
String collection,
DateTime? lastSync,
) async {
try {
final response = await _dio.get(
'/$collection/sync',
queryParameters: lastSync != null
? {'since': lastSync.toIso8601String()}
: null,
);
return response.data;
} catch (e) {
throw Exception('Pull failed: $e');
}
}
@override
Future<void> push(
String collection,
List<Map<String, dynamic>> changes,
) async {
try {
await _dio.post(
'/$collection/sync',
data: {'changes': changes},
);
} catch (e) {
throw Exception('Push failed: $e');
}
}
@override
Future<void> delete(String collection, String id) async {
try {
await _dio.delete('/$collection/$id');
} catch (e) {
throw Exception('Delete failed: $e');
}
}
@override
Future<void> updateAuthToken(String token) async {
_dio.options.headers['Authorization'] = 'Bearer $token';
}
}
// Usage
await SyncLayer.init(
SyncConfig(
collections: ['todos'],
customBackendAdapter: CustomBackendAdapter(
baseUrl: 'https://api.example.com',
authToken: 'your-token',
),
),
);Batch Operations
Efficiently handle multiple operations
dart
// Save multiple items
Future<void> saveBatch(List<Map<String, dynamic>> items) async {
final collection = SyncLayer.collection('todos');
for (final item in items) {
await collection.save(item);
}
// Trigger sync once after all saves
await SyncLayer.syncNow();
}
// Delete multiple items
Future<void> deleteBatch(List<String> ids) async {
final collection = SyncLayer.collection('todos');
for (final id in ids) {
await collection.delete(id);
}
await SyncLayer.syncNow();
}
// Usage
await saveBatch([
{'text': 'Task 1', 'done': false},
{'text': 'Task 2', 'done': false},
{'text': 'Task 3', 'done': false},
]);Query and Filter
Filter and query local data
dart
// Get all todos
final allTodos = await SyncLayer.collection('todos').getAll();
// Filter completed todos
final completedTodos = allTodos.where((todo) => todo['done'] == true).toList();
// Filter by text search
final searchResults = allTodos
.where((todo) =>
todo['text'].toString().toLowerCase().contains('buy'))
.toList();
// Sort by date
final sortedTodos = allTodos
..sort((a, b) =>
DateTime.parse(b['createdAt'])
.compareTo(DateTime.parse(a['createdAt'])));
// Watch with filter
SyncLayer.collection('todos').watch().listen((todos) {
final activeTodos = todos.where((t) => t['done'] == false).toList();
print('Active todos: ${activeTodos.length}');
});Complete Example App
Check out our complete example application on GitHub with more advanced features.
View on GitHub