Provider: cos'è, il gestore dello stato delle app Flutter

Autore: IT Sectr Pubblicato: 2026-02-19 Tempo di lettura: 7 min

Provider — un pacchetto di gestione dello stato per Flutter, creato da Remi Rousselet nel 2019 come wrapper su InheritedWidget. Provider risolve il problema del passaggio di dati lungo l'albero dei widget senza props drilling: qualsiasi widget può accedere allo stato tramite context.read<T>() o context.watch<T>(). Secondo pub.dev, Provider è il gestore di stato Flutter più popolare con oltre 25 mila like.

Punti chiave

  • ChangeNotifier — una classe con notifyListeners per notificare gli iscritti dei cambiamenti
  • Consumer — un widget che si ricostruisce quando ChangeNotifier cambia
  • MultiProvider — composizione di più Provider senza annidamento
  • ProxyProvider — un Provider che dipende da un altro Provider
  • context.read / context.watch — metodi per leggere lo stato senza e con iscrizione

Cos'è Provider?

Provider — un pacchetto per la gestione dello stato e l'iniezione delle dipendenze in Flutter, costruito sopra InheritedWidget. Provider fornisce un oggetto (stato, servizio, repository) nell'albero dei widget e ricostruisce automaticamente l'UI quando i dati cambiano. A differenza dell'uso diretto di InheritedWidget, Provider elimina tutto il boilerplate: non è necessario scrivere una sottoclasse di InheritedWidget, configurare un metodo statico of() o gestire l'annidamento.

Provider è il metodo ufficialmente raccomandato da Google per gestire lo stato in Flutter (Flutter Team, 2019-2023). Il pacchetto fa parte dell'Ecosistema Flutter ed è mantenuto dal team di Flutter. Al suo lancio, Provider è stato proposto come sostituto delle variabili globali e di InheritedWidget: qualsiasi oggetto è accessibile da qualsiasi punto senza essere passato attraverso il costruttore.

Secondo il Sondaggio della Comunità Flutter 2025, Provider è utilizzato nel 72% delle applicazioni Flutter. Le ragioni principali della sua popolarità sono: soglia di ingresso minima, supporto integrato di ChangeNotifier, compatibilità con altre architetture (MVVM, BLoC) e assenza di dipendenze esterne.

ChangeNotifier e notifyListeners

ChangeNotifier — una classe integrata di Flutter che implementa il pattern Listener. ChangeNotifier notifica gli iscritti dei cambiamenti chiamando notifyListeners(). Nel contesto di Provider, ChangeNotifier è la classe principale per lo stato: si crea una classe che estende ChangeNotifier con campi e metodi che chiamano notifyListeners() dopo aver modificato i dati.

Dart
class CounterProvider extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

Regole di notifyListeners: chiamarlo dopo aver modificato completamente i dati — non a metà del metodo, ma alla fine. Se un metodo esegue più modifiche, chiama notifyListeners() una volta dopo tutte le modifiche, non dopo ciascuna. Questo evita molteplici ridisegni in un singolo passo logico. Per aggiornamenti batch, usa notifyListeners insieme a pattern simili a setState.

Alternative a ChangeNotifier: ValueNotifier — per un singolo valore (buono per i primitivi), StateNotifier — dal pacchetto state_notifier (raramente usato da solo). La maggior parte delle soluzioni Provider usa ChangeNotifier grazie al suo supporto integrato e alla sua semplicità.

Consumer e context.watch

Consumer — un widget che si iscrive a ChangeNotifier e si ricostruisce ad ogni chiamata di notifyListeners(). Consumer accetta una funzione builder con tre parametri: context, model, child. Child — un widget che non dipende dal modello e che Consumer non ricostruisce. Questa è un'ottimizzazione: se Consumer contiene un widget statico (icona, testo senza dati), viene passato tramite child e non viene ricreato.

Dart
Consumer<CounterProvider>(
  builder: (context, provider, child) => Column(
    children: [
      child!, // non viene ricostruito
      Text('${provider.count}'),
      ElevatedButton(
        onPressed: () => provider.increment(),
        child: Icon(Icons.add),
      ),
    ],
  ),
  child: Text('Contatore:'),
)

context.watch — un metodo di estensione di BuildContext per iscriversi a un Provider. Restituisce il modello e iscrive il widget corrente ai suoi cambiamenti. context.read — accesso senza iscrizione (per gestori onPressed, initState e dispose). context.select — iscrizione a un campo specifico del modello senza ricostruire quando altri campi cambiano. Select è l'opzione più performante per modelli complessi con 10+ campi.

Quando usare Consumer, watch o select: Consumer — quando un widget child è necessario per l'ottimizzazione. watch — nel metodo build per una lettura semplice. select — quando il modello ha più campi ma il widget dipende solo da uno. Provider si disiscrive automaticamente alla distruzione del widget, prevenendo perdite di memoria.

MultiProvider e ProxyProvider

MultiProvider — un widget per registrare più Provider senza annidamento. Invece di un albero con 5 livelli di Provider → Provider → Provider, MultiProvider accetta una lista di fornitori. Ogni Provider successivo può usare i precedenti tramite il costruttore. MultiProvider è il modo standard per organizzare il livello radice di un'applicazione.

Dart
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => CartProvider()),
    ChangeNotifierProvider(create: (_) => AuthProvider()),
    ProxyProvider<AuthProvider, OrderProvider>(
      update: (_, auth, __) => OrderProvider(auth.userId),
    ),
  ],
  child: MaterialApp(home: HomePage()),
)

ProxyProvider — un Provider che dipende da un altro Provider. ProxyProvider ottiene valori da altri Provider e li passa al suo oggetto. Ad esempio, OrderProvider dipende da AuthProvider (ha bisogno di userId). Quando AuthProvider cambia, ProxyProvider ricrea automaticamente OrderProvider con il nuovo userId. ChangeNotifierProxyProvider — la versione di ProxyProvider per ChangeNotifier.

StreamProvider e FutureProvider: StreamProvider si iscrive a uno Stream (Firebase, WebSocket) e aggiorna Consumer ad ogni nuovo evento. FutureProvider — per inizializzazione asincrona: esegue un Future, mostra il caricamento, poi passa il risultato ai widget. Entrambi risolvono compiti comuni senza gestione manuale delle iscrizioni.

Testare Provider

Provider si testa avvolgendo il widget in un MultiProvider con valori di test. Non è necessaria alcuna API o database reale per il test — il Provider viene sostituito con un oggetto mockato. Il pacchetto provider fornisce ProviderScope per l'isolamento dei test — ogni test crea il proprio albero Provider in modo indipendente.

Dart
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Counter increments on button tap',
    (tester) async {
      await tester.pumpWidget(
        ChangeNotifierProvider(
          create: (_) => CounterProvider(),
          child: CounterScreen(),
        ),
      );
      await tester.tap(find.byKey(Key('increment')));
      await tester.pump();
      expect(find.text('1'), findsOneWidget);
    },
  );
}

MockProvider: per testare widget con un Provider che dipende da un'API, crea una sottoclasse fittizia o usa mockito / mocktail. Provider non richiede strumenti di mock speciali — qualsiasi oggetto che estende ChangeNotifier può essere passato tramite create senza chiamare il servizio reale. Programma i Provider attraverso interfacce (classe astratta) per facilitare la sostituzione.

Provider: prestazioni e limiti

Le prestazioni di Provider si basano su InheritedWidget: quando un Provider cambia, tutti i widget iscritti tramite context.watch o Consumer vengono ricostruiti. Per evitare ridisegni inutili, usa context.select (iscrizione a un campo specifico), Consumer con il parametro child e const per widget statici. Provider non ricostruisce rami non iscritti ai cambiamenti.

MetodoIscrizioneRicostruzioneUtilizzo
context.watchModello completoQualsiasi cambiamentoWidget semplici
ConsumerModello completoQualsiasi cambiamentoCon ottimizzazione child
context.selectCampo specificoSolo quando il campo cambiaModelli complessi
context.readNoMaiGestori di eventi

Limiti: Provider non supporta l'isolamento della logica di business a livello di Evento (come BLoC). Tutte le modifiche avvengono tramite chiamate dirette ai metodi di ChangeNotifier, il che può portare a catene di modifiche incontrollate. Per scenari complessi (molteplici operazioni asincrone, validazione complessa), Provider è inferiore a BLoC e Riverpod.

Migrazione da Provider: Provider può essere facilmente combinato con altri pacchetti. Per migrare a Riverpod, usa ChangeNotifierProvider.adaptive — un adattatore che permette di usare ChangeNotifier esistenti con Riverpod senza riscrivere. Per BLoC — BlocProvider può essere posizionato all'interno di un albero Provider, sostituendo gradualmente ChangeNotifier con Bloc.

Domande frequenti

Qual è la differenza tra Provider e BLoC?

Provider — un wrapper su InheritedWidget per l'iniezione di dipendenze con ChangeNotifier. BLoC — un pattern architetturale con Event + Stream per l'isolamento della logica. Provider è più facile da imparare, BLoC struttura il codice in modo più rigoroso. Provider è adatto per applicazioni piccole e stato dell'UI, BLoC per logica di business complessa. Secondo Flutter Community 2025, entrambi sono spesso usati insieme nello stesso progetto.

Cos'è ChangeNotifierProvider?

ChangeNotifierProvider — un tipo di Provider per istanze di ChangeNotifier. Crea l'oggetto tramite create, lo fornisce ai discendenti e ricostruisce Consumer quando notifyListeners viene chiamato. ChangeNotifierProvider chiama automaticamente dispose su ChangeNotifier quando viene rimosso dall'albero. Esistono tre metodi di creazione: ChangeNotifierProvider.value (per un oggetto esistente), ChangeNotifierProvider (per creazione lazy) e ChangeNotifierProvider.create (per creazione lazy esplicita).

Come evitare ricostruzioni inutili con Provider?

Usa context.select invece di context.watch — il widget si ricostruisce solo quando il campo selezionato cambia. Dividi i ChangeNotifier grandi in diversi piccoli (un modello — una responsabilità). Usa Consumer child per le parti statiche. Per le liste, usa ListView.builder con chiavi. Provider DevTools (Flutter Inspector) mostra quali widget si stanno ricostruendo e perché.

Si può usare Provider senza ChangeNotifier?

Sì. Provider (senza ChangeNotifier) — per iniettare oggetti immutabili (repository, client API, configurazione). ValueListenableProvider — per ValueNotifier. StreamProvider — per Stream (Firebase, WebSocket). FutureProvider — per Future (caricare la configurazione all'avvio). ProxyProvider — per Provider che dipendono da altri Provider. ChangeNotifier è necessario solo per stato mutabile con aggiornamento dell'UI.

Cos'è ProviderNotFoundException?

ProviderNotFoundException — un'eccezione in fase di esecuzione che si verifica quando si tenta di ottenere un Provider che non è stato dichiarato più in alto nell'albero dei widget. Cause comuni: il Provider è dichiarato più in basso del widget che tenta di leggerlo; il Provider è dichiarato in una route e letto in un'altra; un errore di battitura nel tipo. Soluzione: sposta il Provider più in alto nell'albero o usa MultiProvider a livello di MaterialApp per dipendenze globali.

Riepilogo

  • Provider — un wrapper su InheritedWidget per la gestione dello stato e DI in Flutter
  • ChangeNotifier — una classe con notifyListeners per aggiornamenti reattivi dell'UI
  • Consumer e context.watch — iscrizione ai cambiamenti con ricostruzione automatica
  • MultiProvider — composizione di fornitori senza annidamento dell'albero
  • ProxyProvider — una catena di dipendenze tra Provider
  • context.select — iscrizione mirata a un campo specifico del modello
  • StreamProvider e FutureProvider — supporto integrato per dati reattivi e asincroni

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche