Cosa sono i feature flags e perché usarli

I feature flags (o feature toggles) sono interruttori che permettono di attivare o disattivare funzionalità della tua app senza rilasciare una nuova versione sugli store. Sono utili per:

  • Rilasci graduali (rollout progressivo di una feature).
  • A/B testing su gruppi di utenti.
  • Kill switch per disattivare rapidamente una funzione problematica.
  • Personalizzazione dei contenuti in base al segmento di utenza.

In Flutter, uno degli strumenti più comodi per implementarli è Firebase Remote Config, che consente di definire valori sul server e leggerli dall'app in tempo reale.

Setup del progetto

Aggiungi le dipendenze necessarie al file pubspec.yaml:

dependencies:
  firebase_core: ^3.6.0
  firebase_remote_config: ^5.1.3

Assicurati di aver configurato Firebase nel progetto tramite flutterfire configure. Poi inizializza Firebase nel main:

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}

Configurare Remote Config

Creiamo un servizio dedicato che incapsula la logica di Remote Config. Impostiamo prima i valori di default, fondamentali per garantire un comportamento coerente quando l'app è offline o al primo avvio.

import 'package:firebase_remote_config/firebase_remote_config.dart';

class RemoteConfigService {
  final FirebaseRemoteConfig _rc = FirebaseRemoteConfig.instance;

  Future<void> init() async {
    await _rc.setConfigSettings(
      RemoteConfigSettings(
        fetchTimeout: const Duration(seconds: 10),
        minimumFetchInterval: const Duration(hours: 1),
      ),
    );

    // Valori di default usati finché non arriva il fetch dal server
    await _rc.setDefaults(const {
      'new_checkout_enabled': false,
      'welcome_message': 'Benvenuto!',
      'max_items_per_page': 20,
    });

    await _rc.fetchAndActivate();
  }

  bool get newCheckoutEnabled => _rc.getBool('new_checkout_enabled');
  String get welcomeMessage => _rc.getString('welcome_message');
  int get maxItemsPerPage => _rc.getInt('max_items_per_page');
}

minimumFetchInterval

Attenzione a minimumFetchInterval: in produzione un valore alto (es. 1 ora) evita di sovraccaricare il server e ridurre i costi. In sviluppo puoi impostarlo a Duration.zero per vedere subito le modifiche, ma ricorda che Firebase limita comunque il numero di fetch per evitare throttling.

Usare i flag nella UI

Una volta inizializzato il servizio, leggere un flag è immediato:

class CheckoutButton extends StatelessWidget {
  final RemoteConfigService config;
  const CheckoutButton({super.key, required this.config});

  @override
  Widget build(BuildContext context) {
    if (config.newCheckoutEnabled) {
      return const NewCheckoutFlow();
    }
    return const LegacyCheckoutFlow();
  }
}

Aggiornamenti in tempo reale con onConfigUpdated

Remote Config supporta un listener che notifica quando i valori cambiano sul server, permettendo aggiornamenti live senza riavviare l'app:

_rc.onConfigUpdated.listen((event) async {
  await _rc.activate();
  // Notifica la UI, ad esempio tramite un ValueNotifier o uno stream
  notifyListeners();
});

Questo è particolarmente potente per i kill switch: puoi disattivare una feature difettosa e vedere l'effetto sui dispositivi degli utenti in pochi secondi.

Integrazione con la gestione dello stato

Puoi esporre i flag tramite un ChangeNotifier o un provider per renderli reattivi. Ecco un esempio con ValueNotifier:

class FeatureFlags extends ChangeNotifier {
  final RemoteConfigService _service;
  FeatureFlags(this._service) {
    _service.init().then((_) => notifyListeners());
  }

  bool get newCheckout => _service.newCheckoutEnabled;
}

Best practice

  • Definisci sempre i default: garantiscono un'esperienza coerente offline e al primo avvio.
  • Usa nomi di chiavi descrittivi e versionati (es. feature_dark_mode_v2).
  • Non abusare dei flag: troppi toggle attivi rendono difficile testare tutte le combinazioni.
  • Pulisci i flag obsoleti: una volta che una feature è stabile, rimuovi il toggle dal codice.
  • Combina con Firebase A/B Testing per misurare l'impatto delle nuove funzionalità su metriche reali.

Conclusione

Firebase Remote Config offre un modo semplice e gratuito per introdurre feature flags nelle app Flutter, disaccoppiando il rilascio del codice dall'attivazione delle funzionalità. Con pochi passaggi puoi ottenere rollout controllati, A/B test e kill switch, migliorando sensibilmente la sicurezza e la flessibilità dei tuoi rilasci.