Skip to content

Advanced Topics

Introduction

This section covers advanced Bond concepts for experienced developers who want to extend the framework, optimize performance, implement complex patterns, or integrate with existing systems. These topics assume familiarity with Bond's core concepts and are designed for production applications with specific requirements.

Extending Bond Packages

Custom Cache Drivers

Create custom cache drivers for specialized storage needs:

// lib/core/cache/redis_cache_driver.dart
class RedisCacheDriver extends CacheDriver {
  final RedisConnection _redis;

  RedisCacheDriver(this._redis);

  @override
  bool has(String key) {
    try {
      return _redis.exists([key]).first > 0;
    } catch (e) {
      return false;
    }
  }

  @override
  Json? retrieve(String key) {
    try {
      final value = _redis.get(key);
      return value != null ? json.decode(value) : null;
    } catch (e) {
      return null;
    }
  }

  @override
  Future<bool> store(String key, Json data) async {
    try {
      await _redis.set(key, json.encode(data));
      return true;
    } catch (e) {
      return false;
    }
  }

  @override
  Future<bool> forget(String key) async {
    try {
      await _redis.del([key]);
      return true;
    } catch (e) {
      return false;
    }
  }

  @override
  Future<bool> flush() async {
    try {
      await _redis.flushdb();
      return true;
    } catch (e) {
      return false;
    }
  }
}

// Register in Service Provider
class CacheServiceProvider extends ServiceProvider {
  @override
  Future<void> register(GetIt it) async {
    if (AppConfig.useRedisCache) {
      final redis = await RedisConnection.connect('localhost', 6379);
      it.registerLazySingleton<CacheDriver>(
        () => RedisCacheDriver(redis),
        instanceName: 'redis',
      );
    }
  }
}

Custom Notification Providers

Implement custom notification providers for third-party services:

// lib/core/notifications/onesignal_provider.dart
class OneSignalNotificationProvider extends PushNotificationProvider {
  final OneSignal _oneSignal;

  OneSignalNotificationProvider(this._oneSignal);

  @override
  Future<void> initialize() async {
    await _oneSignal.setAppId(NotificationConfig.oneSignalAppId);

    _oneSignal.setNotificationWillShowInForegroundHandler((event) {
      onNotificationReceived(NotificationData.fromOneSignal(event.notification));
    });

    _oneSignal.setNotificationOpenedHandler((event) {
      onNotificationTapped(NotificationData.fromOneSignal(event.notification));
    });
  }

  @override
  Future<String?> getToken() async {
    final deviceState = await _oneSignal.getDeviceState();
    return deviceState?.userId;
  }

  @override
  Future<void> subscribeToTopic(String topic) async {
    await _oneSignal.sendTag('topic_$topic', 'true');
  }
}

Custom Analytics Providers

Integrate with analytics services beyond Firebase:

// lib/core/analytics/mixpanel_provider.dart
class MixpanelAnalyticsProvider implements AnalyticsProvider {
  final Mixpanel _mixpanel;

  MixpanelAnalyticsProvider(this._mixpanel);

  @override
  void log(AnalyticsEvent event) {
    if (event is UserLoggedIn) {
      _mixpanel.identify(event.id.toString());
      _mixpanel.getPeople().set('login_method', event.loginMethod);
    } else if (event is UserSignedUp) {
      _mixpanel.identify(event.id.toString());
      _mixpanel.getPeople().set({
        'signup_date': DateTime.now().toIso8601String(),
        'signup_method': event.signupMethod,
      });
    }

    _mixpanel.track(event.key, event.params);
  }

  @override
  void setUserId(String userId) {
    _mixpanel.identify(userId);
  }

  @override
  void setUserAttributes(Map<String, dynamic> attributes) {
    _mixpanel.getPeople().set(attributes);
  }
}

Performance Optimization

Lazy Loading Strategies

Implement lazy loading for better app startup performance:

class LazyServiceProvider extends ServiceProvider {
  @override
  Future<void> register(GetIt it) async {
    // Register factories that create instances only when needed
    it.registerLazySingleton<ExpensiveService>(() {
      print('Creating ExpensiveService instance...');
      return ExpensiveService();
    });

    // Use async factories for services that require async initialization
    it.registerSingletonAsync<DatabaseService>(() async {
      final db = DatabaseService();
      await db.initialize();
      return db;
    });
  }
}

Memory Management

Implement proper memory management for large datasets:

class MemoryManagedController extends StateNotifier<DataState> {
  static const int _maxCachedItems = 1000;
  final Map<String, dynamic> _memoryCache = {};
  final Queue<String> _accessOrder = Queue<String>();

  void cacheItem(String key, dynamic item) {
    // Remove oldest items if cache is full
    while (_memoryCache.length >= _maxCachedItems) {
      final oldestKey = _accessOrder.removeFirst();
      _memoryCache.remove(oldestKey);
    }

    _memoryCache[key] = item;
    _accessOrder.add(key);
  }

  dynamic getCachedItem(String key) {
    if (_memoryCache.containsKey(key)) {
      // Move to end of access order (LRU)
      _accessOrder.remove(key);
      _accessOrder.add(key);
      return _memoryCache[key];
    }
    return null;
  }

  @override
  void dispose() {
    _memoryCache.clear();
    _accessOrder.clear();
    super.dispose();
  }
}

Background Processing

Handle background tasks efficiently:

class BackgroundTaskService {
  static const String _taskQueueKey = 'background_tasks';

  Future<void> scheduleTask(BackgroundTask task) async {
    final tasks = await _getQueuedTasks();
    tasks.add(task);
    await Cache.put(_taskQueueKey, tasks);

    // Process immediately if app is active
    if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) {
      await _processTasks();
    }
  }

  Future<void> _processTasks() async {
    final tasks = await _getQueuedTasks();
    final completedTasks = <BackgroundTask>[];

    for (final task in tasks) {
      try {
        await task.execute();
        completedTasks.add(task);
      } catch (e) {
        // Log error but continue with other tasks
        print('Background task failed: $e');
      }
    }

    // Remove completed tasks
    tasks.removeWhere((task) => completedTasks.contains(task));
    await Cache.put(_taskQueueKey, tasks);
  }

  Future<List<BackgroundTask>> _getQueuedTasks() async {
    final cached = await Cache.get<List<dynamic>>(_taskQueueKey);
    return cached?.map((json) => BackgroundTask.fromJson(json)).toList() ?? [];
  }
}

Security Best Practices

Certificate Pinning

Implement certificate pinning for API security:

class SecureApiServiceProvider extends ServiceProvider {
  @override
  Future<void> register(GetIt it) async {
    final dio = Dio();

    // Add certificate pinning
    (dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate = (client) {
      client.badCertificateCallback = (cert, host, port) {
        // Verify certificate fingerprint
        final fingerprint = _getCertificateFingerprint(cert);
        return _trustedFingerprints.contains(fingerprint);
      };
      return client;
    };

    it.registerSingleton<Dio>(dio);
  }

  static const List<String> _trustedFingerprints = [
    'AA:BB:CC:DD:EE:FF:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE',
    // Add your API server's certificate fingerprint
  ];

  String _getCertificateFingerprint(X509Certificate cert) {
    final bytes = cert.der;
    final digest = sha256.convert(bytes);
    return digest.bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':').toUpperCase();
  }
}

Data Encryption

Encrypt sensitive data before storage:

class EncryptedStorage {
  final FlutterSecureStorage _secureStorage;
  final Encrypter _encrypter;

  EncryptedStorage(this._secureStorage, this._encrypter);

  Future<void> store(String key, String value) async {
    final encrypted = _encrypter.encrypt(value);
    await _secureStorage.write(key: key, value: encrypted.base64);
  }

  Future<String?> retrieve(String key) async {
    final encryptedValue = await _secureStorage.read(key: key);
    if (encryptedValue == null) return null;

    try {
      final encrypted = Encrypted.fromBase64(encryptedValue);
      return _encrypter.decrypt(encrypted);
    } catch (e) {
      return null;
    }
  }

  Future<void> delete(String key) async {
    await _secureStorage.delete(key: key);
  }
}

Next Steps

Continue exploring Bond's advanced capabilities: