StatefulWidget — Flutter виџет са променљивим стањем који омогућава UI-ју да реагује на акције корисника, асинхроне догађаје и токове података. Према званичној документацији Flutter-а (Flutter.dev, 2026), StatefulWidget се користи за све интерактивне елементе апликације: обрасце за унос, анимације, поља за потврду, прекидаче и екране који учитавају податке из мреже. За разлику од StatelessWidget-а, он креира засебан објекат State који се чува током целог животног циклуса и може се поново изградити без поновног креирања самог виџета.
Главно
StatefulWidget — је Flutter класа која може да мења своје стање као одговор на акције корисника, системске догађаје или асинхроне операције. За разлику од StatelessWidget-а, StatefulWidget се не приказује директно — он креира State објекат који је одговоран за рендеровање. Подела на две класе (Widget и State) омогућава Flutter-у да поново изгради UI без поновног креирања самог виџета, што даје значајну предност у перформансама код честих ажурирања.
Архитектура StatefulWidget-а прати образац «раздвајање променљивог и непроменљивог»: сам виџет остаје непроменљив (као StatelessWidget), а све променљиво стање се чува у засебном State објекту. То омогућава Flutter-у да поново користи виџете упоређујући их по типу и Key-у, и истовремено задржи актуелно стање између поновних изградњи.
Према Google-у (Flutter Architectural Overview, 2026), StatefulWidget је оптималан за сценарије где се стање мења више од једном током живота виџета: текстуална поља, анимације, тајмери, токови података, асинхрона учитавања. За једнократну иницијализацију довољан је StatelessWidget.
StatefulWidget је обавезан када виџет мора да реагује на спољне догађаје: притисак дугмета, завршетак HTTP захтева, ажурирање података из базе, претплата на WebSocket. Такође је неопходан за виџете са анимацијом, текстуална поља са контролерима и компоненте које управљају фокусом. Ако виџет само приказује податке и не генерише догађаје — користите StatelessWidget.
StatefulWidget се састоји од две класе: самог StatefulWidget (лаган, непроменљив) и State (тежак, променљив). Оквир креира State путем метода createState(), који се позива једном приликом уградње у стабло. State добија референцу на виџет преко својства widget и може да приступи његовим пољима у било ком тренутку животног циклуса.
Животни циклус StatefulWidget-а састоји се од шест главних фаза, од којих свака пружа метод који се може прегазити за извршавање специфичних задатака. Разумевање ових фаза је критично важно за правилан рад са ресурсима и избегавање цурења меморије.
createState — први метод животног циклуса, позива се приликом уградње StatefulWidget-а у стабло. Мора да врати нову инстанцу State-а повезану са овим виџетом. Овај метод се позива тачно једном током целог живота елемента. Важно је не извршавати тешке операције овде — createState мора бити максимално лаган.
initState — позива се одмах након креирања State-а, пре прве изградње UI-ја. Овде се извршава: иницијализација контролера (TextEditingController, AnimationController), претплата на токове података (StreamSubscription), подешавање тајмера и почетна иницијализација поља. Према Flutter документима (Flutter.dev, 2026), у initState-у се не може позвати BuildContext.of() — стабло још није у потпуности монтирано.
didChangeDependencies — позива се након initState-а и сваки пут када се промене зависности InheritedWidget-а. Ово је одговарајуће место за позив MediaQuery.of(context) или претплату на Theme — вредности које се могу променити током рада апликације. Ако виџет користи InheritedWidget, логика иницијализације треба да буде овде, а не у initState-у.
build — главни метод који враћа стабло виџета. Позива се након initState-а, након didChangeDependencies-а и након сваког setState-а. didUpdateWidget се позива када родитељ поново изграђује и прослеђује StatefulWidget са новим параметрима. Овде можете упоредити стара и нова поља виџета и, ако је потребно, ажурирати стање.
dispose — завршна фаза животног циклуса. Овде се ослобађају сви ресурси: отказују претплате на токове, бришу контролери, отказују тајмери. Непозивање dispose-а доводи до цурења меморије. Након dispose-а, State се сматра мртвим — позив setState-а унутар њега баца изузетак.
Механизам рада StatefulWidget-а заснива се на усклађеном раду три ентитета: Widget (лагани опис), Element (посреднички слој) и State (складиште података). Када Flutter наиђе на StatefulWidget у опису, креира StatefulElement који позива createState и чува референцу на State објекат. При поновној изградњи родитеља, Flutter упоређује нови виџет са тренутним Element-ом — ако се тип и Key поклапају, Element се ажурира, а State остаје исти.
Стање се мења само позивом setState-а, који обавештава оквир о потреби поновне изградње. Важно је разумети: setState не мења стање аутоматски — он само означава виџет као «прљав». Програмер самостално ажурира поља State-а у колбеку прослеђеном setState-у. Након завршетка колбека, Flutter позива build и ажурира UI.
Према Dart/Flutter тиму (Dart Language Specification, 2026), ова подела гарантује да се све промене стања дешавају синхроно пре позива build-а, искључујући ситуацију када UI приказује делимично ажуриране податке. Ово је кључни механизам конзистентности интерфејса у Flutter-у.
Размотримо једноставан StatefulWidget — бројач кликова на дугме. Он демонстрира основни образац: креирање State-а, иницијализацију поља у initState-у, промену путем setState-а:
class CounterScreen extends StatefulWidget {
const CounterScreen({super.key});
@override
State<CounterScreen> createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Број: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('Увећај'),
),
],
);
}
}
Пример са асинхроним учитавањем података и управљањем животним циклусом. StatefulWidget учитава податке из мреже и приказује стање учитавања:
class UserProfilePage extends StatefulWidget {
final String userId;
const UserProfilePage({super.key, required this.userId});
@override
State<UserProfilePage> createState() => _UserProfilePageState();
}
class _UserProfilePageState extends State<UserProfilePage> {
UserModel? _user;
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadUser();
}
Future<void> _loadUser() async {
final user = await UserService.fetchUser(widget.userId);
setState(() {
_user = user;
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
if (_isLoading) return const CircularProgressIndicator();
return Text('Здраво, ${_user!.name}');
}
}
У другом примеру важно је приметити: initState покреће асинхрону операцију, али сам метод није асинхрон. Асинхроност се реализује кроз async/await унутар засебног метода _loadUser, који ажурира стање путем setState-а након завршетка захтева. Овај приступ гарантује да ће виџет исправно приказати индикатор учитавања пре добијања података.
Избор између StatefulWidget-а и StatelessWidget-а није само питање постојања стања. StatefulWidget пружа потпуни животни циклус са методима initState, didChangeDependencies, didUpdateWidget и dispose, што је неопходно за рад са контролерима, анимацијама и токовима. StatelessWidget, са своје стране, нема ове методе и увек је лакши за оквир.
Препорука Flutter тима (Flutter docs, 2026) — минимизирати број StatefulWidget-а у апликацији, подизањем стања навише у стаблу (State Hoisting) или коришћењем решења за управљање стањем (Riverpod, Bloc, Provider). Сваки StatefulWidget креира State објекат који живи до уклањања елемента — што је више таквих виџета, веће је оптерећење меморије.
| Критеријум | StatefulWidget | StatelessWidget |
|---|---|---|
| Стање | Променљиво | Непроменљиво |
| Животни циклус | 6 фаза | Само build |
| State објекат | Креира се засебно | Није потребан |
| setState | Доступан | Није доступан |
| Претплате | initState/dispose | Нису подржане |
| const конструктор | Ограничен | Потпуно подржан |
| Потрошња меморије | Већа | Мања |
StatefulWidget захтева више ресурса од StatelessWidget-а због потребе за креирањем и одржавањем State објекта. Међутим, правилно коришћење StatefulWidget-а не доводи до проблема са перформансама ако се поштује неколико правила. Прво, избегавајте дубоко угњеждавање StatefulWidget-а — сваки ниво додаје додатне трошкове за обилазак стабла. Друго, разбијајте сложене StatefulWidget-е на неколико једноставних, од којих је сваки одговоран за свој део стања.
Према истраживању Flutter Performance (Flutter.dev, фебруар 2026), најчешћи узрок пада FPS-а је позив setState-а у родитељском виџету који поново гради све потомке, укључујући StatelessWidget-е који нису променили свој приказ. Решење — извуците променљиви део UI-ја у засебан StatefulWidget, тако да setState поново гради само минимум потребних виџета.
Коришћење const-а унутар State-а је још једна важна техника. Ако су виџети деце проглашени као const, Flutter их неће поново градити при позиву setState-а у родитељу. Ово смањује оптерећење оквира и смањује време рендеровања кадра.
Сваки позив setState-а покреће потпуну поновну изградњу виџета. Ако се стање мења високом фреквенцијом (на пример, анимација или ток података), размотрите коришћење AnimatedBuilder-а, ValueListenableBuilder-а или StreamBuilder-а уместо ручног позива setState-а. Ови виџети оптимизују поновну изградњу, ажурирајући само онај део UI-ја који се стварно променио.
Прва типична грешка са StatefulWidget-ом — позив setState-а након dispose-а. Када је виџет уклоњен из стабла, State се сматра мртвим и сваки позив setState-а баца изузетак «setState called after dispose». Најчешће се то дешава када се асинхрона операција заврши након уклањања виџета. Решење — проверавати заставицу mounted пре позива setState-а или отказати асинхроне операције у dispose-у.
Друга грешка — извршавање тешких прорачуна у методи build. Пошто се build позива при сваком setState-у и при свакој поновној изградњи родитеља, сви прорачуни морају бити максимално лагани. Ако је потребно извршити ресурсно захтевну операцију — извуците је у засебан Isolate или кеширајте резултат у пољу State-а.
Трећа грешка — изостанак позива super.initState() и super.dispose(). Прегазећи ове методе, програмер је дужан да позове родитељску имплементацију. Ако се то не уради, оквир неће моћи исправно да управља стањем Element-а, што ће довести до тешко уочљивих грешака.
mounted пре setState-а у асинхроним колбецимаsuper.initState() и super.dispose()Често постављана питања
StatefulWidget може да мења своје стање путем setState-а, има животни циклус (initState, dispose) и креира засебан State објекат. StatelessWidget не може да мења стање и нема методе животног циклуса — он само приказује прослеђене податке.
createState се позива тачно једном за сваку инстанцу StatefulElement-а. Чак и ако се родитељ вишеструко поново гради, док се тип и Key виџета не промене, createState се не позива — користи се постојећи State објекат.
Ресурси се неће ослободити: контролери ће наставити да раде у позадини, претплате на токове ће остати активне, тајмери неће бити отказани. То доводи до цурења меморије и може изазвати позив setState-а након dispose-а, што баца изузетак.
Да, конструктор StatefulWidget-а може бити const. Међутим, ово не даје исту корист као за StatelessWidget — State објекат ће свеједно бити креиран при првом уграђивању. const утиче само на сам виџет (лаки омотач), не на State.
didUpdateWidget се позива када родитељ прослеђује StatefulWidget са новим параметрима. То је потребно да би се стање синхронизовало са новим подацима — на пример, ако се userId у параметрима променио, потребно је учитати профил новог корисника.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође