Widget Tree „је хијерархијска структура виџета у Flutter-у која одређује композицију корисничког интерфејса. Сваки елемент интерфејса, од дугмета до целог екрана, представљен је засебним виџетом уметнутим у родитељски контејнер. Flutter ажурира Widget Tree при свакој промени стања „ оквир пореди ново и старо стабло и примењује минималне измене. Према подацима Flutter Team, 2025, ефикасна структура стабла директно утиче на глаткоћу анимација и брзину одзива интерфејса.
Главне тачке
Widget Tree „је декларативни опис корисничког интерфејса у Flutter-у, изграђен у облику стабла угњежђених виџета. Сваки виџет дефинише део UI: његову конфигурацију, параметре приказа и понашање при интеракцији. Програмер описује како интерфејс треба да изгледа при тренутном стању апликације, а Flutter је одговоран за претварање овог описа у пикселе на екрану.
За разлику од императивних оквира где програмер директно манипулише елементима интерфејса, Flutter користи декларативни приступ. При промени стања апликације ствара се ново Widget Tree, а оквир израчунава разлику између старог и новог стабла. То омогућава минимизирање броја операција цртања и чини код предвидљивијим.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Widget Tree")),
body: Center(
child: Column(
children: [
Text("Здраво, Flutter"),
ElevatedButton(
onPressed: () {},
child: Text("Притисни ме"),
),
],
),
),
),
);
}
}
У овом примеру, Widget Tree се састоји од MaterialApp, Scaffold, AppBar, Center, Column, Text и ElevatedButton. Сваки од ових виџета је чвор стабла. Када се стање апликације промени, Flutter поново позива метод build и упоређује резултат са претходним стаблом.
Widget Tree почиње коренским виџетом који се прослеђује методи runApp. Коренски виџет је обично MaterialApp, CupertinoApp или WidgetsApp „ поставља глобална подешавања апликације. Од корена, стабло се грана на дечије виџете, од којих сваки може садржати сопствене потомке.
Виџети у Flutter-у деле се на single-child (примају једног потомка кроз параметар child) и multi-child (примају листу потомака кроз children). Примери single-child: Center, Padding, SizedBox, Container. Multi-child: Column, Row, Stack, ListView, GridView. Ова разлика утиче на структуру Widget Tree: multi-child виџети стварају шира, а single-child дубља стабла.
BuildContext „је локација виџета у Widget Tree. Сваки виџет има свој BuildContext који се прослеђује методи build и користи се за приступ родитељским виџетима, теми, MediaQuery и другим InheritedWidget. BuildContext је мост између виџета и његовог елемента у Element Tree.
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final mediaQuery = MediaQuery.of(context);
return Container(
color: theme.colorScheme.primary,
child: Text(
"Ширина екрана: ${mediaQuery.size.width}"
),
);
}
}
У овом примеру, BuildContext се користи за добијање теме и димензија екрана. Flutter се пење уз Widget Tree до најближег Theme и MediaQuery, који су InheritedWidget. Ово показује како контекст повезује виџет са његовим положајем у хијерархији.
При покретању Flutter апликације позива се функција runApp која прима коренски виџет и започиње изградњу Widget Tree. Процес укључује три фазе: креирање конфигурације виџета, формирање Element Tree и изградњу RenderObject Tree за стварно цртање.
Функција runApp креира коренски елемент кроз WidgetsFlutterBinding, који повезује оквир са графичким мотором. Коренски виџет се поставља у стабло и Flutter позива метод build за пуњење дечијим виџетима. Сваки позив build генерише нови подграф Widget Tree.
Након изградње Widget Tree, Flutter извршава почетни layout „ израчунавање димензија и позиција свих виџета. Овај процес почиње од корена и шири се низ стабло. Сваки виџет добија ограничења од родитеља и враћа израчунату величину. Ако се величине не поклапају, Flutter генерише грешку layout-а.
Након завршетка layout-а, Flutter приступа цртању сваког виџета. RenderObject претвара опис интерфејса у графичке команде које извршава GPU преко Skia или Impeller-а. Цео процес „ од Widget Tree до пиксела „ понавља се при сваком ажурирању стања са фреквенцијом до 120 кадрова у секунди.
StatelessWidget „је виџет који нема унутрашње променљиво стање. Његов изглед је у потпуности одређен улазним параметрима прослеђеним кроз конструктор. Ако се параметри нису променили, StatelessWidget се не преграђује. То га чини лаганим са становишта перформанси.
Користите StatelessWidget за статичке елементе интерфејса: иконе, текстуалне ознаке, декоративне разделнике и једноставна дугмад без унутрашње логике. Према Flutter документацији, око 70% виџета у типичној апликацији може бити StatelessWidget, што смањује оптерећење сакупљача отпада и убрзава rebuild.
StatefulWidget креира објекат State који се чува између преградњи виџета. Када се стање промени (кроз позив setState), Flutter означава виџет као „прљав” и при следећем кадру га преграђује. StatefulWidget омогућава имплементацију интерактивних елемената: поља за унос, анимација, тајмера и динамичких листи.
class CounterWidget extends StatefulWidget {
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text("Бројач: $_count"),
ElevatedButton(
onPressed: () {
setState(() => _count++);
},
child: Text("Увећај"),
),
],
);
}
}
У овом примеру, StatefulWidget користи setState за промену бројача. Када се стање ажурира, Flutter преграђује само измењени део Widget Tree „ виџет CounterWidget и његове потомке. Родитељски виџети се не преграђују, што је кључна предност декларативног модела Flutter-а.
Widget Tree је конфигурациони слој, а Element Tree је посредна карика између виџета и стварног цртања. Сваки виџет у Widget Tree ствара елемент у Element Tree који чува референцу на виџет и управља животним циклусом. Ова архитектура омогућава Flutter-у да ефикасно обрађује промене.
Када Flutter први пут наиђе на виџет, позива метод createElement који креира одговарајући елемент. За StatelessWidget се креира StatelessElement, за StatefulWidget „ StatefulElement који такође креира објекат State. Елемент се чува између циклуса rebuild, чак и ако се виџет поново ствара.
Key „је идентификатор који помаже Flutter-у да упари виџете из старог и новог Widget Tree. Ако виџет има Key, Flutter га користи за проналажење одговарајућег елемента, а не положај у стаблу. Кључеви су неопходни при раду са динамичким листама где се редослед елемената може мењати.
ListView(
children: items.map((item) => ListItem(
key: ValueKey(item.id),
data: item,
)).toList(),
)
Без Key, Flutter упарује елементе по положају, што може довести до неправилног очувања стања при промени редоследа. ValueKey са јединственим идентификатором гарантује да сваки елемент задржи своје стање без обзира на положај у листи.
Структура Widget Tree директно утиче на перформансе Flutter апликације. Дубока стабла са великим бројем угњежђених виџета захтевају више времена за layout фазу и повећавају потрошњу меморије. Flutter DevTools пружа алате за анализу Widget Tree и идентификацију уских грла.
Сваки ниво угњежђења додаје додатна израчунавања при layout-у и paint-у. Уместо дубоког угњежђења у ланцу, користите равније структуре. На пример, Row са Expanded може заменити неколико угњежђених Container са Align. Према подацима Flutter Team, оптимизација стабла може смањити време layout-а до 40%.
Flutter DevTools пружа виџет „Widget Inspector” који приказује тренутно Widget Tree у реалном времену. Програмер може изабрати било који виџет на екрану и видети његово место у стаблу, параметре и layout ограничења. Ово помаже у идентификацији неочекиваног угњежђења, непотребних rebuild-ова и проблема са димензијама.
RepaintBoundary „је виџет који изолује део Widget Tree за самостално цртање. Ако се садржај RepaintBoundary промени, прецртава се само његова област, а не цео екран. Користите RepaintBoundary за анимације, листе и друге често ажуриране елементе.
RepaintBoundary(
child: CustomPaint(
painter: MyPainter(),
child: SizedBox(
width: 200,
height: 200,
),
),
)
У овом примеру, RepaintBoundary изолује CustomPaint у засебну област цртања. При ажурирању анимације унутар ове области, прецртава се само виџет CustomPaint, а остатак екрана остаје непромењен. Ово је посебно корисно у сложеним интерфејсима са више анимираних елемената.
Често постављана питања
Widget Tree „је декларативни опис интерфејса који се поново ствара при сваком rebuild-у. Element Tree се чува између ажурирања и управља животним циклусом, стањем и упаривањем виџета са стварним RenderObject.
Нема ограничења на број виџета, али у пракси стабло са хиљадама виџета може успорити layout фазу. Flutter је оптимизован за стабла до неколико хиљада чворова; при већем броју препоручује се виртуелизација кроз ListView.builder.
Користите Flutter DevTools „ картица „Widget Inspector”. Покрените апликацију у debug режиму, отворите DevTools у прегледачу и изаберите било који виџет на екрану да бисте видели његово место у Widget Tree.
Rebuild „је процес поновног стварања конфигурације виџета при промени стања. Flutter поново позива метод build за измењене виџете, упоређује ново Widget Tree са претходним и примењује минималне измене на Element Tree.
Смањите дубину угњежђења, користите const виџете за статичке елементе, примените RepaintBoundary за изолацију анимација и избегавајте непотребне StatefulWidget тамо где је довољан StatelessWidget.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође