Skip to main content
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