StatefulWidget — widget Flutter s měnitelným stavem, který umožňuje UI reagovat na akce uživatele, asynchronní události a datové toky. Podle oficiální dokumentace Flutter (Flutter.dev, 2026) se StatefulWidget používá pro všechny interaktivní prvky aplikace: vstupní formuláře, animace, zaškrtávací políčka, přepínače a obrazovky načítající data ze sítě. Na rozdíl od StatelessWidget vytváří samostatný objekt State, který je uchováván po celý životní cyklus a může být znovu sestaven bez opětovného vytvoření samotného widgetu.
Hlavní body
StatefulWidget — je třída Flutter, která může měnit svůj stav v reakci na akce uživatele, systémové události nebo asynchronní operace. Na rozdíl od StatelessWidget není StatefulWidget zobrazen přímo — vytváří objekt State, který je zodpovědný za vykreslování. Rozdělení do dvou tříd (Widget a State) umožňuje Flutter přestavět UI bez opětovného vytvoření samotného widgetu, což přináší významnou výhodu v výkonu při častých aktualizacích.
Architektura StatefulWidget následuje vzor «oddělení měnitelného a neměnného»: samotný widget zůstává neměnný (jako StatelessWidget) a veškerý měnitelný stav je uložen v samostatném objektu State. To umožňuje Flutter znovu používat widgety porovnáváním podle typu a Key a zároveň udržovat aktuální stav mezi přestavbami.
Podle Google (Flutter Architectural Overview, 2026) je StatefulWidget optimální pro scénáře, kde se stav mění více než jednou za životnost widgetu: textová pole, animace, časovače, datové toky, asynchronní načítání. Pro jednorázovou inicializaci stačí StatelessWidget.
StatefulWidget je povinný, když widget musí reagovat na vnější události: stisk tlačítka, dokončení HTTP požadavku, aktualizace dat z databáze, předplatné WebSocket. Je také nezbytný pro widgety s animací, textová pole s ovladači a komponenty spravující fokus. Pokud widget pouze zobrazuje data a negeneruje události — použijte StatelessWidget.
StatefulWidget se skládá ze dvou tříd: samotného StatefulWidget (lehký, neměnný) a State (těžký, měnitelný). Framework vytváří State pomocí metody createState(), která je volána jednou při vložení do stromu. State získává odkaz na widget prostřednictvím vlastnosti widget a může přistupovat k jeho polím kdykoli během životního cyklu.
Životní cyklus StatefulWidget se skládá ze šesti hlavních fází, z nichž každá poskytuje přepisovatelnou metodu pro provádění specifických úkolů. Pochopení těchto fází je kriticky důležité pro správnou práci se zdroji a předcházení únikům paměti.
createState — první metoda životního cyklu, volaná při vložení StatefulWidget do stromu. Musí vrátit novou instanci State spojenou s tímto widgetem. Tato metoda je volána přesně jednou za celou dobu života prvku. Je důležité zde neprovádět těžké operace — createState by měl být co nejlehčí.
initState — je volán ihned po vytvoření State, před první stavbou UI. Zde se provádí: inicializace ovladačů (TextEditingController, AnimationController), předplatné datových toků (StreamSubscription), nastavení časovačů a počáteční inicializace polí. Podle Flutter docs (Flutter.dev, 2026) nelze v initState volat BuildContext.of() — strom ještě není plně připojen.
didChangeDependencies — je volán po initState a pokaždé, když se změní závislosti InheritedWidget. Toto je vhodné místo pro volání MediaQuery.of(context) nebo předplatné Theme — hodnoty, které se mohou změnit během běhu aplikace. Pokud widget používá InheritedWidget, logika inicializace by měla být zde, ne v initState.
build — hlavní metoda vracející strom widgetů. Volána po initState, po didChangeDependencies a po každém setState. didUpdateWidget je volán, když rodič přestavuje a předává StatefulWidget s novými parametry. Zde můžete porovnat stará a nová pole widgetu a v případě potřeby aktualizovat stav.
dispose — závěrečná fáze životního cyklu. Zde jsou uvolněny všechny zdroje: odhlášení z toků, odstranění ovladačů, zrušení časovačů. Nevolání dispose vede k únikům paměti. Po dispose je State považován za mrtvý — volání setState uvnitř něj vyvolá výjimku.
Mechanismus fungování StatefulWidget je založen na koordinované práci tří entit: Widget (lehký popis), Element (mezivrstva) a State (úložiště dat). Když Flutter narazí na StatefulWidget v popisu, vytvoří StatefulElement, který volá createState a ukládá odkaz na objekt State. Při přestavbě rodiče Flutter porovnává nový widget s aktuálním Elementem — pokud se typ a Key shodují, Element je aktualizován a State zůstává stejný.
Stav se mění pouze voláním setState, které informuje framework o nutnosti přestavby. Je důležité pochopit: setState nemění stav automaticky — pouze označí widget jako «špinavý». Vývojář sám aktualizuje pole State v callbacku předaném setState. Po dokončení callbacku Flutter zavolá build a aktualizuje UI.
Podle týmu Dart/Flutter (Dart Language Specification, 2026) toto oddělení zaručuje, že všechny změny stavu probíhají synchronně před voláním build, což vylučuje situaci, kdy UI zobrazuje částečně aktualizovaná data. To je klíčový mechanismus konzistence rozhraní ve Flutter.
Podívejme se na jednoduchý StatefulWidget — počítadlo kliknutí na tlačítko. Ukazuje základní vzor: vytvoření State, inicializace pole v initState, změna prostřednictvím 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('Počet: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('Zvýšit'),
),
],
);
}
}
Příklad s asynchronním načítáním dat a správou životního cyklu. StatefulWidget načítá data ze sítě a zobrazuje stav načítání:
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('Ahoj, ${_user!.name}');
}
}
Ve druhém příkladu je důležité poznamenat: initState spouští asynchronní operaci, ale samotná metoda není asynchronní. Asynchronnost je realizována pomocí async/await uvnitř samostatné metody _loadUser, která aktualizuje stav pomocí setState po dokončení požadavku. Tento přístup zaručuje, že widget správně zobrazí indikátor načítání před obdržením dat.
Volba mezi StatefulWidget a StatelessWidget není jen otázkou existence stavu. StatefulWidget poskytuje plný životní cyklus s metodami initState, didChangeDependencies, didUpdateWidget a dispose, což je nezbytné pro práci s ovladači, animacemi a toky. StatelessWidget naproti tomu tyto metody nemá a je vždy lehčí pro framework.
Doporučení týmu Flutter (Flutter docs, 2026) — minimalizujte počet StatefulWidget v aplikaci zvedáním stavu nahoru ve stromu (State Hoisting) nebo pomocí řešení pro správu stavu (Riverpod, Bloc, Provider). Každý StatefulWidget vytváří objekt State, který žije až do odstranění prvku — čím více takových widgetů, tím vyšší je zatížení paměti.
| Kritérium | StatefulWidget | StatelessWidget |
|---|---|---|
| Stav | Měnitelný | Neměnný |
| Životní cyklus | 6 fází | Pouze build |
| Objekt State | Vytvářen samostatně | Není vyžadován |
| setState | Dostupný | Nedostupný |
| Předplatná | initState/dispose | Nejsou podporována |
| const konstruktor | Omezený | Plně podporován |
| Spotřeba paměti | Vyšší | Nižší |
StatefulWidget vyžaduje více zdrojů než StatelessWidget kvůli potřebě vytvářet a udržovat objekt State. Správné používání StatefulWidget však nevede k problémům s výkonem, pokud se dodržuje několik pravidel. Za prvé, vyhněte se hlubokému vnoření StatefulWidget — každá úroveň přidává režii pro procházení stromu. Za druhé, rozdělte složitý StatefulWidget na několik jednoduchých widgetů, z nichž každý je zodpovědný za svou část stavu.
Podle výzkumu Flutter Performance (Flutter.dev, únor 2026) je nejčastější příčinou poklesu FPS volání setState v nadřazeném widgetu, které přestavuje všechny potomky, včetně StatelessWidget, které nezměnily své zobrazení. Řešením je přesunout měnitelnou část UI do samostatného StatefulWidget, aby setState přestavěl pouze minimum potřebných widgetů.
Použití const uvnitř State je další důležitá technika. Pokud jsou podřízené widgety deklarovány jako const, Flutter je nebude přestavovat při volání setState v rodiči. To snižuje zatížení frameworku a zkracuje dobu vykreslování snímku.
Každé volání setState spouští úplnou přestavbu widgetu. Pokud se stav mění s vysokou frekvencí (například animace nebo datový tok), zvažte použití AnimatedBuilder, ValueListenableBuilder nebo StreamBuilder místo ručního volání setState. Tyto widgety optimalizují přestavbu a aktualizují pouze tu část UI, která se skutečně změnila.
První typická chyba se StatefulWidget — volání setState po dispose. Když je widget odstraněn ze stromu, State je považován za mrtvý a každé volání setState vyvolá výjimku «setState called after dispose». Nejčastěji k tomu dochází, když asynchronní operace skončí po odstranění widgetu. Řešením je kontrola příznaku mounted před voláním setState nebo zrušení asynchronních operací v dispose.
Druhou chybou je provádění těžkých výpočtů v metodě build. Protože build je volán při každém setState a při každé přestavbě rodiče, všechny výpočty by měly být co nejlehčí. Pokud potřebujete provést operaci náročnou na zdroje — přesuňte ji do samostatného Isolate nebo uložte výsledek do pole State.
Třetí chybou je chybějící volání super.initState() a super.dispose(). Při přepisování těchto metod je vývojář povinen zavolat rodičovskou implementaci. Pokud se tak nestane, framework nebude schopen správně spravovat stav Element, což povede k obtížně odhalitelným chybám.
mounted před setState v asynchronních callbacksuper.initState() a super.dispose()Často kladené otázky
StatefulWidget může měnit svůj stav prostřednictvím setState, má životní cyklus (initState, dispose) a vytváří samostatný objekt State. StatelessWidget nemůže měnit stav a nemá metody životního cyklu — pouze zobrazuje předaná data.
createState je voláno přesně jednou pro každou instanci StatefulElement. I když rodič opakovaně přestavuje, dokud se typ a Key widgetu nemění, createState není voláno — používá se existující objekt State.
Zdroje se neuvolní: ovladače budou nadále pracovat na pozadí, předplatná toků zůstanou aktivní, časovače nebudou zrušeny. To vede k únikům paměti a může způsobit volání setState po dispose, což vyvolá výjimku.
Ano, konstruktor StatefulWidget může být const. Nepřináší to však stejnou výhodu jako pro StatelessWidget — objekt State bude stejně vytvořen při prvním vložení. const ovlivňuje pouze samotný widget (lehký obal), ne State.
didUpdateWidget je volán, když rodič předává StatefulWidget s novými parametry. To je potřeba k synchronizaci stavu s novými daty — například pokud se změnilo userId v parametrech, je třeba načíst profil nového uživatele.
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é