InheritedWidget è un widget speciale in Flutter che passa dati verso il basso nell'albero dei widget senza passarli esplicitamente attraverso i costruttori. I widget figli accedono ai dati tramite BuildContext e si iscrivono automaticamente agli aggiornamenti. Quando i dati in InheritedWidget cambiano, tutti i widget dipendenti vengono ricostruiti. Secondo Flutter API Reference, 2025, InheritedWidget è alla base di Theme, MediaQuery, Localizations e della maggior parte delle librerie di gestione dello stato.
Punti chiave
InheritedWidget è un widget che rende i suoi dati disponibili a tutti i discendenti nell'albero Widget Tree. A differenza di un widget normale che passa dati solo attraverso i costruttori agli elementi figli, InheritedWidget permette a qualsiasi widget nel sottoalbero di accedere ai dati senza una catena di parametri. Questo risolve il problema del “prop drilling” — passare dati attraverso molti widget intermedi che non usano questi dati stessi.
Flutter include diversi InheritedWidget incorporati: Theme (schema di colori e stili), MediaQuery (dimensioni schermo, orientamento, densità pixel), Localizations (stringhe localizzate), Directionality (direzione del testo), DefaultTextStyle (stile testo predefinito). Questi widget sono impostati da widget radice come MaterialApp e sono disponibili in tutta l'applicazione.
InheritedWidget non ha uno stato proprio — memorizza i dati passati attraverso il costruttore. Quando il genitore di InheritedWidget viene ricostruito con nuovi dati, il metodo updateShouldNotify viene chiamato per confrontare dati vecchi e nuovi. Se il metodo restituisce true, tutti i widget dipendenti vengono marcati per la ricostruzione. Questo è un meccanismo di aggiornamento reattivo semplice ma efficace.
Il meccanismo di passaggio dati attraverso InheritedWidget si basa sull'Element Tree. Quando un widget chiama dependOnInheritedWidgetOfExactType, l'elemento corrispondente registra una dipendenza su InheritedElement. Quando InheritedWidget cambia, InheritedElement notifica tutti gli elementi dipendenti, che vengono ricostruiti nel fotogramma successivo.
Il metodo dependOnInheritedWidgetOfExactType non solo trova InheritedWidget nell'albero — iscrive l'elemento corrente alle notifiche. Se usassi findAncestorWidgetOfExactType invece di dependOn, il widget otterrebbe i dati ma non verrebbe ricostruito quando cambiano. Questa è una differenza importante: dependOn è un'iscrizione, findAncestor è una ricerca una tantum.
Quando un widget richiede un InheritedWidget, Flutter risale l'Element Tree dall'elemento corrente fino alla radice, controllando ogni InheritedElement per una corrispondenza di tipo. Il primo InheritedElement corrispondente viene restituito. Ciò significa che l'InheritedWidget più vicino nell'albero ha priorità — puoi sovrascrivere i dati a un livello specifico posizionando InheritedWidget più vicino ai discendenti.
class ThemeData {
final Color primaryColor;
final TextTheme textTheme;
const ThemeData({required this.primaryColor, required this.textTheme});
}
class MyTheme extends InheritedWidget {
final ThemeData data;
const MyTheme({required this.data, required Widget child}) : super(child: child);
static MyTheme of(BuildContext context) {
final widget = context.dependOnInheritedWidgetOfExactType<MyTheme>();
assert(widget != null, "MyTheme not found in tree");
return widget!;
}
@override
bool updateShouldNotify(MyTheme oldWidget) => oldWidget.data != data;
}
In questo esempio, MyTheme usa un metodo statico of per fornire dati ai discendenti. Il metodo dependOnInheritedWidgetOfExactType registra una dipendenza, e updateShouldNotify confronta dati vecchi e nuovi per determinare se i widget dipendenti devono essere ricostruiti.
Creare un InheritedWidget personalizzato consiste in due passaggi: definire una classe che estende InheritedWidget e implementare un metodo statico of per l'accesso dai discendenti. I dati vengono passati attraverso il costruttore e il metodo updateShouldNotify determina quando i widget dipendenti devono essere ricostruiti.
La classe deve estendere InheritedWidget e accettare dati attraverso un costruttore con un parametro child obbligatorio. I dati possono essere di qualsiasi tipo: primitivi, oggetti, funzioni. La regola principale è che i dati devono essere immutabili in modo che i valori vecchi e nuovi possano essere confrontati in modo affidabile.
Il metodo statico of prende BuildContext e restituisce i dati di InheritedWidget. Internamente, chiama dependOnInheritedWidgetOfExactType, che trova l'InheritedWidget più vicino del tipo specificato nell'albero. Se InheritedWidget non viene trovato, il metodo lancia un'eccezione o restituisce un valore predefinito a seconda dell'implementazione.
Per accedere ai dati, il widget chiama MyWidget.of(context) all'interno del metodo build. Flutter iscrive automaticamente il widget agli aggiornamenti. Se i dati cambiano, il widget viene ricostruito nel fotogramma successivo. Questo permette codice pulito e dichiarativo senza parametri non necessari.
class UserPreferences extends InheritedWidget {
final String languageCode;
final bool darkMode;
const UserPreferences({
required this.languageCode,
required this.darkMode,
required Widget child,
}) : super(child: child);
static UserPreferences of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserPreferences>()!;
}
@override
bool updateShouldNotify(UserPreferences oldWidget) =>
oldWidget.languageCode != languageCode || oldWidget.darkMode != darkMode;
}
In questo esempio, UserPreferences memorizza le preferenze dell'utente. Il metodo updateShouldNotify confronta ogni campo individualmente, prevenendo ricostruzioni non necessarie quando solo un parametro cambia. Usa un approccio simile per i tuoi InheritedWidget personalizzati con più campi.
updateShouldNotify è il metodo chiave di InheritedWidget che determina se i widget dipendenti devono essere notificati sui cambiamenti dei dati. Se il metodo restituisce false, i widget dipendenti non vengono ricostruiti, anche se InheritedWidget stesso ha ricevuto una nuova istanza con gli stessi dati. Questo è criticamente importante per le prestazioni.
Confronta solo i campi che sono effettivamente cambiati e influenzano la visualizzazione. Se InheritedWidget contiene 10 campi ma solo uno influenza l'interfaccia utente, controlla solo quel campo. Per le collezioni, usa un confronto profondo o strutture dati immutabili. Non usare == per List o Map, poiché confrontano per riferimento.
L'errore più comune è restituire true senza confronto. Questo causa la ricostruzione di tutti i widget dipendenti a ogni aggiornamento del genitore, anche se i dati non sono cambiati. Il secondo errore è restituire false quando i dati sono cambiati, portando a un'interfaccia utente obsoleta. Il terzo è un confronto complesso che viene eseguito ogni fotogramma e rallenta le prestazioni.
InheritedWidget e callback (passare funzioni attraverso i costruttori) risolvono problemi diversi. InheritedWidget è adatto per dati necessari a molti widget a diversi livelli dell'albero. I callback sono convenienti per il passaggio unidirezionale di eventi dal genitore a un figlio specifico o viceversa. La scelta dipende dall'architettura dell'applicazione e dalla frequenza degli aggiornamenti.
Usa InheritedWidget quando i dati sono necessari a molti widget a diversi livelli di annidamento: tema dell'app, preferenze utente, informazioni sul dispositivo, dati della sessione corrente. InheritedWidget è particolarmente efficace per dati “globali” che cambiano raramente ma sono necessari in diverse parti dell'interfaccia utente.
I callback (funzioni di richiamo) sono adatti per passare eventi da un widget figlio a un genitore: pressione di un pulsante, selezione di un elemento di elenco, invio di un modulo. I callback indicano esplicitamente quali azioni un figlio può eseguire e non creano dipendenze nascoste. Per passare dati verso il basso attraverso un piccolo numero di livelli, è anche più semplice usare parametri del costruttore.
| Criterio | InheritedWidget | Callback |
|---|---|---|
| Direzione | Dall'alto verso il basso (genitore → discendenti) | Dal basso verso l'alto (figlio → genitore) o diretta |
| Ambito | Intero sottoalbero | Widget specifico |
| Ricostruzione | Automatica al cambiamento dei dati | Richiede setState manuale |
| Complessità | Media (richiede classe InheritedWidget) | Bassa (solo una funzione) |
Provider e Riverpod sono librerie popolari di gestione dello stato in Flutter costruite sopra InheritedWidget. Estendono le sue capacità: aggiungono supporto ChangeNotifier, smaltimento automatico allo smontaggio, inizializzazione pigra e sintassi semplificata con i generics.
Provider usa InheritedWidget per passare un oggetto di qualsiasi tipo verso il basso nell'albero. ChangeNotifierProvider traccia i cambiamenti attraverso ChangeNotifier e chiama updateShouldNotify quando viene chiamato notifyListeners. Questo libera lo sviluppatore dalla creazione manuale di InheritedWidget e dall'implementazione di updateShouldNotify.
InheritedWidget diretto dà più controllo e non richiede dipendenze esterne. Provider fornisce infrastruttura pronta: Consumer, Selector, MultiProvider, ProxyProvider. La scelta dipende dalla complessità dell'applicazione. Per progetti semplici, InheritedWidget diretto è sufficiente; per progetti grandi, Provider o Riverpod riducono il codice boilerplate.
// InheritedWidget diretto
class UserProvider extends InheritedWidget {
final UserData userData;
const UserProvider({required this.userData, required Widget child}) : super(child: child);
static UserData of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<UserProvider>()!.userData;
@override
bool updateShouldNotify(UserProvider old) => old.userData != userData;
}
// Equivalente Provider
return ChangeNotifierProvider<UserData>(
create: (_) => UserData(),
child: MyApp(),
);
Entrambi gli approcci nell'esempio risolvono lo stesso problema — passare UserData verso il basso nell'albero. Provider riduce il volume di codice ma nasconde la meccanica di InheritedWidget. InheritedWidget diretto dà controllo completo e comprensione di ciò che accade, particolarmente importante quando si impara Flutter e si debuggano problemi complessi di ricostruzione.
Domande frequenti
InheritedWidget rende i dati disponibili a tutti i discendenti attraverso BuildContext, mentre un widget normale passa dati solo attraverso il costruttore. InheritedWidget iscrive anche i discendenti agli aggiornamenti dei dati.
I widget dipendenti vengono ricostruiti solo quando updateShouldNotify restituisce true. Se il metodo è implementato correttamente, la ricostruzione avviene solo quando i dati cambiano effettivamente, non a ogni ricostruzione del genitore.
Sì, puoi usare qualsiasi numero di InheritedWidget nello stesso albero. Ognuno fornisce dati di un tipo specifico e i widget possono ottenere dati da più InheritedWidget contemporaneamente.
dependOn iscrive il widget agli aggiornamenti — quando i dati cambiano, il widget viene ricostruito. findAncestor esegue una ricerca una tantum senza iscrizione e il widget non saprà dei cambiamenti dei dati.
Per stato semplice (tema, impostazioni) InheritedWidget è sufficiente. Per stato complesso con logica di business, usa Provider, Riverpod o BLoC — sono costruiti su InheritedWidget e aggiungono l'infrastruttura necessaria.
Riepilogo
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.
Leggi anche