Element Tree је међуслој у Flutter-у који повезује декларативно стабло Widget Tree са императивним стаблом RenderObject Tree. За разлику од виџета, који се поново креирају при сваком rebuild-у, елементи се задржавају између ажурирања и управљају стањем, кључевима и животним циклусом. Према Flutter API Reference, 2025, разумевање Element Tree-а је неопходно за ефикасан рад са кључевима, оптимизацију перформанси и отклањање грешака у неочекиваном понашању виџета.
Главно
Element Tree је међухијерархија у Flutter-у, која се креира на основу Widget Tree-а и управља уграђивањем виџета у апликацију. Свака инстанца елемента одговара једном виџету у стаблу и чува референцу на њега. Главна разлика између елемента и виџета је у томе што елемент задржава свој положај у стаблу између преизградњи, док се виџет може поново креирати при сваком позиву build-а.
Без Element Tree-а Flutter не би могао ефикасно да ажурира интерфејс. Ако би сваки rebuild поново креирао RenderObject Tree, перформансе би биле неприхватљиво ниске. Element Tree има улогу стабилизатора: задржава референце на RenderObject и State између ажурирања, омогућавајући Flutter-у да примени само минималне измене на стабло исцртавања.
Flutter користи три главна типа елемената: StatelessElement за StatelessWidget, StatefulElement за StatefulWidget и LeafRenderObjectElement, SingleChildRenderObjectElement, MultiChildRenderObjectElement за RenderObjectWidget. Сваки тип је специјализован за своју класу виџета и одређује како елемент интерагује са RenderObject-ом.
Трослојна архитектура Flutter-а састоји се од Widget Tree-а (конфигурација), Element Tree-а (управљање) и RenderObject Tree-а (исцртавање). Element Tree је везна карика: чита конфигурацију из Widget-а и преноси команде RenderObject-у. Без Element Tree-а фрејмворк не би могао ефикасно да синхронизује декларативни опис са стварним исцртавањем.
Када се елемент угради у стабло, он проверава тип виџета. Ако је виџет RenderObjectWidget, елемент креира одговарајући RenderObject и додаје га у RenderObject Tree. Ако је виџет LeafRenderObjectWidget, елемент креира листни RenderObject. За StatelessWidget и StatefulWidget елемент једноставно управља уграђивањем подређених елемената.
abstract class Element {
Widget widget;
Element? parent;
List<Element>? children;
void mount(Element? parent, dynamic newSlot);
void update(Widget newWidget);
void unmount();
}
Овај поједностављени код приказује основну структуру Element. Сваки елемент чува референцу на тренутни виџет, родитељски елемент и подређене елементе. Методе mount и update управљају животним циклусом елемента и са њим повезаног RenderObject-а.
Сваки елемент у Flutter-у пролази кроз низ фаза животног циклуса: креирање, монтирање, ажурирање и демонтирање. Разумевање ових фаза је неопходно за отклањање грешака у неочекиваном понашању, посебно при раду са анимацијама, асинхроним операцијама и управљањем стањем.
Елемент се креира позивом методе createElement виџета. За StatelessWidget се креира StatelessElement, за StatefulWidget — StatefulElement (који такође креира објекат State). За RenderObjectWidget се креира одговарајући RenderObjectElement. Креирање елемента се дешава при првом појављивању виџета у Widget Tree-у.
При монтирању елемент се додаје у Element Tree и добија родитељски елемент. За RenderObjectElement монтирање такође креира RenderObject и уграђује га у RenderObject Tree. Ако је виџет StatefulWidget, у овој фази се позива метода initState објекта State.
Када се виџет прегради са новом конфигурацијом, елемент добија нови виџет преко методе update. Елемент упоређује тип новог и старог виџета: ако се типови поклапају, елемент ажурира своју конфигурацију; ако не — елемент се демонтира и креира се нови. То се назива „промена виџета" и узрок је губитка стања при промени типа.
При уклањању виџета из Widget Tree-а на елементу се позива метода unmount. Елемент се уклања из Element Tree-а, RenderObject се уклања из RenderObject Tree-а, а за StatefulWidget се позива метода dispose објекта State. После unmount-а елемент се може поново користити ако се виџет поново појави на истој позицији.
Кључеви (Key) су механизам идентификације елемената који омогућава Flutter-у да подудари виџете из старог и новог Widget Tree-а не по позицији, већ по јединственом идентификатору. Кључеви су критично важни при раду са динамичким листама, где се редослед елемената може променити: додавање, уклањање или премештање елемената.
Без кључа Flutter подудара елементе по њиховој позицији у стаблу: елемент на позицији 0 из старог стабла замењује се виџетом на позицији 0 из новог стабла. Ако се редослед променио, елементи се помешају, а стање може бити изгубљено или везано за погрешне податке. Кључ приморава Flutter да тражи елемент по идентификатору, а не по позицији.
ValueKey користи једноставну вредност (низ, број) за идентификацију елемента. ObjectKey користи референцу на објекат — одговара када елемент нема стабилан низовни идентификатор. UniqueKey генерише јединствени идентификатор при сваком креирању — користи се када свака инстанца виџета мора бити јединствена.
Column(
children: items.map((item) => TodoItem(
key: ValueKey(item.id),
title: item.title,
isDone: item.isDone,
)).toList(),
)
У овом примеру ValueKey са item.id гарантује да ће сваки TodoItem задржати своје стање (на пример, фокус поља за унос) при промени редоследа елемената у листи. Без кључа елемент на првој позицији добио би стање претходног елемента на истој позицији.
Стање (State) у Flutter-у се чува не у виџетима, већ у елементима. Када се StatefulWidget прегради и креира нову инстанцу виџета, одговарајући StatefulElement задржава референцу на стари објекат State. Нови виџет се повезује са постојећим State-ом, што омогућава задржавање података између преизградњи.
При rebuild-у виџета Flutter креира нову инстанцу StatefulWidget-а, али одговарајући StatefulElement остаје у Element Tree-у. Елемент позива методу update на State-у, преносећи нови виџет. На тај начин објекат State и његови подаци се задржавају. Губитак стања се дешава само при промени типа виџета, промени кључа или уклањању елемента из стабла.
InheritedElement је посебан елемент који омогућава подређеним елементима да добијају податке од родитељског InheritedWidget-а без експлицитног преношења кроз конструкторе. Када се InheritedWidget промени, InheritedElement обавештава све зависне елементе, који се преграђују. Овај механизам је у основи Theme, MediaQuery и Provider-а.
Element Tree троши меморију и утиче на брзину првог исцртавања. Сваки елемент заузима одређену количину меморије: референцу на виџет, референцу на родитеља, листу подређених елемената, слот и додатна поља за RenderObjectElement. Оптимизација Element Tree-а смањује време покретања и смањује потрошњу меморије.
Flutter настоји да поново користи елементе при преизградњи. Ако виџет у новој конфигурацији има исти тип и кључ, елемент се не креира поново — он се ажурира. То је знатно брже од креирања новог елемента са накнадним монтирањем. Међутим, при промени типа или кључа стари елемент се демонтира, а нови се креира испочетка.
RepaintBoundary креира посебан RenderRepaintBoundary у RenderObject Tree-у, који изолује део стабла. На нивоу Element Tree-а RepaintBoundary не креира посебан елемент — он користи SingleChildRenderObjectElement. Разлика се испољава на нивоу RenderObject-а: при промени садржаја поново се исцртава само изолована област RepaintBoundary-ја.
| Операција | Без RepaintBoundary-ја | Са RepaintBoundary-јем |
|---|---|---|
| Поновно исцртавање | Цео екран | Само изолована област |
| Време | ~16 ms при 60 FPS | ~2-5 ms |
| Меморија | Минимално | + неколико килобајта по слоју |
Као што табела показује, RepaintBoundary значајно смањује време поновног исцртавања захваљујући изолацији променљиве области. На нивоу Element Tree-а то не захтева додатну конфигурацију — довољно је умотати променљиви виџет у RepaintBoundary.
Често постављана питања
Widget Tree је конфигурација која се поново креира при сваком rebuild-у. Element Tree је стална структура која се задржава између ажурирања и управља стањем, RenderObject-ом и животним циклусом виџета.
Без Element Tree-а Flutter би морао поново да креира RenderObject Tree при свакој промени стања, што би изазвало значајна кашњења. Element Tree задржава RenderObject и State, омогућавајући примену само минималних измена.
Елемент се уклања када одговарајући виџет нестане из Widget Tree-а, или када се промени тип виџета (на пример, Column се замењује са Row) или кључ. При демонтирању се позива dispose на State-у.
Кључеви мењају алгоритам подударања: уместо тражења елемента по позицији, Flutter тражи елемент по вредности кључа. То омогућава задржавање стања при промени редоследа или броја виџета.
Да, преко BuildContext-а, који је апстракција елемента. Методе findAncestorWidgetOfExactType и dependOnInheritedWidgetOfExactType раде са Element Tree-ом, уздижући се кроз стабло елемената.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође