[{"data":1,"prerenderedAt":27},["ShallowReactive",2],{"articolo-inheritedwidget-e-buildcontext-come-funziona-davvero-la-propagazione-dello-stato-in-flutter":3,"comments-article-inheritedwidget-e-buildcontext-come-funziona-davvero-la-propagazione-dello-stato-in-flutter":26},{"id":4,"title":5,"slug":6,"excerpt":7,"body":8,"cover_image":9,"cover_remote_url":10,"cover_credit":11,"video_url":15,"status":16,"published_at":17,"meta_title":18,"meta_description":19,"category":20,"author":24},85,"InheritedWidget e BuildContext: come funziona davvero la propagazione dello stato in Flutter","inheritedwidget-e-buildcontext-come-funziona-davvero-la-propagazione-dello-stato-in-flutter","Provider, Riverpod e Theme.of() poggiano tutti sullo stesso meccanismo: gli InheritedWidget. Capire come funzionano, quando ricostruiscono e come usare InheritedNotifier e InheritedModel rende il codice più efficiente e i bug di context finalmente comprensibili.","## Perché studiare gli InheritedWidget nel 2024\n\nOgni volta che scrivi `Theme.of(context)`, `MediaQuery.sizeOf(context)` o `Navigator.of(context)` stai usando un **InheritedWidget**. Lo stesso vale per Provider, per lo `ProviderScope` di Riverpod e per il `BlocProvider` di flutter_bloc: sotto il cofano c'è sempre quel meccanismo.\n\nConoscerlo non è archeologia del framework: serve a capire **perché un widget si ricostruisce**, perché `context` a volte \"non trova\" quello che cerca, e come propagare dati senza aggiungere dipendenze esterne. In molte app un piccolo InheritedWidget scritto a mano è la soluzione più leggera e leggibile.\n\n## Il modello mentale: tre alberi, un solo lookup\n\nFlutter mantiene tre alberi paralleli:\n\n- **Widget tree**: la configurazione immutabile che descrivi nel `build`.\n- **Element tree**: le istanze persistenti che collegano widget e render object. Il `BuildContext` **è** un `Element`.\n- **Render tree**: ciò che viene effettivamente misurato e disegnato.\n\nQuando chiami `context.dependOnInheritedWidgetOfExactType\u003CT>()`, Flutter **non scorre l'albero** ogni volta: ogni `Element` mantiene una mappa (`_inheritedElements`) degli InheritedWidget disponibili sopra di lui, ereditata dal genitore. Il lookup è quindi un accesso a una hash map, **O(1)**, non una risalita lineare. Per questo `Theme.of(context)` è economico anche in alberi profondi.\n\nLa parte costosa non è la lettura: è la **dipendenza** che viene registrata. Chi legge con `dependOn...` viene aggiunto alla lista dei dipendenti e sarà ricostruito ogni volta che l'InheritedWidget cambia (se `updateShouldNotify` restituisce `true`).\n\n## Un InheritedWidget scritto a mano\n\nEcco il pattern canonico, con la coppia `of` \u002F `maybeOf` che ormai è convenzione nel framework:\n\n```dart\nclass AppConfig extends InheritedWidget {\n  const AppConfig({\n    super.key,\n    required this.apiBaseUrl,\n    required this.isBetaUser,\n    required super.child,\n  });\n\n  final String apiBaseUrl;\n  final bool isBetaUser;\n\n  static AppConfig? maybeOf(BuildContext context) =>\n      context.dependOnInheritedWidgetOfExactType\u003CAppConfig>();\n\n  static AppConfig of(BuildContext context) {\n    final config = maybeOf(context);\n    assert(config != null, 'Nessun AppConfig trovato sopra questo context');\n    return config!;\n  }\n\n  @override\n  bool updateShouldNotify(AppConfig oldWidget) =>\n      apiBaseUrl != oldWidget.apiBaseUrl || isBetaUser != oldWidget.isBetaUser;\n}\n```\n\nTre punti da non sottovalutare:\n\n1. **`updateShouldNotify` è il tuo filtro anti-rebuild.** Se restituisci sempre `true`, ogni ricostruzione dell'antenato ricostruisce tutti i dipendenti. Confronta i campi che contano davvero.\n2. **I campi devono essere immutabili** (`final`). Se muti un oggetto interno senza sostituire il widget, il framework non ha modo di accorgersene.\n3. **`of` lancia in assert**, `maybeOf` restituisce `null`: dai al chiamante la possibilità di scegliere.\n\n## Stato mutabile: InheritedWidget + StatefulWidget\n\nUn InheritedWidget è immutabile, quindi per gestire stato lo si accoppia a uno `StatefulWidget` che lo ricrea:\n\n```dart\nclass CartScope extends StatefulWidget {\n  const CartScope({super.key, required this.child});\n\n  final Widget child;\n\n  static CartController of(BuildContext context) =>\n      context.dependOnInheritedWidgetOfExactType\u003C_CartInherited>()!.controller;\n\n  @override\n  State\u003CCartScope> createState() => _CartScopeState();\n}\n\nclass _CartScopeState extends State\u003CCartScope> {\n  final CartController controller = CartController();\n\n  @override\n  void dispose() {\n    controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) =>\n      _CartInherited(controller: controller, child: widget.child);\n}\n\nclass _CartInherited extends InheritedWidget {\n  const _CartInherited({required this.controller, required super.child});\n\n  final CartController controller;\n\n  @override\n  bool updateShouldNotify(_CartInherited oldWidget) =>\n      controller != oldWidget.controller;\n}\n```\n\nQui `updateShouldNotify` confronta l'identità del controller: il controller in sé non cambia mai, quindi nessuno viene ricostruito inutilmente. La notifica dei cambiamenti la deleghi al controller stesso, che tipicamente è un `ChangeNotifier` o espone uno `Stream`.\n\n## InheritedNotifier: la scorciatoia per i ChangeNotifier\n\nSe il tuo stato è un `Listenable` (`ChangeNotifier`, `ValueNotifier`, `AnimationController`), `InheritedNotifier` fa il lavoro sporco: si iscrive al notifier e ricostruisce i dipendenti a ogni `notifyListeners()`.\n\n```dart\nclass ThemeModeScope extends InheritedNotifier\u003CValueNotifier\u003CThemeMode>> {\n  const ThemeModeScope({\n    super.key,\n    required ValueNotifier\u003CThemeMode> super.notifier,\n    required super.child,\n  });\n\n  static ValueNotifier\u003CThemeMode> of(BuildContext context) => context\n      .dependOnInheritedWidgetOfExactType\u003CThemeModeScope>()!\n      .notifier!;\n}\n\n\u002F\u002F Uso\nfinal mode = ThemeModeScope.of(context).value;\n\u002F\u002F ...\nThemeModeScope.of(context).value = ThemeMode.dark;\n```\n\nAttenzione: la granularità è quella del notifier intero. Chi legge viene ricostruito **anche se il campo che gli interessa non è cambiato**. Per stati grandi, spezza in più scope o usa `InheritedModel`.\n\n## InheritedModel: dipendenze per \"aspetto\"\n\n`InheritedModel` permette a un widget di dipendere solo da una **fetta** dei dati. Ogni dipendente dichiara l'aspetto che gli interessa e viene ricostruito solo se quell'aspetto cambia.\n\n```dart\nenum UserAspect { name, avatar }\n\nclass UserScope extends InheritedModel\u003CUserAspect> {\n  const UserScope({\n    super.key,\n    required this.name,\n    required this.avatarUrl,\n    required super.child,\n  });\n\n  final String name;\n  final String avatarUrl;\n\n  static UserScope of(BuildContext context, UserAspect aspect) =>\n      InheritedModel.inheritFrom\u003CUserScope>(context, aspect: aspect)!;\n\n  @override\n  bool updateShouldNotify(UserScope old) =>\n      name != old.name || avatarUrl != old.avatarUrl;\n\n  @override\n  bool updateShouldNotifyDependent(UserScope old, Set\u003CUserAspect> aspects) {\n    if (aspects.contains(UserAspect.name) && name != old.name) return true;\n    if (aspects.contains(UserAspect.avatar) && avatarUrl != old.avatarUrl) {\n      return true;\n    }\n    return false;\n  }\n}\n```\n\nUn widget che mostra solo l'avatar userà `UserScope.of(context, UserAspect.avatar)` e ignorerà i cambi di nome. È esattamente la tecnica adottata dal framework per `MediaQuery`: da Flutter 3.10 esistono `MediaQuery.sizeOf(context)`, `paddingOf`, `viewInsetsOf`… proprio per evitare che un cambio di padding ricostruisca chi legge solo la dimensione.\n\n> **Best practice immediata:** nel codice nuovo preferisci `MediaQuery.sizeOf(context)` a `MediaQuery.of(context).size`. È una riga che elimina rebuild inutili, per esempio all'apertura della tastiera.\n\n## dependOn vs getElementForInheritedWidgetOfExactType\n\nEsistono due modi di leggere:\n\n- `dependOnInheritedWidgetOfExactType\u003CT>()` — legge **e** registra la dipendenza. Da usare nel `build` o in `didChangeDependencies`.\n- `getInheritedWidgetOfExactType\u003CT>()` (o `getElementForInheritedWidgetOfExactType\u003CT>()?.widget as T`) — legge **senza** dipendere. Utile in `initState`, in `dispose` o nei callback, quando ti serve solo un riferimento stabile (es. un controller) e non vuoi essere ricostruito.\n\n```dart\n@override\nvoid initState() {\n  super.initState();\n  \u002F\u002F OK: nessuna dipendenza registrata\n  final controller = context\n      .getInheritedWidgetOfExactType\u003C_CartInherited>()!\n      .controller;\n  controller.load();\n}\n```\n\nChiamare `dependOn...` in `initState` genera un'eccezione, perché in quella fase l'element non può ancora registrare dipendenze in modo sicuro. Se ti serve reagire ai cambiamenti, il posto giusto è `didChangeDependencies`.\n\n## I tre errori più comuni con il BuildContext\n\n### 1. Il context sbagliato\n\n```dart\n\u002F\u002F SBAGLIATO: il context di build() è sopra lo Scaffold\nScaffold(\n  body: Builder(\n    builder: (innerContext) => ElevatedButton(\n      onPressed: () => Scaffold.of(innerContext).openDrawer(),\n      child: const Text('Apri'),\n    ),\n  ),\n)\n```\n\nUn lookup vede solo gli antenati del **proprio** element. Se l'InheritedWidget è creato nello stesso `build`, serve un `Builder` (o un widget separato) per ottenere un context più in basso.\n\n### 2. Leggere dopo un `await`\n\n```dart\nFuture\u003Cvoid> _save(BuildContext context) async {\n  await repository.save();\n  if (!context.mounted) return; \u002F\u002F indispensabile\n  ScaffoldMessenger.of(context).showSnackBar(\n    const SnackBar(content: Text('Salvato')),\n  );\n}\n```\n\nDal 2023 `BuildContext.mounted` è disponibile direttamente e il lint `use_build_context_synchronously` lo segnala: attivalo in `analysis_options.yaml`.\n\n### 3. Dipendere da tutto per usare poco\n\n`Theme.of(context)` in un widget che usa solo un colore va benissimo, ma `MediaQuery.of(context)` per la sola larghezza è uno spreco. Regola pratica: **dipendi dal minimo indispensabile e il più in basso possibile nell'albero**.\n\n## Quando usare un InheritedWidget e quando no\n\nUsalo quando:\n\n- devi passare una dipendenza a un sottoalbero **senza attraversare dieci costruttori** (prop drilling);\n- stai scrivendo un **package** e non vuoi imporre una libreria di state management;\n- il dato è di scope (tema, configurazione, sessione, controller di una schermata complessa).\n\nPreferisci una soluzione dedicata (Riverpod, BLoC, Provider) quando ti servono: cache e disposal automatici, dipendenze fra provider, gestione di stati asincroni, testabilità con override. Quelle librerie non sostituiscono l'InheritedWidget: lo **incapsulano** aggiungendo ergonomia.\n\n## Verificare i rebuild\n\nPer capire se le tue dipendenze sono troppo larghe, in DevTools attiva **Track Widget Rebuilds** (Flutter Inspector) oppure abilita temporaneamente:\n\n```dart\nimport 'package:flutter\u002Frendering.dart';\n\nvoid main() {\n  debugPrintRebuildDirtyWidgets = true; \u002F\u002F solo in debug\n  runApp(const MyApp());\n}\n```\n\nSe vedi ricostruzioni a raffica all'apertura della tastiera o alla rotazione, quasi sempre la causa è un `MediaQuery.of(context)` posizionato troppo in alto nell'albero.\n\n## In sintesi\n\n- Il `BuildContext` è un `Element`: il lookup degli InheritedWidget è O(1) grazie a una mappa mantenuta da ogni element.\n- `updateShouldNotify` decide chi si ricostruisce: scrivilo con cura.\n- `InheritedNotifier` semplifica l'integrazione con i `Listenable`; `InheritedModel` offre dipendenze granulari per aspetto.\n- Usa `dependOn...` nel `build`, `get...` in `initState`\u002F`dispose`.\n- Preferisci le API \"of\" granulari (`MediaQuery.sizeOf`) e proteggi sempre il context dopo un `await` con `context.mounted`.\n\nPadroneggiare questo livello del framework ti permette di scegliere consapevolmente lo strumento giusto — e di debuggare in cinque minuti problemi che altrimenti sembrano magia nera.","https:\u002F\u002Fflutter.it\u002Fstorage\u002Farticles\u002F83d4b036-003b-4891-b553-348003c9836b.jpg","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1695796007641-80ee269d0cb4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w5NzA2NTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODgzMjE2ODR8&ixlib=rb-4.1.0&q=80&w=1080",{"name":12,"author_url":13,"photo_url":14},"XS Xue","https:\u002F\u002Funsplash.com\u002F@kngstm","https:\u002F\u002Funsplash.com\u002Fphotos\u002Fa-close-up-of-a-tree-with-water-coming-out-of-it-LygCizv81G0",null,"published","2026-09-02T04:01:25+00:00","InheritedWidget e BuildContext in Flutter: guida","Come funzionano davvero InheritedWidget, BuildContext, InheritedNotifier e InheritedModel in Flutter: rebuild, dipendenze granulari ed errori comuni.",{"id":21,"name":22,"slug":23},1,"Guide","guide",{"id":21,"name":25},"Flutter Bot",[],1789120584552]