Exemples de Base de Données Hive Flutter

Exemples d'implémentation de base de données Hive Flutter avec stockage hors ligne, modélisation des données et optimisation des performances

💻 Base de Données Hive de Base dart

🟢 simple ⭐⭐

Implémentation complète de base de données Hive Flutter avec modélisation des données, opérations CRUD et stockage hors ligne

⏱️ 40 min 🏷️ flutter, hive, database, offline
Prerequisites: Flutter basics, Dart programming, Database concepts
// Flutter Hive Database - Complete Implementation
// Offline NoSQL Database for Flutter Applications

import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';

part 'user_model.g.dart';
part 'product_model.g.dart';
part 'app_models.g.dart';

// 1. Main Application Setup
void main() async {
  // Ensure Flutter bindings are initialized
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Hive
  await Hive.initFlutter('my_app_db');

  // Register Hive Adapters
  Hive.registerAdapter(UserAdapter());
  Hive.registerAdapter(ProductAdapter());
  Hive.registerAdapter(OrderAdapter());
  Hive.registerAdapter(OrderItemAdapter());

  // Open Hive boxes
  await Hive.openBox<User>('users');
  await Hive.openBox<Product>('products');
  await Hive.openBox<Order>('orders');
  await Hive.openBox('settings');

  runApp(MyApp());
}

// 2. Data Models

@HiveType(typeId: 0)
class User extends HiveObject {
  @HiveField(0)
  late String id;

  @HiveField(1)
  late String name;

  @HiveField(2)
  late String email;

  @HiveField(3)
  late DateTime createdAt;

  @HiveField(4)
  DateTime? lastLogin;

  @HiveField(5)
  String? avatar;

  @HiveField(6)
  List<String> tags = [];

  @HiveField(7)
  bool isActive = true;

  // Computed property
  String get displayName => name.isEmpty ? email : name;

  // Helper method
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'createdAt': createdAt.toIso8601String(),
      'lastLogin': lastLogin?.toIso8601String(),
      'avatar': avatar,
      'tags': tags,
      'isActive': isActive,
    };
  }

  // Factory constructor
  factory User.fromJson(Map<String, dynamic> json) {
    final user = User();
    user.id = json['id'];
    user.name = json['name'] ?? '';
    user.email = json['email'] ?? '';
    user.createdAt = DateTime.parse(json['createdAt']);
    user.lastLogin = json['lastLogin'] != null ? DateTime.parse(json['lastLogin']) : null;
    user.avatar = json['avatar'];
    user.tags = List<String>.from(json['tags'] ?? []);
    user.isActive = json['isActive'] ?? true;
    return user;
  }
}

@HiveType(typeId: 1)
class Product extends HiveObject {
  @HiveField(0)
  late String id;

  @HiveField(1)
  late String name;

  @HiveField(2)
  late String description;

  @HiveField(3)
  late double price;

  @HiveField(4)
  String? imageUrl;

  @HiveField(5)
  List<String> categories = [];

  @HiveField(6)
  int stock = 0;

  @HiveField(7)
  bool isAvailable = true;

  @HiveField(8)
  DateTime createdAt;

  @HiveField(9)
  DateTime? updatedAt;

  @HiveField(10)
  Map<String, dynamic> attributes = {};

  // Helper methods
  bool get isInStock => stock > 0;

  String get formattedPrice => '${price.toStringAsFixed(2)}';

  // Search helper
  bool matchesQuery(String query) {
    final lowerQuery = query.toLowerCase();
    return name.toLowerCase().contains(lowerQuery) ||
           description.toLowerCase().contains(lowerQuery) ||
           categories.any((cat) => cat.toLowerCase().contains(lowerQuery));
  }
}

@HiveType(typeId: 2)
class Order extends HiveObject {
  @HiveField(0)
  late String id;

  @HiveField(1)
  late String userId;

  @HiveField(2)
  List<OrderItem> items = [];

  @HiveField(3)
  late double totalAmount;

  @HiveField(4)
  late DateTime createdAt;

  @HiveField(5)
  OrderStatus status = OrderStatus.pending;

  @HiveField(6)
  String? shippingAddress;

  @HiveField(7)
  String? notes;

  // Computed properties
  int get totalItems => items.fold(0, (sum, item) => sum + item.quantity);
}

@HiveType(typeId: 3)
class OrderItem {
  @HiveField(0)
  late String productId;

  @HiveField(1)
  late String productName;

  @HiveField(2)
  late int quantity;

  @HiveField(3)
  late double unitPrice;

  @HiveField(4)
  late double totalPrice;
}

enum OrderStatus {
  pending,
  confirmed,
  processing,
  shipped,
  delivered,
  cancelled
}

// 3. Database Service
class DatabaseService {
  static final DatabaseService _instance = DatabaseService._internal();

  factory DatabaseService() => _instance;

  DatabaseService._internal();

  // Box references
  late final Box<User> _userBox;
  late final Box<Product> _productBox;
  late final Box<Order> _orderBox;
  late final Box _settingsBox;

  // Initialize boxes
  void initialize() {
    _userBox = Hive.box<User>('users');
    _productBox = Hive.box<Product>('products');
    _orderBox = Hive.box<Order>('orders');
    _settingsBox = Hive.box('settings');
  }

  // User CRUD Operations
  Future<User> createUser(User user) async {
    if (user.id.isEmpty) {
      user.id = DateTime.now().millisecondsSinceEpoch.toString();
    }

    await _userBox.put(user);
    return user;
  }

  Future<User?> getUser(String id) async {
    return _userBox.get(id);
  }

  Future<List<User>> getAllUsers() async {
    return _userBox.values.toList();
  }

  Future<List<User>> getActiveUsers() async {
    return _userBox.values.where((user) => user.isActive).toList();
  }

  Future<void> updateUser(User user) async {
    await _userBox.put(user);
  }

  Future<void> deleteUser(String id) async {
    await _userBox.delete(id);
  }

  Future<List<User>> searchUsers(String query) async {
    if (query.isEmpty) {
      return getAllUsers();
    }

    final lowerQuery = query.toLowerCase();
    return _userBox.values.where((user) =>
      user.name.toLowerCase().contains(lowerQuery) ||
      user.email.toLowerCase().contains(lowerQuery)
    ).toList();
  }

  // Product CRUD Operations
  Future<Product> createProduct(Product product) async {
    if (product.id.isEmpty) {
      product.id = DateTime.now().millisecondsSinceEpoch.toString();
    }

    if (product.createdAt == null) {
      product.createdAt = DateTime.now();
    }

    await _productBox.put(product);
    return product;
  }

  Future<Product?> getProduct(String id) async {
    return _productBox.get(id);
  }

  Future<List<Product>> getAllProducts() async {
    return _productBox.values.toList();
  }

  Future<List<Product>> getAvailableProducts() async {
    return _productBox.values.where((product) => product.isAvailable).toList();
  }

  Future<List<Product>> getProductsByCategory(String category) async {
    return _productBox.values.where((product) =>
      product.categories.contains(category)
    ).toList();
  }

  Future<void> updateProduct(Product product) async {
    product.updatedAt = DateTime.now();
    await _productBox.put(product);
  }

  Future<void> deleteProduct(String id) async {
    await _productBox.delete(id);
  }

  Future<List<Product>> searchProducts(String query) async {
    if (query.isEmpty) {
      return getAllProducts();
    }

    return _productBox.values.where((product) => product.matchesQuery(query)).toList();
  }

  Future<void> updateProductStock(String productId, int newStock) async {
    final product = await getProduct(productId);
    if (product != null) {
      product.stock = newStock;
      product.isAvailable = newStock > 0;
      await updateProduct(product);
    }
  }

  // Order CRUD Operations
  Future<Order> createOrder(Order order) async {
    if (order.id.isEmpty) {
      order.id = DateTime.now().millisecondsSinceEpoch.toString();
    }

    await _orderBox.put(order);
    return order;
  }

  Future<Order?> getOrder(String id) async {
    return _orderBox.get(id);
  }

  Future<List<Order>> getUserOrders(String userId) async {
    return _orderBox.values
        .where((order) => order.userId == userId)
        .toList();
  }

  Future<List<Order>> getOrdersByStatus(OrderStatus status) async {
    return _orderBox.values
        .where((order) => order.status == status)
        .toList();
  }

  Future<void> updateOrderStatus(String orderId, OrderStatus newStatus) async {
    final order = await getOrder(orderId);
    if (order != null) {
      order.status = newStatus;
      await _orderBox.put(order);
    }
  }

  Future<void> deleteOrder(String id) async {
    await _orderBox.delete(id);
  }

  // Settings Operations
  Future<void> saveSetting(String key, dynamic value) async {
    await _settingsBox.put(key, value);
  }

  Future<T?> getSetting<T>(String key, {T? defaultValue}) async {
    return _settingsBox.get(key, defaultValue);
  }

  // Backup and Restore
  Future<void> backupData(String backupPath) async {
    final appDir = await getApplicationDocumentsDirectory();
    final backupDir = Directory('${appDir.path}/backups');

    if (!await backupDir.exists()) {
      await backupDir.create(recursive: true);
    }

    final timestamp = DateTime.now().millisecondsSinceEpoch;
    final backupFile = File('${backupDir.path}/backup_${timestamp}.hive');

    // Export all boxes to backup file
    final userData = _userBox.toMap();
    final productData = _productBox.toMap();
    final orderData = _orderBox.toMap();
    final settingsData = _settingsBox.toMap();

    final backupData = {
      'users': userData,
      'products': productData,
      'orders': orderData,
      'settings': settingsData,
      'timestamp': timestamp,
      'version': '1.0.0'
    };

    // Convert to JSON and save
    final jsonString = jsonEncode(backupData);
    await backupFile.writeAsString(jsonString);

    print('Data backed up successfully to ${backupFile.path}');
  }

  Future<void> restoreData(String backupPath) async {
    final backupFile = File(backupPath);

    if (!await backupFile.exists()) {
      throw Exception('Backup file not found');
    }

    final jsonString = await backupFile.readAsString();
    final backupData = jsonDecode(jsonString) as Map<String, dynamic>;

    // Clear existing data
    await _userBox.clear();
    await _productBox.clear();
    await _orderBox.clear();
    await _settingsBox.clear();

    // Restore users
    final usersData = backupData['users'] as Map;
    for (final entry in usersData.entries) {
      final userJson = entry.value;
      final user = User.fromJson(userJson);
      await _userBox.put(user);
    }

    // Restore products
    final productsData = backupData['products'] as Map;
    for (final entry in productsData.entries) {
      final productData = entry.value;
      // Note: Product would need its own fromJson method
      // This is a simplified example
    }

    // Restore orders
    final ordersData = backupData['orders'] as Map;
    for (final entry in ordersData.entries) {
      // Restore orders similar to users
    }

    // Restore settings
    final settingsData = backupData['settings'] as Map;
    for (final entry in settingsData.entries) {
      await _settingsBox.put(entry.key, entry.value);
    }

    print('Data restored successfully from ${backupPath}');
  }

  // Performance Optimization
  Future<void> compactDatabase() async {
    await _userBox.compact();
    await _productBox.compact();
    await _orderBox.compact();
    await _settingsBox.compact();
    print('Database compacted successfully');
  }

  Future<void> clearAllData() async {
    await _userBox.clear();
    await _productBox.clear();
    await _orderBox.clear();
    await _settingsBox.clear();
    print('All data cleared successfully');
  }

  // Statistics
  Future<Map<String, dynamic>> getDatabaseStats() async {
    return {
      'users': _userBox.length,
      'products': _productBox.length,
      'orders': _orderBox.length,
      'activeUsers': _userBox.values.where((u) => u.isActive).length,
      'availableProducts': _productBox.values.where((p) => p.isAvailable).length,
      'pendingOrders': _orderBox.values.where((o) => o.status == OrderStatus.pending).length,
      'databaseSize': await _getDatabaseSize(),
    };
  }

  Future<int> _getDatabaseSize() async {
    final appDir = await getApplicationDocumentsDirectory();
    final dbDir = Directory('${appDir.path}/my_app_db');

    if (!await dbDir.exists()) {
      return 0;
    }

    int totalSize = 0;
    await for (final entity in dbDir.list()) {
      if (entity is File) {
        final file = entity as File;
        totalSize += await file.length();
      }
    }

    return totalSize;
  }
}

// 4. Flutter App UI
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hive Demo',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
        useMaterial3: true,
      ),
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final DatabaseService _dbService = DatabaseService();

  @override
  void initState() {
    super.initState();
    _dbService.initialize();
    _initializeSampleData();
  }

  Future<void> _initializeSampleData() async {
    // Create sample user
    final user = User()
      ..id = 'user_1'
      ..name = 'John Doe'
      ..email = '[email protected]'
      ..createdAt = DateTime.now()
      ..tags = ['developer', 'flutter'];

    await _dbService.createUser(user);

    // Create sample products
    final products = [
      Product()
        ..id = 'prod_1'
        ..name = 'Flutter Development Guide'
        ..description = 'Complete guide to Flutter development'
        ..price = 29.99
        ..categories = ['books', 'education']
        ..stock = 100
        ..isAvailable = true
        ..createdAt = DateTime.now(),
      Product()
        ..id = 'prod_2'
        ..name = 'Dart Programming'
        ..description = 'Learn Dart programming language'
        ..price = 19.99
        ..categories = ['books', 'programming']
        ..stock = 50
        ..isAvailable = true
        ..createdAt = DateTime.now(),
    ];

    for (final product in products) {
      await _dbService.createProduct(product);
    }

    // Create sample order
    final order = Order()
      ..id = 'order_1'
      ..userId = 'user_1'
      ..items = [
        OrderItem()
          ..productId = 'prod_1'
          ..productName = 'Flutter Development Guide'
          ..quantity = 1
          ..unitPrice = 29.99
          ..totalPrice = 29.99,
      ]
      ..totalAmount = 29.99
      ..createdAt = DateTime.now()
      ..status = OrderStatus.pending;

    await _dbService.createOrder(order);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Hive Demo'),
        actions: [
          IconButton(
            icon: const Icon(Icons.backup),
            onPressed: () => _showBackupDialog(context),
          ),
          IconButton(
            icon: const Icon(Icons.restore),
            onPressed: () => _showRestoreDialog(context),
          ),
        ],
      ),
      body: ValueListenableBuilder(
        valueListenable: Hive.box<User>('users').listenable(),
        builder: (context, snapshot, _) {
          final users = Hive.box<User>('users').values.toList();

          return Column(
            children: [
              _buildStatsHeader(),
              Expanded(
                child: users.isEmpty
                    ? const Center(child: Text('No users found'))
                    : ListView.builder(
                        itemCount: users.length,
                        itemBuilder: (context, index) {
                          final user = users[index];
                          return UserCard(
                            user: user,
                            onTap: () => _showUserDetails(context, user),
                          );
                        },
                      ),
              ),
            ],
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showAddUserDialog(context),
        child: const Icon(Icons.add),
      ),
    );
  }

  Widget _buildStatsHeader() {
    return FutureBuilder<Map<String, dynamic>>(
      future: _dbService.getDatabaseStats(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return const SizedBox(height: 80);
        }

        final stats = snapshot.data!;

        return Container(
          padding: const EdgeInsets.all(16),
          child: Column(
            children: [
              const Text('Database Statistics', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              const SizedBox(height: 8),
              Wrap(
                spacing: 16,
                children: [
                  _StatCard(label: 'Users', value: stats['users'].toString()),
                  _StatCard(label: 'Products', value: stats['products'].toString()),
                  _StatCard(label: 'Orders', value: stats['orders'].toString()),
                  _StatCard(label: 'DB Size', value: '${(stats['databaseSize'] / 1024).toStringAsFixed(1)} KB'),
                ],
              ),
            ],
          ),
        );
      },
    );
  }

  void _showAddUserDialog(BuildContext context) {
    final nameController = TextEditingController();
    final emailController = TextEditingController();

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Add User'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(labelText: 'Name'),
            ),
            TextField(
              controller: emailController,
              decoration: const InputDecoration(labelText: 'Email'),
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () async {
              if (nameController.text.isNotEmpty && emailController.text.isNotEmpty) {
                final user = User()
                  ..id = DateTime.now().millisecondsSinceEpoch.toString()
                  ..name = nameController.text
                  ..email = emailController.text
                  ..createdAt = DateTime.now();

                await _dbService.createUser(user);
                Navigator.pop(context);
              }
            },
            child: const Text('Add'),
          ),
        ],
      ),
    );
  }

  void _showUserDetails(BuildContext context, User user) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(user.displayName),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Email: ${user.email}'),
            const SizedBox(height: 8),
            Text('Created: ${user.createdAt.toString().split('.')[0]}'),
            Text('Active: ${user.isActive ? 'Yes' : 'No'}'),
            if (user.tags.isNotEmpty) ...[
              const SizedBox(height: 8),
              Text('Tags: ${user.tags.join(', ')}'),
            ],
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  void _showBackupDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Backup Data'),
        content: const Text('Create a backup of all local data?'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () async {
              await _dbService.backupData('');
              Navigator.pop(context);
              ScaffoldMessenger.of(context).showSnackBar(
                const SnackBar(content: 'Backup completed successfully'),
              );
            },
            child: const Text('Backup'),
          ),
        ],
      ),
    );
  }

  void _showRestoreDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Restore Data'),
        content: const Text('Restore data from backup file?'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          ElevatedButton(
            onPressed: () async {
              // For demo purposes, using a fixed path
              // In real app, you'd show a file picker
              try {
                await _dbService.restoreData('backup_path.hive');
                Navigator.pop(context);
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: 'Data restored successfully'),
                );
              } catch (e) {
                Navigator.pop(context);
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: 'Failed to restore data: $e'),
                );
              }
            },
            child: const Text('Restore'),
          ),
        ],
      ),
    );
  }
}

class UserCard extends StatelessWidget {
  final User user;
  final VoidCallback onTap;

  const UserCard({
    Key? key,
    required this.user,
    required this.onTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: ListTile(
        leading: CircleAvatar(
          child: Text(user.name.isNotEmpty ? user.name[0] : user.email[0]),
        ),
        title: Text(user.displayName),
        subtitle: Text(user.email),
        trailing: Icon(
          user.isActive ? Icons.check_circle : Icons.cancel,
          color: user.isActive ? Colors.green : Colors.red,
        ),
        onTap: onTap,
      ),
    );
  }
}

class _StatCard extends StatelessWidget {
  final String label;
  final String value;

  const _StatCard({
    Key? key,
    required this.label,
    required this.value,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              value,
              style: const TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
            ),
            Text(
              label,
              style: const TextStyle(
                fontSize: 12,
                color: Colors.grey,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// 5. Advanced Features

// IndexedBox for better performance with large datasets
class IndexedUserBox {
  late final Box<int> _indexBox;

  Future<void> open() async {
    _indexBox = await Hive.openBox<int>('user_index');
  }

  Future<void> indexUser(User user) async {
    final index = await _indexBox.get(user.email) ?? [];
    if (!index.contains(user.id)) {
      index.add(user.id);
      await _indexBox.put(user.email, index);
    }
  }

  Future<List<String>> getUserIdsByEmail(String email) async {
    final index = await _indexBox.get(email) ?? [];
    return index;
  }
}

// LazyBox for efficient querying
class LazyUserService {
  static final LazyUserService _instance = LazyUserService._internal();

  factory LazyUserService() => _instance;

  LazyUserService._internal();

  late final LazyBox<User> _lazyBox;

  Future<void> open() async {
    _lazyBox = await Hive.openLazyBox<User>('users_lazy');
  }

  Stream<User> watchUser(String id) {
    return _lazyBox.watch(id);
  }

  Future<void> updateUser(String id, User user) async {
    await _lazyBox.put(id, user);
  }

  Future<User?> getUser(String id) async {
    return _lazyBox.get(id);
  }
}

// Hive Queries
class UserQueries {
  static Future<List<User>> findActiveUsers() async {
    final box = await Hive.openBox<User>('users');
    return box.values.where((user) => user.isActive).toList();
  }

  static Future<List<User>> findUsersCreatedAfter(DateTime date) async {
    final box = await Hive.openBox<User>('users');
    return box.values
        .where((user) => user.createdAt.isAfter(date))
        .toList();
  }

  static Future<List<User>> searchByName(String name) async {
    final box = await Hive.openBox<User>('users');
    return box.values
        .where((user) => user.name.toLowerCase().contains(name.toLowerCase()))
        .toList();
  }
}

// Migration Example
class DatabaseMigration {
  static Future<void> migrateToV2() async {
    final box = await Hive.openBox<User>('users');

    // Add new field to existing users
    for (final user in box.values) {
      if (user.tags.isEmpty) {
        user.tags = ['default'];
        await box.put(user);
      }
    }
  }
}

export { DatabaseService, User, Product, Order, OrderStatus };

💻 Optimisation des Performances Hive dart

🟡 intermediate ⭐⭐⭐⭐

Techniques avancées d'optimisation Hive avec chargement différé, indexation et gestion efficace des données

⏱️ 50 min 🏷️ flutter, hive, performance, optimization
Prerequisites: Flutter, Hive basics, Dart async programming
// Hive Performance Optimization Techniques
// Efficient data management for Flutter applications

import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'dart:async';
import 'package:meta/meta.dart';

// 1. Efficient Box Management
class EfficientBoxManager {
  static final Map<String, Box> _boxCache = {};
  static final Map<String, LazyBox> _lazyBoxCache = {};

  // Get or create box with caching
  static Future<T> getBox<T>(String name, {bool lazy = false}) async {
    if (!lazy && _boxCache.containsKey(name)) {
      return _boxCache[name] as Box<T>;
    }

    if (lazy && _lazyBoxCache.containsKey(name)) {
      return _lazyBoxCache[name] as LazyBox<T>;
    }

    if (lazy) {
      final box = await Hive.openLazyBox<T>(name);
      _lazyBoxCache[name] = box;
      return box;
    } else {
      final box = await Hive.openBox<T>(name);
      _boxCache[name] = box;
      return box;
    }
  }

  // Preload frequently used boxes
  static Future<void> preloadBoxes(List<String> boxNames) async {
    await Future.wait(
      boxNames.map((name) => getBox(name)),
    );
  }

  // Close all boxes
  static Future<void> closeAll() async {
    for (final box in _boxCache.values) {
      await box.close();
    }
    for (final box in _lazyBoxCache.values) {
      await box.close();
    }
    _boxCache.clear();
    _lazyBoxCache.clear();
  }
}

// 2. Pagination and Batch Operations
class PaginationService<T> {
  final String _boxName;
  final int _pageSize;

  PaginationService(this._boxName, {int pageSize = 20}) : _pageSize = pageSize;

  // Get paginated results
  Future<PaginatedResult<T>> getPage(int page, {String? sortBy, bool ascending = true}) async {
    final box = await EfficientBoxManager.getBox<T>(_boxName);
    final values = box.values.toList();

    // Sort if needed
    if (sortBy != null) {
      values.sort((a, b) {
        final aValue = _getSortValue(a, sortBy);
        final bValue = _getSortValue(b, sortBy);
        return ascending
            ? aValue.compareTo(bValue)
            : bValue.compareTo(aValue);
      });
    }

    final startIndex = page * _pageSize;
    final endIndex = (page + 1) * _pageSize;

    final items = startIndex < values.length
        ? values.sublist(startIndex, endIndex.clamp(0, values.length))
        : <T>[];

    return PaginatedResult<T>(
      items: items,
      page: page,
      pageSize: _pageSize,
      totalItems: values.length,
      hasNextPage: endIndex < values.length,
    );
  }

  dynamic _getSortValue(T item, String sortBy) {
    // This would be implemented based on your model
    // Example for User model:
    if (item is User) {
      final user = item as User;
      switch (sortBy) {
        case 'name':
          return user.name;
        case 'createdAt':
          return user.createdAt.millisecondsSinceEpoch;
        case 'email':
          return user.email;
        default:
          return '';
      }
    }
    return '';
  }

  // Batch insert
  Future<void> batchInsert(List<T> items) async {
    final box = await EfficientBoxManager.getBox<T>(_boxName);
    final writeBatch = <Future<void>>[];

    for (final item in items) {
      writeBatch.add(box.put(item));
    }

    await Future.wait(writeBatch);
  }

  // Stream paginated results
  Stream<PaginatedResult<T>> streamPages({
    int initialPage = 0,
    String? sortBy,
    bool ascending = true,
  }) async* {
    var currentPage = initialPage;
    bool hasNext = true;

    while (hasNext) {
      final result = await getPage(currentPage, sortBy: sortBy, ascending: ascending);
      yield result;
      hasNext = result.hasNextPage;
      currentPage++;
    }
  }
}

class PaginatedResult<T> {
  final List<T> items;
  final int page;
  final int pageSize;
  final int totalItems;
  final bool hasNextPage;

  PaginatedResult({
    required this.items,
    required this.page,
    required this.pageSize,
    required this.totalItems,
    required this.hasNextPage,
  });

  int get totalPages => (totalItems / pageSize).ceil();
  int get currentPageIndex => page;
}

// 3. Search and Filtering
class AdvancedSearchService<T> {
  final String _boxName;
  late final Box<T> _box;

  AdvancedSearchService(this._boxName);

  Future<void> initialize() async {
    _box = await EfficientBoxManager.getBox<T>(_boxName);
  }

  // Full-text search with filters
  Future<List<T>> search(SearchQuery query) async {
    var results = _box.values.toList();

    // Apply text search
    if (query.searchTerm.isNotEmpty) {
      results = results.where((item) => _matchesSearch(item, query.searchTerm)).toList();
    }

    // Apply filters
    for (final filter in query.filters) {
      results = results.where((item) => _matchesFilter(item, filter)).toList();
    }

    // Apply sorting
    if (query.sortBy.isNotEmpty) {
      results.sort((a, b) => _compareItems(a, b, query.sortBy, query.ascending));
    }

    // Apply limit and offset
    if (query.limit > 0) {
      final start = query.offset;
      final end = start + query.limit;
      results = results.sublist(start, end.clamp(0, results.length));
    }

    return results;
  }

  bool _matchesSearch(T item, String searchTerm) {
    // This would be implemented based on your model
    // Example implementation:
    if (item is User) {
      final user = item as User;
      final term = searchTerm.toLowerCase();
      return user.name.toLowerCase().contains(term) ||
             user.email.toLowerCase().contains(term) ||
             user.tags.any((tag) => tag.toLowerCase().contains(term));
    }
    return false;
  }

  bool _matchesFilter(T item, Filter filter) {
    // Implementation based on filter type and field
    switch (filter.type) {
      case FilterType.equals:
        return _getFieldValue(item, filter.field) == filter.value;
      case FilterType.contains:
        return _getFieldValue(item, filter.field).toString().contains(filter.value.toString());
      case FilterType.greaterThan:
        return _compareValues(_getFieldValue(item, filter.field), filter.value) > 0;
      case FilterType.lessThan:
        return _compareValues(_getFieldValue(item, filter.field), filter.value) < 0;
      case FilterType.isInList:
        final list = filter.value as List;
        return list.contains(_getFieldValue(item, filter.field));
      default:
        return true;
    }
  }

  dynamic _getFieldValue(T item, String field) {
    // Implementation based on your model
    if (item is User) {
      final user = item as User;
      switch (field) {
        case 'name':
          return user.name;
        case 'email':
          return user.email;
        case 'isActive':
          return user.isActive;
        case 'createdAt':
          return user.createdAt;
        case 'tags':
          return user.tags;
        default:
          return null;
      }
    }
    return null;
  }

  int _compareValues(dynamic a, dynamic b) {
    // Generic comparison
    if (a == null) return b == null ? 0 : -1;
    if (b == null) return 1;
    return a.toString().compareTo(b.toString());
  }

  int _compareItems(T a, T b, String sortBy, bool ascending) {
    final aValue = _getFieldValue(a, sortBy);
    final bValue = _getFieldValue(b, sortBy);
    final comparison = _compareValues(aValue, bValue);
    return ascending ? comparison : -comparison;
  }
}

class SearchQuery {
  String searchTerm = '';
  List<Filter> filters = [];
  String sortBy = '';
  bool ascending = true;
  int limit = 0;
  int offset = 0;
}

enum FilterType {
  equals,
  contains,
  greaterThan,
  lessThan,
  isInList,
}

class Filter {
  final String field;
  final FilterType type;
  final dynamic value;

  Filter({required this.field, required this.type, required this.value});
}

// 4. Caching Layer
class HiveCacheService<T> {
  final String _boxName;
  final Duration _cacheTtl;
  final Map<String, CachedItem<T>> _cache = {};

  HiveCacheService(this._boxName, {Duration cacheTtl = const Duration(minutes: 5)})
      : _cacheTtl = cacheTtl;

  Future<T?> get(String key, {bool useCache = true}) async {
    if (!useCache || !_cache.containsKey(key)) {
      final box = await EfficientBoxManager.getBox<T>(_boxName);
      final item = box.get(key);
      if (item != null) {
        _cache[key] = CachedItem(item, DateTime.now().add(_cacheTtl));
      }
      return item;
    }

    final cachedItem = _cache[key]!;
    if (DateTime.now().isAfter(cachedItem.expiresAt)) {
      _cache.remove(key);
      return null;
    }

    return cachedItem.item;
  }

  Future<void> put(String key, T item) async {
    final box = await EfficientBoxManager.getBox<T>(_boxName);
    await box.put(key, item);
    _cache[key] = CachedItem(item, DateTime.now().add(_cacheTtl));
  }

  Future<void> remove(String key) async {
    final box = await EfficientBoxManager.getBox<T>(_boxName);
    await box.delete(key);
    _cache.remove(key);
  }

  void clearCache() {
    _cache.clear();
  }

  // Preload frequently accessed items
  Future<void> preload(List<String> keys) async {
    final box = await EfficientBoxManager.getBox<T>(_boxName);

    for (final key in keys) {
      final item = box.get(key);
      if (item != null) {
        _cache[key] = CachedItem(item, DateTime.now().add(_cacheTtl));
      }
    }
  }
}

class CachedItem<T> {
  final T item;
  final DateTime expiresAt;

  CachedItem(this.item, this.expiresAt);
}

// 5. Background Sync Service
class BackgroundSyncService<T> {
  final String _boxName;
  final Duration _syncInterval;
  Timer? _syncTimer;
  bool _isSyncing = false;

  BackgroundSyncService(this._boxName, {Duration syncInterval = const Duration(minutes: 30)})
      : _syncInterval = syncInterval;

  Future<void> startAutoSync({Function(List<T>)? onSync}) async {
    _syncTimer?.cancel();
    _syncTimer = Timer.periodic(_syncInterval, (_) async {
      if (!_isSyncing) {
        _isSyncing = true;
        try {
          final items = await _performSync();
          if (onSync != null) {
            onSync(items);
          }
        } finally {
          _isSyncing = false;
        }
      }
    });
  }

  void stopAutoSync() {
    _syncTimer?.cancel();
    _syncTimer = null;
  }

  Future<List<T>> _performSync() async {
    final box = await EfficientBoxManager.getBox<T>(_boxName);
    final items = box.values.toList();

    // Perform sync logic here (e.g., sync with remote API)

    return items;
  }
}

// 6. Data Validation and Migration
class DataValidator<T> {
  final List<ValidationRule<T>> _rules;

  DataValidator(this._rules);

  ValidationResult validate(T item) {
    final errors = <String>[];

    for (final rule in _rules) {
      final result = rule.validate(item);
      if (!result.isValid) {
        errors.addAll(result.errors);
      }
    }

    return ValidationResult(
      isValid: errors.isEmpty,
      errors: errors,
    );
  }

  Future<ValidationResult> validateAndSave(T item, Future<void> Function(T) saveFunc) async {
    final result = validate(item);
    if (result.isValid) {
      await saveFunc(item);
    }
    return result;
  }
}

abstract class ValidationRule<T> {
  ValidationResult validate(T item);
}

class ValidationResult {
  final bool isValid;
  final List<String> errors;

  ValidationResult({required this.isValid, required this.errors});
}

// Example validation rules for User model
class UserNameValidationRule extends ValidationRule<User> {
  @override
  ValidationResult validate(User user) {
    final errors = <String>[];

    if (user.name.isEmpty) {
      errors.add('Name cannot be empty');
    }

    if (user.name.length < 2) {
      errors.add('Name must be at least 2 characters');
    }

    return ValidationResult(isValid: errors.isEmpty, errors: errors);
  }
}

class EmailValidationRule extends ValidationRule<User> {
  @override
  ValidationResult validate(User user) {
    final errors = <String>[];

    if (user.email.isEmpty) {
      errors.add('Email cannot be empty');
    }

    if (!_isValidEmail(user.email)) {
      errors.add('Invalid email format');
    }

    return ValidationResult(isValid: errors.isEmpty, errors: errors);
  }

  bool _isValidEmail(String email) {
    return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
  }
}

// 7. Performance Monitoring
class PerformanceMonitor {
  static final Map<String, List<int>> _operationTimes = {};

  static void recordOperation(String operation, int durationMs) {
    _operationTimes.putIfAbsent(operation, <int>[]);
    _operationTimes[operation]!.add(durationMs);
  }

  static Map<String, PerformanceStats> getStats() {
    final stats = <String, PerformanceStats>{};

    for (final entry in _operationTimes.entries) {
      final times = entry.value;
      if (times.isNotEmpty) {
        final avg = times.reduce((a, b) => a + b) / times.length;
        final min = times.reduce((a, b) => a < b ? a : b);
        final max = times.reduce((a, b) => a > b ? a : b);

        stats[entry.key] = PerformanceStats(
          operation: entry.key,
          averageTime: avg,
          minTime: min,
          maxTime: max,
          operationCount: times.length,
        );
      }
    }

    return stats;
  }

  static void clearStats() {
    _operationTimes.clear();
  }
}

class PerformanceStats {
  final String operation;
  final double averageTime;
  final int minTime;
  final int maxTime;
  final int operationCount;

  PerformanceStats({
    required this.operation,
    required this.averageTime,
    required this.minTime,
    required this.maxTime,
    required this.operationCount,
  });

  @override
  String toString() {
    return '$operation: avg=${averageTime.toStringAsFixed(2)}ms, '
           'min=$minTime ms, max=$maxTime ms, count=$operationCount';
  }
}

// 8. Usage Example
void demonstrateOptimizations() async {
  // Initialize efficient box manager
  await EfficientBoxManager.preloadBoxes(['users', 'products', 'orders']);

  // Create services
  final userService = PaginationService<User>('users', pageSize: 10);
  final userCache = HiveCacheService<User>('users');
  final searchService = AdvancedSearchService<User>('users');

  // Initialize search service
  await searchService.initialize();

  // Performance monitoring
  final stopwatch = Stopwatch()..start();

  // Use pagination
  final usersPage1 = await userService.getPage(0);
  stopwatch.stop();
  PerformanceMonitor.recordOperation('get_users_page', stopwatch.elapsedMilliseconds);

  // Use cache
  stopwatch.reset()..start();
  final cachedUser = await userCache.get('user_1');
  stopwatch.stop();
  PerformanceMonitor.recordOperation('get_user_cached', stopwatch.elapsedMilliseconds);

  // Use search
  stopwatch.reset()..start();
  final searchResults = await searchService.search(
    SearchQuery(
      searchTerm: 'john',
      filters: [
        Filter(field: 'isActive', type: FilterType.equals, value: true),
      ],
      sortBy: 'createdAt',
      limit: 20,
    ),
  );
  stopwatch.stop();
  PerformanceMonitor.recordOperation('search_users', stopwatch.elapsedMilliseconds);

  // Show performance stats
  final stats = PerformanceMonitor.getStats();
  for (final stat in stats.values) {
    print(stat);
  }
}

// 9. Widget Optimization
class OptimizedListView<T> extends StatefulWidget {
  final List<T> items;
  final Widget Function(BuildContext, T) itemBuilder;
  final ScrollController? controller;
  final bool lazyLoading;

  const OptimizedListView({
    Key? key,
    required this.items,
    required this.itemBuilder,
    this.controller,
    this.lazyLoading = true,
  }) : super(key: key);

  @override
  _OptimizedListViewState<T> createState() => _OptimizedListViewState<T>();
}

class _OptimizedListViewState<T> extends State<OptimizedListView<T>> {
  late final ScrollController _scrollController;
  late final Set<int> _visibleIndices = {};

  @override
  void initState() {
    super.initState();
    _scrollController = widget.controller ?? ScrollController();
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() {
    // Implement visibility tracking for lazy loading
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _scrollController,
      itemCount: widget.items.length,
      itemBuilder: (context, index) {
        return _buildOptimizedItem(context, index);
      },
    );
  }

  Widget _buildOptimizedContext(BuildContext context, int index) {
    if (_visibleIndices.contains(index)) {
      return widget.itemBuilder(context, widget.items[index]);
    }

    // Return placeholder for off-screen items
    return SizedBox(
      height: 60.0, // Estimated item height
      child: Container(),
    );
  }

  Widget _buildOptimizedItem(BuildContext context, int index) {
    return RepaintBoundary(
      key: ValueKey(widget.items[index]),
      child: widget.itemBuilder(context, widget.items[index]),
    );
  }
}

// 10. Memory Management
class MemoryManager {
  static void cleanupOldCache() async {
    // Clean up items older than 1 hour
    final cutoff = DateTime.now().subtract(const Duration(hours: 1));

    // This would be implemented based on your cache strategy
  }

  static Future<void> compactDatabase() async {
    final boxes = ['users', 'products', 'orders'];
    for (final boxName in boxes) {
      final box = await Hive.openBox(boxName);
      await box.compact();
    }
  }
}

export {
  EfficientBoxManager,
  PaginationService,
  AdvancedSearchService,
  HiveCacheService,
  BackgroundSyncService,
  DataValidator,
  PerformanceMonitor,
  OptimizedListView,
  MemoryManager,
};

💻 Synchronisation en Temps Réel Hive dart

🔴 complex ⭐⭐⭐⭐⭐

Synchronisation de données en temps réel avec APIs REST, support hors connexion et résolution de conflits

⏱️ 70 min 🏷️ flutter, hive, sync, realtime, offline
Prerequisites: Flutter, Hive, HTTP requests, Async programming
// Hive Real-time Synchronization with REST API
// Offline-first architecture with automatic sync and conflict resolution

import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';

// 1. Sync Configuration
class SyncConfiguration {
  final String baseUrl;
  final String apiKey;
  final Duration syncInterval;
  final Duration timeout;
  final int maxRetries;
  final bool enableAutoSync;
  final Map<String, String> headers;

  const SyncConfiguration({
    required this.baseUrl,
    required this.apiKey,
    this.syncInterval = const Duration(minutes: 5),
    this.timeout = const Duration(seconds: 30),
    this.maxRetries = 3,
    this.enableAutoSync = true,
    Map<String, String>? headers,
  }) : headers = headers ?? {};
}

// 2. Sync Status
enum SyncStatus {
  idle,
  syncing,
  success,
  error,
  conflict,
  offline
}

class SyncResult {
  final bool success;
  final String? message;
  final DateTime timestamp;
  final int syncedItems;
  final List<SyncConflict> conflicts;
  final List<SyncError> errors;

  const SyncResult({
    required this.success,
    this.message,
    required this.timestamp,
    this.syncedItems = 0,
    this.conflicts = const [],
    this.errors = const [],
  });

  bool get hasConflicts => conflicts.isNotEmpty;
  bool get hasErrors => errors.isNotEmpty;
}

class SyncConflict {
  final String entityId;
  final String entityType;
  final String localData;
  final String remoteData;
  final ConflictType type;
  final DateTime timestamp;

  const SyncConflict({
    required this.entityId,
    required this.entityType,
    required this.localData,
    required this.remoteData,
    required this.type,
    required this.timestamp,
  });
}

enum ConflictType {
  updateConflict,
  deleteConflict,
  createConflict,
}

class SyncError {
  final String entityId;
  final String entityType;
  final String error;
  final StackTrace? stackTrace;
  final DateTime timestamp;

  const SyncError({
    required this.entityId,
    required this.entityType,
    required this.error,
    this.stackTrace,
    required this.timestamp,
  });
}

// 3. Generic Sync Entity
abstract class SyncEntity {
  String get id;
  String get entityType;
  DateTime get lastModified;
  Map<String, dynamic> toJson();

  // Sync metadata
  String? syncId;
  DateTime? lastSyncAt;
  bool isDeleted = false;
  bool needsSync = true;

  // Conflict resolution
  bool autoResolve(SyncEntity remoteEntity);
  SyncEntity mergeWith(SyncEntity other);
}

// 4. Concrete Entity Example
@HiveType(typeId: 0)
class SyncUser extends HiveObject implements SyncEntity {
  @HiveField(0)
  late String id;

  @HiveField(1)
  late String name;

  @HiveField(2)
  late String email;

  @HiveField(3)
  late DateTime lastModified;

  @HiveField(4)
  String? syncId;

  @HiveField(5)
  DateTime? lastSyncAt;

  @HiveField(6)
  bool isDeleted = false;

  @HiveField(7)
  bool needsSync = true;

  @override
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'lastModified': lastModified.toIso8601String(),
      'syncId': syncId,
      'lastSyncAt': lastSyncAt?.toIso8601String(),
      'isDeleted': isDeleted,
      'needsSync': needsSync,
    };
  }

  factory SyncUser.fromJson(Map<String, dynamic> json) {
    final user = SyncUser();
    user.id = json['id'];
    user.name = json['name'];
    user.email = json['email'];
    user.lastModified = DateTime.parse(json['lastModified']);
    user.syncId = json['syncId'];
    user.lastSyncAt = json['lastSyncAt'] != null
        ? DateTime.parse(json['lastSyncAt'])
        : null;
    user.isDeleted = json['isDeleted'] ?? false;
    user.needsSync = json['needsSync'] ?? true;
    return user;
  }

  @override
  bool autoResolve(SyncEntity remoteEntity) {
    if (remoteEntity is! SyncUser) return false;

    final remoteUser = remoteEntity as SyncUser;

    // Auto-resolve by using the most recent version
    if (lastModified.isAfter(remoteUser.lastModified)) {
      return true; // Keep local version
    } else {
      return false; // Keep remote version (will be merged)
    }
  }

  @override
  SyncEntity mergeWith(SyncEntity other) {
    if (other is! SyncUser) return this;

    final remoteUser = other as SyncUser;

    // Merge logic: prefer remote data but keep some local fields
    name = remoteUser.name;
    email = remoteUser.email;
    lastModified = remoteUser.lastModified;
    isDeleted = remoteUser.isDeleted;

    return this;
  }
}

// 5. HTTP API Client
class ApiClient {
  final SyncConfiguration config;
  late final http.Client _client;

  ApiClient(this.config) {
    _client = http.Client();
  }

  Future<http.Response> get(String endpoint, {Map<String, String>? queryParams}) async {
    final uri = _buildUri(endpoint, queryParams);
    final response = await _client.get(
      uri,
      headers: _buildHeaders(),
    ).timeout(config.timeout);

    return _handleResponse(response);
  }

  Future<http.Response> post(String endpoint, {Map<String, dynamic>? body}) async {
    final uri = _buildUri(endpoint);

    final response = await _client.post(
      uri,
      headers: _buildHeaders(),
      body: body != null ? jsonEncode(body) : null,
    ).timeout(config.timeout);

    return _handleResponse(response);
  }

  Future<http.Response> put(String endpoint, {Map<String, dynamic>? body}) async {
    final uri = _buildUri(endpoint);

    final response = await _client.put(
      uri,
      headers: _buildHeaders(),
      body: body != null ? jsonEncode(body) : null,
    ).timeout(config.timeout);

    return _handleResponse(response);
  }

  Future<http.Response> delete(String endpoint) async {
    final uri = _buildUri(endpoint);

    final response = await _client.delete(
      uri,
      headers: _buildHeaders(),
    ).timeout(config.timeout);

    return _handleResponse(response);
  }

  Uri _buildUri(String endpoint, [Map<String, String>? queryParams]) {
    var uri = Uri.parse('${config.baseUrl}${endpoint}');

    if (queryParams != null) {
      uri = uri.replace(queryParameters: queryParams);
    }

    return uri;
  }

  Map<String, String> _buildHeaders() {
    final headers = <String, String>{
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ${config.apiKey}',
      ...config.headers,
    };

    return headers;
  }

  http.Response _handleResponse(http.Response response) {
    if (response.statusCode >= 400) {
      throw ApiException(
        'API Error: ${response.statusCode} - ${response.reasonPhrase}',
        response.statusCode,
        response.body,
      );
    }

    return response;
  }
}

class ApiException implements Exception {
  final String message;
  final int statusCode;
  final dynamic body;

  const ApiException(this.message, this.statusCode, this.body);

  @override
  String toString() => 'ApiException: $message';
}

// 6. Sync Service
class SyncService {
  final SyncConfiguration config;
  final ApiClient _apiClient;
  final Map<Type, EntitySyncHandler> _handlers;

  SyncStatus _status = SyncStatus.idle;
  Timer? _syncTimer;
  final StreamController<SyncStatus> _statusController = StreamController<SyncStatus>.broadcast();
  final StreamController<SyncResult> _resultController = StreamController<SyncResult>.broadcast();

  // Cache for sync state
  final Map<String, DateTime> _lastSyncTimes = {};
  final Map<String, bool> _syncLocks = {};

  SyncService(this.config) : _apiClient = ApiClient(config) {
    _handlers = {};
    _registerDefaultHandlers();
  }

  Stream<SyncStatus> get statusStream => _statusController.stream;
  Stream<SyncResult> get resultStream => _resultController.stream;

  SyncStatus get currentStatus => _status;

  void _registerDefaultHandlers() {
    // Register handlers for different entity types
    registerHandler<SyncUser>(UserSyncHandler(_apiClient));
    // Add more handlers as needed
  }

  void registerHandler<T extends SyncEntity>(EntitySyncHandler<T> handler) {
    _handlers[T] = handler;
  }

  Future<void> startAutoSync() async {
    if (!config.enableAutoSync) return;

    _syncTimer?.cancel();
    _syncTimer = Timer.periodic(config.syncInterval, (_) async {
      if (_status == SyncStatus.idle) {
        await performFullSync();
      }
    });
  }

  void stopAutoSync() {
    _syncTimer?.cancel();
    _syncTimer = null;
  }

  Future<SyncResult> performFullSync() async {
    _setStatus(SyncStatus.syncing);

    try {
      final conflicts = <SyncConflict>[];
      final errors = <SyncError>[];
      int syncedItems = 0;

      // Process each entity type
      for (final handler in _handlers.values) {
        final result = await handler.syncAll();

        syncedItems += result.syncedItems;
        conflicts.addAll(result.conflicts);
        errors.addAll(result.errors);
      }

      final syncResult = SyncResult(
        success: errors.isEmpty && conflicts.isEmpty,
        timestamp: DateTime.now(),
        syncedItems: syncedItems,
        conflicts: conflicts,
        errors: errors,
        message: 'Sync completed',
      );

      _setStatus(SyncStatus.success);
      _resultController.add(syncResult);

      return syncResult;

    } catch (e) {
      _setStatus(SyncStatus.error);

      final syncResult = SyncResult(
        success: false,
        timestamp: DateTime.now(),
        errors: [
          SyncError(
            entityId: 'global',
            entityType: 'system',
            error: e.toString(),
            stackTrace: e.stackTrace,
            timestamp: DateTime.now(),
          ),
        ],
      );

      _resultController.add(syncResult);
      return syncResult;
    }
  }

  Future<SyncResult> syncEntity<T extends SyncEntity>(T entity) async {
    if (!_handlers.containsKey(T)) {
      throw UnsupportedEntityType(T.toString());
    }

    _setStatus(SyncStatus.syncing);

    try {
      final handler = _handlers[T] as EntitySyncHandler<T>;
      final result = await handler.syncEntity(entity);

      _setStatus(SyncStatus.success);
      _resultController.add(result);

      return result;

    } catch (e) {
      _setStatus(SyncStatus.error);

      final syncResult = SyncResult(
        success: false,
        timestamp: DateTime.now(),
        errors: [
          SyncError(
            entityId: entity.id,
            entityType: entity.entityType,
            error: e.toString(),
            stackTrace: e.stackTrace,
            timestamp: DateTime.now(),
          ),
        ],
      );

      _resultController.add(syncResult);
      return syncResult;
    }
  }

  Future<SyncResult> resolveConflict(String entityId, ConflictResolution resolution) async {
    // Find the conflicting entity
    for (final handler in _handlers.values) {
      final result = await handler.resolveConflict(entityId, resolution);
      if (result != null) {
        return result;
      }
    }

    return SyncResult(
      success: false,
      timestamp: DateTime.now(),
      message: 'Conflict not found for entity: $entityId',
    );
  }

  void _setStatus(SyncStatus status) {
    _status = status;
    _statusController.add(status);
  }

  void dispose() {
    stopAutoSync();
    _statusController.close();
    _resultController.close();
  }
}

// 7. Entity Sync Handler
abstract class EntitySyncHandler<T extends SyncEntity> {
  final ApiClient _apiClient;
  final String endpoint;

  EntitySyncHandler(this._apiClient, this.endpoint);

  Future<SyncResult> syncAll() async {
    final conflicts = <SyncConflict>[];
    final errors = <SyncError>[];
    int syncedItems = 0;

    try {
      // Get all local entities that need sync
      final localEntities = await getLocalEntities();
      final remoteEntities = await getRemoteEntities();

      // Process local entities
      for (final entity in localEntities) {
        if (entity.needsSync) {
          try {
            final result = await syncEntity(entity);
            syncedItems += result.syncedItems;
            conflicts.addAll(result.conflicts);
            errors.addAll(result.errors);
          } catch (e) {
            errors.add(SyncError(
              entityId: entity.id,
              entityType: entity.entityType,
              error: e.toString(),
              timestamp: DateTime.now(),
            ));
          }
        }
      }

      // Process remote entities (for deletions and missing local entities)
      for (final remoteEntity in remoteEntities) {
        final localEntity = localEntities.cast<T?>().firstWhere(
          (e) => e?.id == remoteEntity.id,
          orElse: () => null,
        );

        if (localEntity == null) {
          // Remote entity doesn't exist locally, create it
          try {
            final entity = await createFromRemote(remoteEntity);
            syncedItems++;
          } catch (e) {
            errors.add(SyncError(
              entityId: remoteEntity.id,
              entityType: remoteEntity.entityType,
              error: e.toString(),
              timestamp: DateTime.now(),
            ));
          }
        }
      }

    } catch (e) {
      errors.add(SyncError(
        entityId: 'all',
        entityType: endpoint,
        error: e.toString(),
        timestamp: DateTime.now(),
      ));
    }

    return SyncResult(
      success: errors.isEmpty && conflicts.isEmpty,
      timestamp: DateTime.now(),
      syncedItems: syncedItems,
      conflicts: conflicts,
      errors: errors,
    );
  }

  Future<SyncResult> syncEntity(T entity) async {
    try {
      if (entity.isDeleted) {
        return await deleteRemote(entity);
      } else {
        return await upsertRemote(entity);
      }
    } catch (e) {
      return SyncResult(
        success: false,
        timestamp: DateTime.now(),
        errors: [
          SyncError(
            entityId: entity.id,
            entityType: entity.entityType,
            error: e.toString(),
            timestamp: DateTime.now(),
          ),
        ],
      );
    }
  }

  Future<SyncResult?> resolveConflict(String entityId, ConflictResolution resolution) async {
    try {
      // Get the conflicting entities
      final localEntity = await getLocalEntity(entityId);
      final remoteEntity = await getRemoteEntity(entityId);

      if (localEntity == null || remoteEntity == null) {
        return null;
      }

      switch (resolution) {
        case ConflictResolution.useLocal:
          // Force upload local version
          await forceUpload(localEntity);
          return SyncResult(success: true, timestamp: DateTime.now());

        case ConflictResolution.useRemote:
          // Force download remote version
          await forceDownload(remoteEntity);
          return SyncResult(success: Future.success: true, timestamp: DateTime.now());

        case ConflictResolution.merge:
          // Merge entities
          final mergedEntity = localEntity.mergeWith(remoteEntity);
          await updateLocal(mergedEntity);
          await forceUpload(mergedEntity);
          return SyncResult(success: true, timestamp: DateTime.now());
      }

    } catch (e) {
      return SyncResult(
        success: false,
        timestamp: DateTime.now(),
        errors: [
          SyncError(
            entityId: entityId,
            entityType: endpoint,
            error: e.toString(),
            timestamp: DateTime.now(),
          ),
        ],
      );
    }
  }

  Future<SyncResult> upsertRemote(T entity) async {
    try {
      final remoteEntity = await getRemoteEntity(entity.id);

      if (remoteEntity == null) {
        // Create new remote entity
        await createRemote(entity);
      } else {
        // Update existing remote entity
        await updateRemote(entity);
      }

      // Update sync metadata
      entity.syncId = _generateSyncId();
      entity.lastSyncAt = DateTime.now();
      entity.needsSync = false;
      await updateLocal(entity);

      return SyncResult(success: true, timestamp: DateTime.now());

    } catch (e) {
      return SyncResult(
        success: false,
        timestamp: DateTime.now(),
        errors: [
          SyncError(
            entityId: entity.id,
            entityType: entity.entityType,
            error: e.toString(),
            timestamp: DateTime.now(),
          ),
        ],
      );
    }
  }

  Future<SyncResult> deleteRemote(T entity) async {
    try {
      await _apiClient.delete('$endpoint/${entity.id}');

      // Update local entity metadata
      entity.isDeleted = true;
      entity.lastSyncAt = DateTime.now();
      entity.needsSync = false;
      await updateLocal(entity);

      return SyncResult(success: true, timestamp: DateTime.now());

    } catch (e) {
      return SyncResult(
        success: false,
        timestamp: DateTime.now(),
        errors: [
          SyncError(
            entityId: entity.id,
            entityType: entity.entityType,
            error: e.toString(),
            timestamp: DateTime.now(),
          ),
        ],
      );
    }
  }

  Future<SyncResult> forceUpload(T entity) async {
    // Clear sync metadata to force upload
    entity.syncId = null;
    entity.needsSync = true;

    return await upsertRemote(entity);
  }

  Future<SyncResult> forceDownload(T remoteEntity) async {
    try {
      final localEntity = await createFromRemote(remoteEntity);
      localEntity.syncId = _generateSyncId();
      localEntity.lastSyncAt = DateTime.now();
      localEntity.needsSync = false;

      return SyncResult(
        success: true,
        timestamp: DateTime.now(),
        syncedItems: 1,
      );

    } catch (e) {
      return SyncResult(
        success: false,
        timestamp: DateTime.now(),
        errors: [
          SyncError(
            entityId: remoteEntity.id,
            entityType: remoteEntity.entityType,
            error: e.toString(),
            timestamp: DateTime.now(),
          ),
        ],
      );
    }
  }

  // Abstract methods to be implemented by concrete handlers
  Future<List<T>> getLocalEntities();
  Future<T?> getLocalEntity(String id);
  Future<void> updateLocal(T entity);
  Future<void> createLocal(T entity);
  Future<void> deleteLocal(String id);

  Future<List<T>> getRemoteEntities() async {
    final response = await _apiClient.get(endpoint);
    final List<dynamic> data = jsonDecode(response.body);
    return data.map((item) => createFromJson(item)).cast<T>();
  }

  Future<T?> getRemoteEntity(String id) async {
    try {
      final response = await _apiClient.get('$endpoint/$id');
      final data = jsonDecode(response.body);
      return createFromJson(data);
    } on ApiException catch (e) {
      if (e.statusCode == 404) {
        return null; // Entity not found
      }
      rethrow;
    }
  }

  Future<void> createRemote(T entity) async {
    await _apiClient.post(endpoint, body: entity.toJson());
  }

  Future<void> updateRemote(T entity) async {
    await _apiClient.put('$endpoint/${entity.id}', body: entity.toJson());
  }

  Future<T> createFromJson(Map<String, dynamic> json) async {
    throw UnimplementedError('createFromJson must be implemented by concrete handlers');
  }

  Future<T> createFromRemote(T remoteEntity) async {
    return createFromJson(remoteEntity.toJson());
  }

  String _generateSyncId() {
    return DateTime.now().millisecondsSinceEpoch.toString();
  }
}

// 8. User Sync Handler
class UserSyncHandler extends EntitySyncHandler<SyncUser> {
  UserSyncHandler(super.apiClient, 'users');

  @override
  Future<List<SyncUser>> getLocalEntities() async {
    final box = await Hive.openBox<SyncUser>('users');
    return box.values.where((user) => !user.isDeleted).toList();
  }

  @override
  Future<SyncUser?> getLocalEntity(String id) async {
    final box = await Hive.openBox<SyncUser>('users');
    return box.get(id);
  }

  @override
  Future<void> updateLocal(SyncUser entity) async {
    final box = await Hive.openBox<SyncUser>('users');
    await box.put(entity);
  }

  @override
  Future<void> createLocal(SyncUser entity) async {
    final box = await Hive.openBox<SyncUser>('users');
    await box.put(entity);
  }

  @override
  Future<void> deleteLocal(String id) async {
    final box = await Hive.openBox<SyncUser>('users');
    await box.delete(id);
  }

  @override
  Future<SyncUser> createFromJson(Map<String, dynamic> json) async {
    return SyncUser.fromJson(json);
  }
}

// 9. Conflict Resolution Strategies
enum ConflictResolution {
  useLocal,
  useRemote,
  merge,
}

// 10. Sync Manager - High-level interface
class SyncManager {
  final Map<Type, DatabaseService> _databaseServices;
  late final SyncService _syncService;
  late final ConflictResolutionStrategy _conflictStrategy;

  SyncManager(
    SyncConfiguration syncConfig,
    this._databaseServices, {
    ConflictResolutionStrategy conflictStrategy = ConflictResolutionStrategy.auto,
  })  : _conflictStrategy = conflictStrategy {
    _syncService = SyncService(syncConfig);
  }

  Future<void> initialize() async {
    // Initialize database services
    for (final service in _databaseServices.values) {
      await service.initialize();
    }

    // Register sync handlers
    _registerSyncHandlers();

    // Start auto sync if enabled
    await _syncService.startAutoSync();
  }

  void _registerSyncHandlers() {
    for (final entry in _databaseServices.entries) {
      _syncService.registerHandler(entry.key, entry.value.createSyncHandler(_syncService._apiClient));
    }
  }

  Future<SyncResult> syncAll() async {
    return await _syncService.performFullSync();
  }

  Future<SyncResult> syncEntity<T extends SyncEntity>(T entity) async {
    final result = await _syncService.syncEntity(entity);

    if (result.hasConflicts) {
      return await _handleConflicts(result);
    }

    return result;
  }

  Future<SyncResult> _handleConflicts(SyncResult result) async {
    for (final conflict in result.conflicts) {
      ConflictResolution? resolution;

      switch (_conflictStrategy) {
        case ConflictResolutionStrategy.auto:
          resolution = await _autoResolveConflict(conflict);
          break;
        case ConflictResolutionStrategy.manual:
          resolution = await _promptForResolution(conflict);
          break;
        case ConflictResolutionStrategy.local:
          resolution = ConflictResolution.useLocal;
          break;
        case ConflictResolutionStrategy.remote:
          resolution = ConflictResolution.useRemote;
          break;
      }

      if (resolution != null) {
        final resolutionResult = await _syncService.resolveConflict(conflict.entityId, resolution);
        // Update result with resolution outcome
      }
    }

    return result;
  }

  Future<ConflictResolution?> _autoResolveConflict(SyncConflict conflict) async {
    final localEntity = await _getEntity(conflict.entityId, conflict.entityType);

    if (localEntity?.autoResolve(_createFromJson(jsonDecode(conflict.remoteData))) ?? false) {
      return ConflictResolution.useLocal;
    }

    return ConflictResolution.merge;
  }

  Future<ConflictResolution?> _promptForResolution(SyncConflict conflict) async {
    // In a real app, this would show a UI dialog
    // For now, return auto-resolution
    return _autoResolveConflict(conflict);
  }

  Future<T?> _getEntity<T>(String id, String entityType) async {
    final service = _databaseServices[T];
    return await service.getEntity(id);
  }

  Stream<SyncStatus> get statusStream => _syncService.statusStream;
  Stream<SyncResult> get resultStream => _syncService.resultStream;

  void dispose() {
    _syncService.dispose();
  }
}

// 11. Database Service Interface
abstract class DatabaseService<T> {
  Future<void> initialize();
  Future<T?> getEntity(String id);
  Future<void> saveEntity(T entity);
  Future<void> deleteEntity(String id);
  Future<List<T>> getAllEntities();
  EntitySyncHandler<T> createSyncHandler(ApiClient apiClient);
}

// 12. Usage Example
void demonstrateRealtimeSync() async {
  // Configuration
  final syncConfig = SyncConfiguration(
    baseUrl: 'https://api.example.com/v1',
    apiKey: 'your-api-key',
    syncInterval: Duration(minutes: 5),
    enableAutoSync: true,
  );

  // Create sync manager
  final syncManager = SyncManager(
    syncConfig,
    {
      SyncUser: UserService(),
      // Add more services as needed
    },
  );

  // Initialize
  await syncManager.initialize();

  // Listen for sync status
  syncManager.statusStream.listen((status) {
    print('Sync status: $status');
  });

  // Listen for sync results
  syncManager.resultStream.listen((result) {
    print('Sync result: ${result.success ? 'Success' : 'Failed'}');
    if (result.hasConflicts) {
      print('Conflicts: ${result.conflicts.length}');
    }
  });

  // Perform manual sync
  final syncResult = await syncManager.syncAll();
  print('Manual sync completed: ${syncResult.syncedItems} items synced');
}

export {
  SyncService,
  SyncEntity,
  SyncConfiguration,
  SyncStatus,
  SyncResult,
  SyncConflict,
  SyncUser,
  ConflictResolution,
  SyncManager,
  RealtimeSyncManager,
};