InheritedWidget — co to je, předávání dat ve stromě a jak funguje

Autor: IT Sectr Publikováno: 2026-07-02 Doba čtení: 9 min

InheritedWidget — je speciální widget ve Flutteru, který předává data dolů stromem widgetů bez explicitního předávání přes konstruktory. Dětské widgety získávají přístup k datům přes BuildContext a automaticky se přihlašují k odběru aktualizací. Když se data v InheritedWidget změní, všechny závislé widgety se znovu postaví. Podle Flutter API Reference, 2025 je InheritedWidget základem Theme, MediaQuery, Localizations a většiny knihoven pro správu stavu.

Hlavní body

  • InheritedWidget předává data dolů Widget Tree bez explicitního protahování přes každý widget.
  • Automatické přihlášení — widgety používající dependOnInheritedWidgetOfExactType se znovu postaví při změně dat.
  • Theme a MediaQuery — vestavěné příklady InheritedWidget dostupné v každé Flutter aplikaci.
  • Provider a Riverpod jsou postaveny na InheritedWidget a rozšiřují jeho možnosti správy stavu.
  • Správná implementace vyžaduje přepsání updateShouldNotify, aby se zabránilo zbytečným přestavbám.

Co je InheritedWidget ve Flutteru?

InheritedWidget — je widget, který zpřístupňuje svá data všem potomkům ve Widget Tree. Na rozdíl od běžného widgetu, který předává data pouze přes konstruktor dětským prvkům, InheritedWidget umožňuje libovolnému widgetu v podstromě přistupovat k datům bez řetězce parametrů. To řeší problém "prop drilling" — předávání dat přes mnoho zprostředkujících widgetů, které samy tato data nepoužívají.

Vestavěné InheritedWidget

Flutter obsahuje několik vestavěných InheritedWidget: Theme (barevné schéma a styly), MediaQuery (velikost obrazovky, orientace, hustota pixelů), Localizations (lokalizované řetězce), Directionality (směr textu), DefaultTextStyle (výchozí styl textu). Tyto widgety jsou nastaveny kořenovými widgety jako MaterialApp a jsou dostupné v celé aplikaci.

Životní cyklus InheritedWidget

InheritedWidget nemá vlastní stav — ukládá data předaná přes konstruktor. Když se rodič InheritedWidget znovu postaví s novými daty, je volána metoda updateShouldNotify pro porovnání starých a nových dat. Pokud metoda vrátí true, všechny závislé widgety jsou označeny k přestavbě. To je jednoduchý, ale účinný mechanismus reaktivní aktualizace.

Jak funguje předávání dat přes InheritedWidget

Mechanismus předávání dat přes InheritedWidget je založen na Element Tree. Když widget zavolá dependOnInheritedWidgetOfExactType, odpovídající prvek zaregistruje závislost na InheritedElement. Při změně InheritedWidget, InheritedElement informuje všechny závislé prvky, které se znovu postaví v příštím snímku.

Registrace závislosti

Metoda dependOnInheritedWidgetOfExactType nejen najde InheritedWidget ve stromě — přihlásí aktuální prvek k odběru oznámení. Pokud byste použili findAncestorWidgetOfExactType místo dependOn, widget by data obdržel, ale nepřestavěl by se při jejich změně. To je důležitý rozdíl: dependOn je přihlášení, findAncestor je jednorázové vyhledávání.

Procházení stromu InheritedWidget

Když widget požaduje InheritedWidget, Flutter stoupá po Element Tree od aktuálního prvku ke kořeni, kontroluje každý InheritedElement na shodu typu. První nalezený InheritedElement je vrácen. To znamená, že nejbližší InheritedWidget ve stromě má prioritu — můžete přepsat data na určité úrovni umístěním InheritedWidget blíže k potomkům.

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

V tomto příkladu MyTheme používá statickou metodu of k poskytování dat potomkům. Metoda dependOnInheritedWidgetOfExactType registruje závislost a updateShouldNotify porovnává stará a nová data k určení potřeby přestavby závislých widgetů.

Vytvoření vlastního InheritedWidget

Vytvoření vlastního InheritedWidget se skládá ze dvou kroků: definice třídy dědící z InheritedWidget a implementace statické metody of pro přístup z potomků. Data jsou předávána přes konstruktor a metoda updateShouldNotify určuje, kdy by se měly závislé widgety znovu postavit.

Krok 1: Definice třídy InheritedWidget

Třída musí dědit z InheritedWidget a přijímat data přes konstruktor s povinným parametrem child. Data mohou být libovolného typu: primitiva, objekty, funkce. Hlavní pravidlo — data musí být neměnná (immutable), aby bylo možné spolehlivě porovnávat staré a nové hodnoty.

Krok 2: Statická metoda of

Statická metoda of přijímá BuildContext a vrací data InheritedWidget. Uvnitř je volána dependOnInheritedWidgetOfExactType, která hledá nejbližší InheritedWidget zadaného typu ve stromě. Pokud InheritedWidget není nalezen, metoda v závislosti na implementaci buď vyhodí výjimku, nebo vrátí výchozí hodnotu.

Krok 3: Použití ve widgetech

Pro přístup k datům widget volá MyWidget.of(context) uvnitř metody build. Flutter automaticky přihlásí widget k odběru aktualizací. Pokud se data změní, widget se znovu postaví v příštím snímku. To umožňuje vytvářet čistý a deklarativní kód bez zbytečných parametrů.

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

V tomto příkladu UserPreferences ukládá uživatelské preference. Metoda updateShouldNotify porovnává každé pole zvlášť, což zabraňuje zbytečným přestavbám při změně pouze jednoho parametru. Použijte podobný přístup pro vlastní InheritedWidget s mnoha poli.

Metoda updateShouldNotify a prevence zbytečných přestaveb

updateShouldNotify — je klíčová metoda InheritedWidget, která určuje, zda by měly být závislé widgety informovány o změně dat. Pokud metoda vrátí false, závislé widgety se nepřestaví, i když samotný InheritedWidget obdržel novou instanci se stejnými daty. To je kriticky důležité pro výkon.

Správná implementace updateShouldNotify

Porovnávejte pouze pole, která se skutečně změnila a ovlivňují zobrazení. Pokud InheritedWidget obsahuje 10 polí, ale pouze jedno z nich ovlivňuje UI, kontrolujte jen toto pole. Pro kolekce používejte hluboké porovnání nebo neměnné datové struktury. Nepoužívejte == pro List nebo Map, protože se porovnávají podle reference.

  • Primitiva — používejte přímá porovnání: oldWidget.value != value.
  • Neměnné objekty — používejte přepsané ==: oldWidget.data != data (pokud data přepisuje ==).
  • Kolekce — používejte listEquals, mapEquals z package:flutter/foundation.dart.

Chyby při implementaci updateShouldNotify

Nejčastější chyba — vracení true bez porovnání. To vede k přestavbě všech závislých widgetů při každé aktualizaci rodiče, i když se data nezměnila. Druhá chyba — vracení false, když se data změnila, což vede k zastaralému UI. Třetí — složité porovnání, které se provádí každý snímek a zpomaluje práci.

InheritedWidget vs callbacky: co zvolit?

InheritedWidget a callbacky (předávání funkcí přes konstruktor) řeší různé úkoly. InheritedWidget je vhodný pro data, která potřebuje mnoho widgetů na různých úrovních stromu. Callbacky jsou vhodné pro jednosměrné předávání událostí od rodiče ke konkrétnímu potomkovi nebo naopak. Výběr závisí na architektuře aplikace a frekvenci změn.

Kdy použít InheritedWidget

Použijte InheritedWidget, když data potřebuje mnoho widgetů na různých úrovních vnoření: téma aplikace, uživatelské preference, informace o zařízení, data aktuální relace. InheritedWidget je zvláště účinný pro "globální" data, která se mění zřídka, ale jsou potřebná v různých částech UI.

Kdy použít callbacky

Callbacky (zpětné funkce) jsou vhodné pro předávání událostí z dětského widgetu rodičovskému: stisk tlačítka, výběr položky seznamu, odeslání formuláře. Callbacky explicitně ukazují, jaké akce může potomek provádět, a nevytvářejí skryté závislosti. Pro předávání dat dolů stromem na malý počet úrovní je také jednodušší použít parametry konstruktoru.

KritériumInheritedWidgetCallbacky
SměrShora dolů (rodič → potomci)Zespoda nahoru (potomek → rodič) nebo přímo
OblastCelý podstromKonkrétní widget
PřestavbaAutomatická při změně datVyžaduje ruční setState
SložitostStřední (nutná třída InheritedWidget)Nízká (jednoduchá funkce)

InheritedWidget a knihovny pro správu stavu

Provider a Riverpod — populární knihovny pro správu stavu ve Flutteru, postavené na InheritedWidget. Rozšiřují jeho možnosti: přidávají podporu ChangeNotifier, automatické odstranění při demontáži, línou inicializaci a zjednodušenou syntaxi přes generika.

Provider založený na InheritedWidget

Provider používá InheritedWidget k předávání objektu libovolného typu dolů stromem. ChangeNotifierProvider sleduje změny přes ChangeNotifier a volá updateShouldNotify při volání notifyListeners. To zbavuje vývojáře ručního vytváření InheritedWidget a implementace updateShouldNotify.

Porovnání s přímým InheritedWidget

Přímý InheritedWidget dává více kontroly a nevyžaduje externí závislosti. Provider poskytuje hotovou infrastrukturu: Consumer, Selector, MultiProvider, ProxyProvider. Výběr závisí na složitosti aplikace. Pro jednoduché projekty stačí přímý InheritedWidget, pro velké projekty Provider nebo Riverpod snižují šablonový kód.

dart
// Přímý InheritedWidget
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;
}

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

Oba přístupy v příkladu řeší stejný úkol — předávání UserData dolů stromem. Provider snižuje množství kódu, ale skrývá mechaniku InheritedWidget. Přímý InheritedWidget poskytuje plnou kontrolu a pochopení dění, což je zvláště důležité při učení Flutteru a ladění složitých problémů s přestavbou.

Často kladené otázky

Čím se InheritedWidget liší od běžného widgetu?

InheritedWidget zpřístupňuje data všem potomkům přes BuildContext, zatímco běžný widget předává data pouze přes konstruktor. InheritedWidget také přihlašuje potomky k odběru aktualizací dat.

Jak často se závislé widgety přestavují?

Závislé widgety se přestavují pouze když updateShouldNotify vrátí true. Pokud je metoda správně implementována, přestavba nastává pouze při skutečné změně dat, ne při každém rebuildu rodiče.

Lze použít více InheritedWidget v jednom stromě?

Ano, můžete použít libovolný počet InheritedWidget v jednom stromě. Každý poskytuje data určitého typu a widgety mohou přijímat data z více InheritedWidget současně.

Čím se liší dependOnInheritedWidgetOfExactType od findAncestorWidgetOfExactType?

dependOn přihlásí widget k odběru aktualizací — při změně dat se widget přestaví. findAncestor provádí jednorázové vyhledávání bez přihlášení a widget se o změnách dat nedozví.

Je InheritedWidget vhodný pro správu složitého stavu?

Pro jednoduchý stav (téma, nastavení) InheritedWidget stačí. Pro složitý stav s obchodní logikou použijte Provider, Riverpod nebo BLoC — jsou postaveny na InheritedWidget a přidávají potřebnou infrastrukturu.

Shrnutí

  • InheritedWidget — speciální widget Flutter pro předávání dat dolů stromem s automatickým přihlášením k aktualizacím.
  • Mechanismus fungování je založen na Element Tree: InheritedElement registruje závislé prvky a informuje je o změnách.
  • updateShouldNotify — klíčová metoda pro prevenci zbytečných přestaveb závislých widgetů.
  • Vestavěné InheritedWidget: Theme, MediaQuery, Localizations, Directionality, DefaultTextStyle.
  • Vytvoření vlastního InheritedWidget zahrnuje dědění třídy, předávání dat přes konstruktor a statickou metodu of.
  • Provider a Riverpod jsou postaveny na InheritedWidget a přidávají ChangeNotifier, Consumer, Selector a zjednodušenou syntaxi.
  • InheritedWidget řeší problém prop drilling a je základem reaktivní správy stavu ve Flutteru.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také