setState() — суштина, механизам рада и примена

Аутор: IT Sectr Објављено: 2026-07-01 Време читања: 9 мин

setState() — кључна метода класе State у Flutter-у, која обавештава оквир о промени података и покреће поновно изграђивање интерфејса. Према званичној документацији Flutter-а (Flutter.dev, 2026), setState је основни механизам реактивности у StatefulWidget-у: без његовог позивања, UI неће сазнати за промене поља State и остаће у претходном стању. Метода прима колбек VoidCallback, унутар кога програмер мења променљива поља, након чега Flutter аутоматски позива build за поновно изграђивање виџета.

Главно

  • setState() — метода State која означава виџет као прљав (dirty) и планира поновно изграђивање UI у следећем оквиру
  • Колбек — setState прима VoidCallback, унутар кога треба да буду све промене поља State које утичу на интерфејс
  • Асинхроност — setTimeout или Future унутар setState не гарантују синхроност; мутације после await морају бити унутар другог setState
  • Перформансе — свако позивање setState поново изграђује цео виџет; за минимизацију користите const за виџете потомке
  • mounted — пре позивања setState у асинхроним колбецима обавезно проверите mounted, иначе — изузетак

Шта је setState()?

setState() — уграђена метода класе State у Flutter-у, намењена обавештавању оквира да се унутрашње стање виџета променило и да је потребно поновно изграђивање UI. Без позивања setState, Flutter не зна за промене — чак и ако су поља State измењена, интерфејс ће остати непромењен до следећег принудног поновног изграђивања од стране родитеља.

Потпис методе: void setState(VoidCallback fn). Колбек се извршава синхроно унутар setState и тек након његовог завршетка, State се означава као dirty. Ово гарантује да се све промене примењују атомично пре поновног изграђивања. Према Dart Language Specification (Dart Team, 2026), атомичност setState спречава стање трке, у којем би build могао видети делимично ажурирано стање.

setState не прима аргументе, не враћа вредност и не може бити прегажен. Ово је коначна (sealed) метода класе State. Програмер не може променити њено понашање — може је само користити наменски. Покушај позивања setState ван State (на пример, из друге класе) је немогућ, јер је метода декларисана у класи State.

setState не мења стање — мењате ви

Честа заблуда — да setState сам мења стање. То није тачно. setState само позива прослеђени колбек (у којем програмер мења поља) и затим сигнализира оквиру потребу за build. Колбек је обавезан — прослеђивање null или празног колбека ће довести до грешке.

Како ради setState()?

Механизам рада setState() може се поделити на четири фазе. Прва — позивање методе са колбеком. Друга — синхроно извршавање колбека, унутар којег се мењају поља State. Трећа — State се означава као dirty у посебном пољу _dirty. Четврта — на крају тренутног микрозадатка (microtask), Flutter обилази све dirty-елементе и позива њихов build редом појављивања у стаблу.

Важан детаљ: setState не позива build одмах. Flutter користи стратегију групног ажурирања: сви dirty-елементи се прикупљају и поново изграђују у једном оквиру. То значи да, ако је setState позван више пута у оквиру истог синхроног блока, build ће се извршити само једном — након завршетка свих промена. Таква оптимизација спречава вишеструка поновна изграђивања у једном оквиру.

Према Flutter Engine Team (Google, 2025), механизам dirty-заставица заснива се на проласку кроз BuildOwner._dirtyElements. Сваки dirty StatefulElement се додаје на листу и обрађује у фази ажурирања оквира. Ако је виџет уклоњен из стабла пре обраде, аутоматски се искључује из листе dirty-елемената.

Гаранције setState

  • Колбек се извршава синхроно пре означавања dirty
  • build се позива највише једном по оквиру (чак и при вишеструким setState)
  • Ажурирање UI се дешава у следећем оквиру (обично ~16ms при 60 FPS)
  • Након dispose, позивање је забрањено — баца се изузетак
  • Током build, позивање setState је забрањено — бесконачна петља

Примери кода у Dart-у

Основни пример setState() са повећањем бројача. Демонстрира исправну употребу: промена поља унутар колбека:

dart
class _CounterState extends State<CounterWidget> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++; // мутирање поља унутар колбека
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _increment,
      child: Text('$_count'),
    );
  }
}

Пример са текстуалним пољем и контролером — setState() за управљање видљивошћу лозинке:

dart
class _PasswordFieldState extends State<PasswordField> {
  bool _obscured = true;
  final _controller = TextEditingController();

  void _toggleVisibility() {
    setState(() {
      _obscured = !_obscured;
    });
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      obscureText: _obscured,
      decoration: InputDecoration(
        suffixIcon: IconButton(
          icon: Icon(_obscured ? Icons.visibility : Icons.visibility_off),
          onPressed: _toggleVisibility,
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

У овом примеру setState() мења само булово поље _obscured, што изазива поновно изграђивање TextField-а са новом иконом и режимом приказа. Контролер текста се притом не рекреира — иницијализован је једном у initState и ослобађа се у dispose.

Вишеструке мутације у једном setState

Ако треба променити више поља, све промене се извршавају унутар једног setState. Ово гарантује да ће build видети конзистентно стање:

dart
setState(() {
  _isLoading = false;
  _items = newItems;
  _error = null;
});

Три поља се мењају у једном колбеку — build ће се извршити једном и видети све промене истовремено. Да је свако позивање било засебан setState, build би се ипак извршио једнократно захваљујући групној обради dirty-елемената.

Асинхроност и setState

Једна од најважнијих нијанси setState() — његово понашање са асинхроним операцијама. Колбек setState се извршава синхроно, али ако се унутар њега позове await, код после await ће се извршити након што setState заврши рад. То значи да промене поља после await неће бити захваћене тренутним setState.

Исправан приступ: асинхрона операција се извршава ван setState, а setState се позива након њеног завршетка. Сав код између добијања резултата и позивања setState извршава се у синхроном контексту после await:

dart
// ИСПРАВНО: await ван setState
Future<void> _loadData() async {
  final result = await ApiService.fetchData();
  setState(() {
    _data = result;
    _isLoading = false;
  });
}

// ПОГРЕШНО: await унутар setState — нема гаранције ажурирања
void _loadDataWrong() {
  setState(() async {
    _data = await ApiService.fetchData(); // setState се враћа пре завршетка await
    _isLoading = false; // овај код није захваћен од стране setState
  });
}

Према Flutter документима (Dart async patterns, 2026), прослеђивање async-колбека у setState је антиобразац, јер setState очекује VoidCallback (синхрону функцију), а async-функција враћа Future који се игнорише. Промене после првог await у таквом колбеку неће бити исправно обрађене од стране оквира.

mounted провера у асинхроним сценаријима

Пре позивања setState() после асинхроне операције увек проверите mounted:

dart
if (mounted) {
  setState(() => _data = data);
}

Ако је виџет уклоњен из стабла током извршавања асинхроне операције, mounted ће постати false и setState се неће позвати. Ово спречава изузетак и цурење ресурса.

Перформансе и оптимизација

setState() — згодан, али потенцијално скуп механизам, ако се користи без размишљања. Свако позивање setState поново изграђује цео виџет и све његове потомке (ако нису const). У дубоким стаблима или при честим позивањима, ово може довести до пада FPS-а.

Главне стратегије оптимизације: минимизирати област поновног изграђивања (издвајати променљиве делове UI у засебне StatefulWidget), користити const за непроменљиве потомке и избегавати позивање setState у родитељским виџетима ако се променио само мали UX детаљ. Ако се стање ажурира високом фреквенцијом (анимација, ток података), размотрите AnimatedBuilder или ValueListenableBuilder.

Према Flutter Performance Best Practices (Flutter.dev, фебруар 2026), профилисање стварних апликација показује да се до 40% свих позивања setState може заменити const-виџетима потомцима или реактивним билдерима (StreamBuilder, FutureBuilder). Ово смањује просечно време изградње оквира за 15–25%.

Када је setState сувишан

СценариоАлтернативаПредност
АнимацијаAnimatedBuilderПоново изграђује само анимирани виџет
Ток податакаStreamBuilderРеагује на сваки елемент тока
Будући резултатFutureBuilderУправља стањима учитавања/грешке
Локална вредностValueListenableBuilderРеагује на промене једне вредности

Алтернативе за setState

Упркос свестраности setState(), у великим пројектима се углавном користи за локално стање. За глобално или дељено стање користе се специјализована решења, од којих свако замењује или облаже setState.

Provider користи ChangeNotifier + notifyListeners као аналог setState, али са могућношћу претплате више виџета. Bloc користи Streams — стање се мења додавањем догађаја у StreamController. Riverpod комбинује приступе, пружајући и локално (StateProvider) и асинхроно (AsyncNotifier) управљање без везивања за StatefulWidget. Сва три приступа уклањају потребу за ручним позивањем setState — ажурирање UI се дешава аутоматски при промени података.

Према Flutter Community Survey 2025 (Flutter Foundation, децембар 2025), 74% програмера користи барем један алат за управљање стањем поред setState. Истовремено, 92% наставља да користи setState за локалне податке текстуалног поља, чејкбокса или једноставног бројача — ово се сматра најбољом праксом.

Када задржати setState

  • Стање користи само један виџет
  • Једноставна булова или нумеричка вредност (фокус, видљивост, бројач)
  • Прототипирање и брзи експерименти
  • Контролери (TextEditingController, PageController) ионако захтевају StatefulWidget

Типичне грешке

Прва и најопаснија грешка — позивање setState после dispose. Асинхрона операција је започела у initState, корисник је напустио екран, виџет је уклоњен, а колбек асинхроне операције позива setState — апликација пада са изузетком. Решење — увек проверите mounted пре позивања.

Друга грешка — позивање setState унутар build. Ово доводи до бесконачне петље: build → setState → dirty → build → setState → ... Flutter не блокира такав позив (добићете StackOverflowError). setState се може позвати само као одговор на догађај (притисак дугмета, завршетак Future, пријем података из тока).

Трећа грешка — промена поља State без позивања setState. Програмер пише _count++ и очекује да ће се UI ажурирати. Flutter не може аутоматски да прати промене поља — потребан му је експлицитан сигнал кроз setState. Ово је фундаментална разлика од реактивних оквира попут Vue.js, где промена података аутоматски покреће ажурирање.

Четврта грешка — позивање setState са асинхроним колбеком (async-ламбда). Као што је описано у одељку о асинхроности, промене после await неће бити захваћене, што доводи до грешака које је тешко репродуковати. Користите синхрони колбек и позовите setState после await.

Подсетник за безбедан setState

  • Увек проверите mounted у асинхроним колбецима
  • Не позивајте setState унутар build
  • Не прослеђујте async-ламбду у setState
  • Не мењајте поља State ван setState
  • Ако мењате више поља — радите то у једном setState

Често постављана питања

Шта ради setState() у Flutter-у?

setState() обавештава Flutter да су се унутрашњи подаци StatefulWidget-а променили и да UI треба поново изградити. Метода прима колбек, извршава га синхроно, означава виџет као dirty и планира позив build у следећем оквиру.

Шта ће се десити ако не позовем setState након промене поља?

UI се неће ажурирати. Flutter не прати промене поља аутоматски. Вредност поља ће се променити у меморији, али ће виџет остати у претходном стању до следећег принудног поновног изграђивања од стране родитеља.

Може ли се позвати setState унутар build?

Не може. Ово ће довести до бесконачне петље: build позива setState, који означава виџет као dirty и поново позива build. Flutter не блокира такву ситуацију — апликација ће пасти са StackOverflowError.

Колико пута ће се извршити build при два узастопна setState?

Build ће се извршити једном. Flutter прикупља све dirty-елементе и поново их изграђује групно на крају оквира. Други setState пре обраде првог једноставно додаје елемент у исту листу dirty-елемената — поновно изграђивање се неће десити.

Шта је mounted и зашто је важан за setState?

mounted — булова заставица која показује да је виџет још увек у стаблу. Ако после асинхроне операције позовете setState без провере mounted, а виџет је већ уклоњен — апликација ће пасти са изузетком „setState called after dispose”.

Резиме

  • setState() — метода State која обавештава Flutter о промени података и покреће поновно изграђивање UI у следећем оквиру
  • Механизам рада — синхроно извршавање колбека, означавање State као dirty, групно поновно изграђивање свих dirty-елемената на крају оквира
  • Асинхроност — async-колбеци у setState не раде; await мора бити споља, а setState — након добијања резултата
  • mounted — обавезна провера пре setState у асинхроним операцијама ради спречавања изузетка
  • Оптимизација — минимизирајте област поновног изграђивања кроз const виџете потомке и преместите анимације у AnimatedBuilder
  • Алтернативе — за глобално стање користите Riverpod, Bloc или Provider; setState оставите за локалне податке
  • Правило — не позивајте setState унутар build, не прослеђујте async-ламбде, увек проверавајте mounted

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође