Flutter Interview Handbook

Dynamic Config & Feature Toggles

100%

Dynamic Config & Feature Toggles

Production mobile applications rely on Dynamic Remote Configuration and Feature Toggles (using tools like Firebase Remote Config, LaunchDarkly, or Flagsmith) to manage feature rollouts, enable instant emergency kill-switches, perform A/B testing, and decouple code deployments from feature releases.


1. Feature Toggle Use Cases

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚                      FEATURE TOGGLE CATEGORIES                         β”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
 β”‚ 1. Release Toggleβ”‚ 2. Kill Switch   β”‚ 3. Experiment   β”‚ 4. Permission  β”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
 β”‚ - Dark launch    β”‚ - Disable broken β”‚ - A/B testing   β”‚ - Premium vs   β”‚
 β”‚   new features   β”‚   payment gatewayβ”‚   checkout flow β”‚   free user    β”‚
 β”‚ - Phased % rolloutβ”‚   instantly      β”‚ - Measure conversion feature accessβ”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Remote Config Lifecycle (FirebaseRemoteConfig)

Firebase Remote Config operates across a 3-stage lifecycle:

  1. setDefaults(): In-memory local default values loaded instantly on app launch.
  2. fetch(): Fetches remote configurations from cloud servers and stores them in a local disk cache.
  3. activate(): Applies fetched values to the active runtime configuration used by the application.
import 'package:firebase_remote_config/firebase_remote_config.dart';

class RemoteConfigService {
  final FirebaseRemoteConfig _remoteConfig = FirebaseRemoteConfig.instance;

  Future<void> initialize() async {
    // 1. Set local fallback defaults
    await _remoteConfig.setDefaults(const {
      'enable_new_checkout': false,
      'max_upload_size_mb': 10,
      'banner_message': 'Welcome!',
    });

    // 2. Configure fetch settings (e.g. 1 hour cache in production)
    await _remoteConfig.setConfigSettings(RemoteConfigSettings(
      fetchTimeout: const Duration(seconds: 10),
      minimumFetchInterval: const Duration(hours: 1),
    ));

    // 3. Fetch & activate remote values asynchronously
    try {
      await _remoteConfig.fetchAndActivate();
    } catch (e) {
      print('Remote Config fetch failed; using local fallback defaults.');
    }
  }

  bool get isNewCheckoutEnabled => _remoteConfig.getBool('enable_new_checkout');
}

3. Real-Time Remote Config Listener

In Firebase Remote Config (Flutter 3.10+), real-time updates can be received instantly using addOnConfigUpdateListener:

void listenToRealtimeUpdates() {
  _remoteConfig.addOnConfigUpdateListener(
    onConfigUpdate: (configUpdate, error) async {
      // Activate new keys immediately when changed on backend console
      await _remoteConfig.activate();
      print('Updated keys: ${configUpdate.updatedKeys}');
    },
    onError: (error) => print('Config update error: $error'),
  );
}

4. Type-Safe Remote Config Wrapper Pattern

Avoid hardcoding string keys across UI widgets. Encapsulate Remote Config keys inside a strongly-typed service interface:

abstract class FeatureFlags {
  bool get isRedesignEnabled;
  int get maxCartItems;
}

class FirebaseFeatureFlags implements FeatureFlags {
  final FirebaseRemoteConfig _config;
  FirebaseFeatureFlags(this._config);

  @override
  bool get isRedesignEnabled => _config.getBool('is_redesign_enabled');

  @override
  int get maxCartItems => _config.getInt('max_cart_items');
}

5. Trade-offs & Production Considerations

  • UI Jarring & Mid-Session Changes: Activating new configurations mid-session (e.g. changing isRedesignEnabled while a user is actively interacting with a screen) causes jarring layout jumps. Apply new configs upon app restart or navigation boundaries.
  • Throttling Risks: Setting minimumFetchInterval to 0 in production triggers Firebase HTTP 429 throttling errors. Keep minimumFetchInterval to at least 1 hour in production (and 0 only during development).