Element Tree je mezivrstva ve Flutter, která spojuje deklarativní Widget Tree s imperativním RenderObject Tree. Na rozdíl od widgetů, které jsou při každém rebuildu znovu vytvářeny, prvky přetrvávají mezi aktualizacemi a spravují stav, klíče a životní cyklus. Podle Flutter API Reference, 2025 je porozumění Element Tree nezbytné pro efektivní práci s klíči, optimalizaci výkonu a ladění neočekávaného chování widgetů.
Hlavní body
Element Tree je zprostředkující hierarchie ve Flutter, která je vytvořena na základě Widget Tree a spravuje vkládání widgetů do aplikace. Každá instance prvku odpovídá jednomu widgetu ve stromu a ukládá na něj odkaz. Hlavní rozdíl mezi prvkem a widgetem je ten, že prvek si zachovává svou pozici ve stromu mezi přestavbami, zatímco widget může být při každém volání build znovu vytvořen.
Bez Element Tree by Flutter nemohl efektivně aktualizovat rozhraní. Pokud by každý rebuild znovu vytvářel RenderObject Tree, výkon by byl nepřijatelně nízký. Element Tree funguje jako stabilizátor: uchovává odkazy na RenderObject a State mezi aktualizacemi, což Flutter umožňuje aplikovat pouze minimální změny do vykreslovacího stromu.
Flutter používá tři hlavní typy prvků: StatelessElement pro StatelessWidget, StatefulElement pro StatefulWidget a LeafRenderObjectElement, SingleChildRenderObjectElement, MultiChildRenderObjectElement pro RenderObjectWidget. Každý typ je specializovaný pro svou třídu widgetu a určuje, jak prvek interaguje s RenderObject.
Třívrstvá architektura Flutter se skládá z Widget Tree (konfigurace), Element Tree (správa) a RenderObject Tree (vykreslování). Element Tree je spojovacím článkem: čte konfiguraci z Widget a předává příkazy RenderObject. Bez Element Tree by framework nemohl efektivně synchronizovat deklarativní popis se skutečným vykreslováním.
Když je prvek vložen do stromu, zkontroluje typ widgetu. Pokud je widget RenderObjectWidget, prvek vytvoří odpovídající RenderObject a přidá ho do RenderObject Tree. Pokud je widget LeafRenderObjectWidget, prvek vytvoří listový RenderObject. Pro StatelessWidget a StatefulWidget prvek jednoduše spravuje vkládání podřízených prvků.
abstract class Element {
Widget widget;
Element? parent;
List<Element>? children;
void mount(Element? parent, dynamic newSlot);
void update(Widget newWidget);
void unmount();
}
Tento zjednodušený kód ukazuje základní strukturu Element. Každý prvek ukládá odkaz na aktuální widget, rodičovský prvek a podřízené prvky. Metody mount, update a unmount spravují životní cyklus prvku a souvisejícího RenderObject.
Každý prvek ve Flutter prochází sekvencí fází životního cyklu: vytvoření, připojení, aktualizace a odpojení. Porozumění těmto fázím je nezbytné pro ladění neočekávaného chování, zejména při práci s animacemi, asynchronními operacemi a správou stavu.
Prvek je vytvořen voláním metody createElement widgetu. Pro StatelessWidget se vytváří StatelessElement, pro StatefulWidget — StatefulElement (který také vytváří objekt State). Pro RenderObjectWidget se vytváří odpovídající RenderObjectElement. Vytvoření prvku nastává při prvním výskytu widgetu ve Widget Tree.
Při připojení je prvek přidán do Element Tree a obdrží rodičovský prvek. Pro RenderObjectElement připojení také vytváří RenderObject a vkládá ho do RenderObject Tree. Pokud je widget StatefulWidget, v této fázi je volána metoda initState objektu State.
Když je widget přestavěn s novou konfigurací, prvek obdrží nový widget prostřednictvím metody update. Prvek porovná typ starého a nového widgetu: pokud se typy shodují, prvek aktualizuje svou konfiguraci; pokud ne — prvek je odpojen a vytvoří se nový. Tomu se říká "změna widgetu" a je to příčina ztráty stavu při změně typu.
Když je widget odstraněn z Widget Tree, je volána metoda unmount prvku. Prvek je odstraněn z Element Tree, RenderObject je odstraněn z RenderObject Tree a pro StatefulWidget je volána metoda dispose objektu State. Po unmount může být prvek znovu použit, pokud se widget znovu objeví na stejné pozici.
Klíče (Key) jsou mechanismem identifikace prvků, který umožňuje Flutter přiřazovat widgety ze starého a nového Widget Tree nikoli podle pozice, ale podle jedinečného identifikátoru. Klíče jsou kriticky důležité při práci s dynamickými seznamy, kde se může pořadí prvků měnit: přidávání, odebírání nebo přesouvání prvků.
Bez klíče Flutter přiřazuje prvky podle jejich pozice ve stromu: prvek na pozici 0 ze starého stromu je nahrazen widgetem na pozici 0 z nového stromu. Pokud se pořadí změnilo, prvky se zamíchají a stav může být ztracen nebo připojen k nesprávným datům. Klíč nutí Flutter hledat prvek podle identifikátoru, nikoli podle pozice.
ValueKey používá jednoduchou hodnotu (řetězec, číslo) k identifikaci prvku. ObjectKey používá odkaz na objekt — vhodné, když prvek nemá stabilní řetězcový identifikátor. UniqueKey generuje jedinečný identifikátor při každém vytvoření — používá se, když každá instance widgetu musí být jedinečná.
Column(
children: items.map((item) => TodoItem(
key: ValueKey(item.id),
title: item.title,
isDone: item.isDone,
)).toList(),
)
V tomto příkladu ValueKey s item.id zaručuje, že každý TodoItem zachová svůj stav (například fokus vstupního pole) při změně pořadí prvků v seznamu. Bez klíče by prvek na první pozici získal stav předchozího prvku na stejné pozici.
Stav (State) ve Flutter není uložen ve widgetech, ale v prvcích. Když je StatefulWidget přestavěn a vytváří novou instanci widgetu, odpovídající StatefulElement uchovává odkaz na starý objekt State. Nový widget se propojuje s existujícím State, což umožňuje uchovávat data mezi přestavbami.
Při přestavbě widgetu Flutter vytváří novou instanci StatefulWidget, ale odpovídající StatefulElement zůstává v Element Tree. Prvek volá metodu update na State a předává nový widget. Tímto způsobem objekt State a jeho data zůstávají zachovány. Ztráta stavu nastává pouze při změně typu widgetu, změně klíče nebo odstranění prvku ze stromu.
InheritedElement je speciální prvek, který umožňuje podřízeným prvkům získávat data z rodičovského InheritedWidget bez explicitního předávání přes konstruktory. Když se InheritedWidget změní, InheritedElement informuje všechny závislé prvky, které se přestaví. Tento mechanismus je základem Theme, MediaQuery a Provider.
Element Tree spotřebovává paměť a ovlivňuje rychlost prvního vykreslení. Každý prvek zabírá určité množství paměti: odkaz na widget, odkaz na rodiče, seznam podřízených prvků, slot a další pole pro RenderObjectElement. Optimalizace Element Tree snižuje dobu spouštění a spotřebu paměti.
Flutter se snaží znovu používat prvky při přestavbě. Pokud má widget v nové konfiguraci stejný typ a klíč, prvek není vytvořen znovu — je aktualizován. To je výrazně rychlejší než vytvoření nového prvku s následným připojením. Při změně typu nebo klíče je však starý prvek odpojen a nový je vytvořen od začátku.
RepaintBoundary vytváří samostatný RenderRepaintBoundary v RenderObject Tree, který izoluje část stromu. Na úrovni Element Tree RepaintBoundary nevytváří speciální prvek — používá SingleChildRenderObjectElement. Rozdíl se projevuje na úrovni RenderObject: při změně obsahu RepaintBoundary je překreslena pouze izolovaná oblast.
| Operace | Bez RepaintBoundary | S RepaintBoundary |
|---|---|---|
| Překreslení | Celá obrazovka | Pouze izolovaná oblast |
| Čas | ~16 ms při 60 FPS | ~2-5 ms |
| Paměť | Minimální | + několik kilobajtů na vrstvu |
Jak ukazuje tabulka, RepaintBoundary výrazně zkracuje dobu překreslení izolací měnící se oblasti. Na úrovni Element Tree to nevyžaduje další konfiguraci — stačí zabalit měnící se widget do RepaintBoundary.
Často kladené otázky
Widget Tree je konfigurace, která je při každém rebuildu znovu vytvářena. Element Tree je trvalá struktura, která přetrvává mezi aktualizacemi a spravuje stav, RenderObject a životní cyklus widgetů.
Bez Element Tree by Flutter musel při každé změně stavu znovu vytvářet RenderObject Tree, což by způsobilo významná zpoždění. Element Tree uchovává RenderObject a State a umožňuje aplikovat pouze minimální změny.
Prvek je odstraněn, když odpovídající widget zmizí z Widget Tree, nebo když se změní typ widgetu (například Column je nahrazen Row) nebo klíč. Při odpojení je volána dispose na State.
Klíče mění algoritmus přiřazování: místo hledání prvku podle pozice Flutter hledá prvek podle hodnoty klíče. To umožňuje zachovat stav při změně pořadí nebo počtu widgetů.
Ano, prostřednictvím BuildContext, který je abstrakcí prvku. Metody findAncestorWidgetOfExactType a dependOnInheritedWidgetOfExactType pracují s Element Tree a stoupají stromem prvků nahoru.
Shrnutí
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í.
Přečtěte si také