InheritedWidget — ce este, transmiterea datelor în arbore și cum funcționează

Autor: IT Sectr Publicat: 2026-07-02 Timp de citire: 9 min

InheritedWidget — este un widget special în Flutter care transmite date în jos pe arborele de widgeturi fără transmiterea explicită prin constructori. Widgeturile copil obțin acces la date prin BuildContext și se abonează automat la actualizări. Când datele din InheritedWidget se modifică, toate widgeturile dependente se reconstruiesc. Conform Flutter API Reference, 2025, InheritedWidget stă la baza Theme, MediaQuery, Localizations și a majorității bibliotecilor de gestionare a stării.

Principalele puncte

  • InheritedWidget transmite date în jos pe Widget Tree fără transmiterea explicită prin fiecare widget.
  • Abonare automată — widgeturile care folosesc dependOnInheritedWidgetOfExactType se reconstruiesc la modificarea datelor.
  • Theme și MediaQuery — exemple încorporate de InheritedWidget disponibile în fiecare aplicație Flutter.
  • Provider și Riverpod sunt construite pe InheritedWidget și îi extind capacitățile de gestionare a stării.
  • Implementarea corectă necesită suprascrierea updateShouldNotify pentru a preveni reconstruirile inutile.

Ce este InheritedWidget în Flutter?

InheritedWidget — este un widget care face datele sale accesibile tuturor descendenților în Widget Tree. Spre deosebire de un widget obișnuit, care transmite date doar prin constructor către elementele copil, InheritedWidget permite oricărui widget din subarbore să acceseze datele fără un lanț de parametri. Aceasta rezolvă problema "prop drilling" — transmiterea datelor prin mai multe widgeturi intermediare care nu folosesc ele însele aceste date.

InheritedWidgeturi încorporate

Flutter include mai multe InheritedWidgeturi încorporate: Theme (schema de culori și stiluri), MediaQuery (dimensiunea ecranului, orientarea, densitatea pixelilor), Localizations (șiruri localizate), Directionality (direcția textului), DefaultTextStyle (stilul implicit al textului). Aceste widgeturi sunt stabilite de widgeturile rădăcină precum MaterialApp și sunt disponibile în întreaga aplicație.

Ciclul de viață al InheritedWidget

InheritedWidget nu are stare proprie — stochează datele transmise prin constructor. Când părintele InheritedWidget se reconstruiește cu date noi, metoda updateShouldNotify este apelată pentru a compara datele vechi și noi. Dacă metoda returnează true, toate widgeturile dependente sunt marcate pentru reconstruire. Acesta este un mecanism simplu, dar eficient de actualizare reactivă.

Cum funcționează transmiterea datelor prin InheritedWidget

Mecanismul de transmitere a datelor prin InheritedWidget se bazează pe Element Tree. Când un widget apelează dependOnInheritedWidgetOfExactType, elementul corespunzător înregistrează o dependență de InheritedElement. La modificarea InheritedWidget, InheritedElement notifică toate elementele dependente, care se reconstruiesc în următorul cadru.

Înregistrarea dependenței

Metoda dependOnInheritedWidgetOfExactType nu doar găsește InheritedWidget în arbore — ea abonează elementul curent la notificări. Dacă ați folosi findAncestorWidgetOfExactType în loc de dependOn, widgetul ar primi datele, dar nu s-ar reconstrui la modificarea lor. Aceasta este o diferență importantă: dependOn este o abonare, iar findAncestor este o căutare unică.

Parcurgerea arborelui InheritedWidget

Când un widget solicită InheritedWidget, Flutter urcă pe Element Tree de la elementul curent până la rădăcină, verificând fiecare InheritedElement pentru potrivirea tipului. Primul InheritedElement găsit este returnat. Aceasta înseamnă că cel mai apropiat InheritedWidget din arbore are prioritate — puteți suprascrie datele la un anumit nivel plasând InheritedWidget mai aproape de descendenți.

dart
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;
}

În acest exemplu, MyTheme folosește metoda statică of pentru a furniza date descendenților. Metoda dependOnInheritedWidgetOfExactType înregistrează dependența, iar updateShouldNotify compară datele vechi și noi pentru a determina necesitatea reconstruirii widgeturilor dependente.

Crearea propriului InheritedWidget

Crearea propriului InheritedWidget constă în doi pași: definirea clasei care moștenește InheritedWidget și implementarea metodei statice of pentru accesul din descendenți. Datele sunt transmise prin constructor, iar metoda updateShouldNotify determină când widgeturile dependente trebuie să se reconstruiască.

Pasul 1: Definirea clasei InheritedWidget

Clasa trebuie să moștenească InheritedWidget și să primească date prin constructor cu parametrul obligatoriu child. Datele pot fi de orice tip: primitive, obiecte, funcții. Regula principală — datele trebuie să fie imutabile (immutable) pentru a putea compara fiabil valorile vechi și noi.

Pasul 2: Metoda statică of

Metoda statică of primește un BuildContext și returnează datele InheritedWidget. În interior, se apelează dependOnInheritedWidgetOfExactType, care caută cel mai apropiat InheritedWidget de tipul specificat în arbore. Dacă InheritedWidget nu este găsit, metoda aruncă o excepție sau returnează o valoare implicită în funcție de implementare.

Pasul 3: Utilizarea în widgeturi

Pentru a accesa datele, widgetul apelează MyWidget.of(context) în interiorul metodei build. Flutter abonează automat widgetul la actualizări. Dacă datele se modifică, widgetul se va reconstrui în următorul cadru. Acest lucru permite crearea unui cod curat și declarativ fără parametri inutili.

dart
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;
}

În acest exemplu, UserPreferences stochează preferințele utilizatorului. Metoda updateShouldNotify compară fiecare câmp separat, ceea ce previne reconstruirile inutile la modificarea unui singur parametru. Folosiți o abordare similară pentru propriile InheritedWidgeturi cu mai multe câmpuri.

Metoda updateShouldNotify și prevenirea reconstruirilor inutile

updateShouldNotify — este metoda cheie a InheritedWidget care determină dacă widgeturile dependente trebuie notificate despre modificarea datelor. Dacă metoda returnează false, widgeturile dependente nu se reconstruiesc, chiar dacă InheritedWidget a primit o nouă instanță cu aceleași date. Acest lucru este critic pentru performanță.

Implementarea corectă a updateShouldNotify

Comparați doar câmpurile care s-au modificat efectiv și afectează afișarea. Dacă InheritedWidget conține 10 câmpuri, dar doar unul dintre ele afectează UI, verificați doar acel câmp. Pentru colecții, folosiți comparație profundă sau structuri de date imutabile. Nu folosiți == pentru List sau Map, deoarece acestea sunt comparate prin referință.

  • Primitive — folosiți comparații directe: oldWidget.value != value.
  • Obiecte imutabile — folosiți == suprascris: oldWidget.data != data (dacă data suprascrie ==).
  • Colecții — folosiți listEquals, mapEquals din package:flutter/foundation.dart.

Erori la implementarea updateShouldNotify

Cea mai frecventă eroare — returnarea true fără comparație. Aceasta duce la reconstruirea tuturor widgeturilor dependente la fiecare actualizare a părintelui, chiar dacă datele nu s-au modificat. A doua eroare — returnarea false când datele s-au modificat, ceea ce duce la un UI învechit. A treia — o comparație complexă care se execută în fiecare cadru și încetinește lucrul.

InheritedWidget vs callbackuri: ce să alegeți?

InheritedWidget și callbackurile (transmiterea funcțiilor prin constructor) rezolvă sarcini diferite. InheritedWidget este potrivit pentru datele de care au nevoie multe widgeturi la diferite niveluri ale arborelui. Callbackurile sunt convenabile pentru transmiterea unidirecțională a evenimentelor de la părinte la un anumit copil sau invers. Alegerea depinde de arhitectura aplicației și de frecvența modificărilor.

Când să folosiți InheritedWidget

Folosiți InheritedWidget când datele sunt necesare multor widgeturi la diferite niveluri de imbricare: tema aplicației, preferințele utilizatorului, informații despre dispozitiv, datele sesiunii curente. InheritedWidget este deosebit de eficient pentru datele "globale" care se modifică rar, dar sunt necesare în diferite părți ale UI.

Când să folosiți callbackuri

Callbackurile (funcții de revenire) sunt potrivite pentru transmiterea evenimentelor de la widgetul copil la cel părinte: apăsarea butonului, selectarea unui element din listă, trimiterea unui formular. Callbackurile indică explicit ce acțiuni poate efectua copilul și nu creează dependențe ascunse. Pentru transmiterea datelor în jos pe arbore pe un număr mic de niveluri, este mai simplu să folosiți parametrii constructorului.

CriteriuInheritedWidgetCallbackuri
DirecțieDe sus în jos (părinte → descendenți)De jos în sus (copil → părinte) sau direct
DomeniuÎntregul subarboreWidget specific
ReconstruireAutomată la modificarea datelorNecesită setState manual
ComplexitateMedie (necesită clasă InheritedWidget)Scăzută (doar o funcție)

InheritedWidget și bibliotecile de gestionare a stării

Provider și Riverpod — biblioteci populare de gestionare a stării în Flutter, construite pe InheritedWidget. Ele îi extind capacitățile: adaugă suport pentru ChangeNotifier, ștergere automată la demontare, inițializare lentă și sintaxă simplificată prin generice.

Provider bazat pe InheritedWidget

Provider folosește InheritedWidget pentru a transmite un obiect de orice tip în jos pe arbore. ChangeNotifierProvider urmărește modificările prin ChangeNotifier și apelează updateShouldNotify la apelarea notifyListeners. Acest lucru scutește dezvoltatorul de crearea manuală a InheritedWidget și implementarea updateShouldNotify.

Comparație cu InheritedWidget direct

InheritedWidget direct oferă mai mult control și nu necesită dependențe externe. Provider oferă infrastructură gata făcută: Consumer, Selector, MultiProvider, ProxyProvider. Alegerea depinde de complexitatea aplicației. Pentru proiecte simple, InheritedWidget direct este suficient; pentru proiecte mari, Provider sau Riverpod reduc codul șablon.

dart
// InheritedWidget direct
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;
}

// Echivalent Provider
return ChangeNotifierProvider<UserData>(
  create: (_) => UserData(),
  child: MyApp(),
);

Ambele abordări în exemplu rezolvă aceeași sarcină — transmiterea UserData în jos pe arbore. Provider reduce cantitatea de cod, dar ascunde mecanica InheritedWidget. InheritedWidget direct oferă control complet și înțelegere a ceea ce se întâmplă, ceea ce este deosebit de important la învățarea Flutter și depanarea problemelor complexe de reconstruire.

Întrebări frecvente

Cu ce diferă InheritedWidget de un widget obișnuit?

InheritedWidget face datele accesibile tuturor descendenților prin BuildContext, iar un widget obișnuit transmite date doar prin constructor. InheritedWidget, de asemenea, abonează descendenții la actualizări de date.

Cât de des se reconstruiesc widgeturile dependente?

Widgeturile dependente se reconstruiesc doar când updateShouldNotify returnează true. Dacă metoda este implementată corect, reconstruirea are loc doar la modificarea efectivă a datelor, nu la fiecare reconstruire a părintelui.

Pot fi folosite mai multe InheritedWidgeturi în același arbore?

Da, puteți folosi orice număr de InheritedWidgeturi în același arbore. Fiecare furnizează date de un anumit tip, iar widgeturile pot primi date din mai multe InheritedWidgeturi simultan.

Cu ce diferă dependOnInheritedWidgetOfExactType de findAncestorWidgetOfExactType?

dependOn abonează widgetul la actualizări — la modificarea datelor, widgetul se va reconstrui. findAncestor efectuează o căutare unică fără abonare, iar widgetul nu va afla despre modificările datelor.

Este InheritedWidget potrivit pentru gestionarea stării complexe?

Pentru stare simplă (temă, setări) InheritedWidget este suficient. Pentru stare complexă cu logică de afaceri, folosiți Provider, Riverpod sau BLoC — sunt construite pe InheritedWidget și adaugă infrastructura necesară.

Rezumat

  • InheritedWidget — un widget special Flutter pentru transmiterea datelor în jos pe arbore cu abonare automată la actualizări.
  • Mecanismul de funcționare se bazează pe Element Tree: InheritedElement înregistrează elementele dependente și le notifică despre modificări.
  • updateShouldNotify — metoda cheie pentru prevenirea reconstruirilor inutile ale widgeturilor dependente.
  • InheritedWidgeturi încorporate: Theme, MediaQuery, Localizations, Directionality, DefaultTextStyle.
  • Crearea propriului InheritedWidget include moștenirea clasei, transmiterea datelor prin constructor și metoda statică of.
  • Provider și Riverpod sunt construite pe InheritedWidget și adaugă ChangeNotifier, Consumer, Selector și sintaxă simplificată.
  • InheritedWidget rezolvă problema prop drilling și este fundamentul gestionării reactive a stării în Flutter.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și