State: czym jest, zarządzanie stanem i zasada działania

Autor: IT Sectr Opublikowano: 2026-07-01 Czas czytania: 9 min

State — centralny obiekt zarządzania danymi we Flutter, powiązany z StatefulWidget i odpowiedzialny za przechowywanie zmiennych informacji oraz budowanie interfejsu. Według oficjalnej dokumentacji Flutter (Flutter.dev, 2026), State istnieje przez cały cykl życia widgetu i przetrwa jego przebudowy, zapewniając spójność danych między aktualizacjami UI. W przeciwieństwie do samego widgetu, State może zmieniać swoje pola i inicjować przebudowę przez wywołanie setState.

Najważniejsze

  • State — obiekt przechowujący zmienne dane StatefulWidget i zarządzający jego przebudową przez setState
  • Cykl życia — State przechodzi przez initState, didChangeDependencies, build, didUpdateWidget i dispose, każdy etap z jasnym przeznaczeniem
  • mounted — flaga wskazująca, że State wciąż znajduje się w drzewie widgetów i może bezpiecznie wywoływać setState
  • widget — referencja do powiązanego StatefulWidget, dostępna przez właściwość State do odczytu parametrów rodzica
  • Izolacja — State jest izolowany od innych State; do wymiany danych używa się InheritedWidget lub zewnętrznych narzędzi zarządzania stanem

Czym jest State we Flutter?

State — to obiekt w architekturze Flutter, który przechowuje zmienne dane StatefulWidget i określa, jak te dane są wyświetlane w interfejsie. Każdy StatefulWidget podczas wbudowywania w drzewo tworzy dokładnie jeden obiekt State przez metodę createState. State istnieje niezależnie od widgetu: jeśli rodzic przebudowuje StatefulWidget z nowymi parametrami, State pozostaje ten sam i otrzymuje zaktualizowany widget przez właściwość widget.

Według Flutter Architectural Overview (Google, 2026), rozdzielenie Widget i State to świadoma decyzja architektoniczna, pozwalająca frameworkowi na ponowne wykorzystanie elementów drzewa. Widget (lekki opis) może być tworzony i niszczony wielokrotnie, ale State (ciężki obiekt z danymi) pozostaje w pamięci, dopóki element znajduje się w drzewie. Zapobiega to utracie danych przy częstych przebudowach widgetów nadrzędnych.

State implementuje interfejs StatefulWidget przez generyk: class _MyState extends State<MyWidget>. Generyk wiąże State z konkretnym typem StatefulWidget, zapewniając bezpieczny typowo dostęp do jego pól przez właściwość widget.

Gdzie przechowywany jest State?

Obiekt State jest przechowywany w StatefulElement — pośredniej warstwie między Widget a RenderObject. StatefulElement tworzy State przez createState, zapisuje referencję do niego i przekazuje State jako właściciela. Element jest niszczony dopiero, gdy widget zostanie usunięty z drzewa — do tego momentu State żyje w pamięci.

Cykl życia State

Cykl życia State jest deterministyczny i składa się ze ścisłej sekwencji wywołań. Zrozumienie tej sekwencji to podstawa poprawnej pracy z zasobami i zapobiegania wyciekom pamięci.

initState — inicjalizacja

initState jest wywoływany jako pierwszy przy tworzeniu State. W tej metodzie inicjalizuje się kontrolery, subskrypcje strumieni danych, timery i początkowe wartości pól. Wymagane jest wywołanie super.initState() w pierwszej linii. Na etapie initState drzewo widgetów nie jest jeszcze w pełni zamontowane, więc metody takie jak MediaQuery.of(context) mogą działać nieprawidłowo.

didChangeDependencies

didChangeDependencies jest wywoływany po initState i przy każdej zmianie zależności InheritedWidget. To tutaj, a nie w initState, należy wywoływać MediaQuery.of(context) lub Theme.of(context), ponieważ w tym momencie drzewo jest już zamontowane. Ta metoda jest również wywoływana, jeśli widget zostanie przeniesiony do innego kontekstu, gdzie InheritedWidget dostarcza inne wartości.

build — budowanie UI

build — główna metoda State, zwracająca drzewo widgetów. Wywoływana po initState, po didChangeDependencies i po każdym setState. Metoda build nie powinna mieć efektów ubocznych — tylko opisuje interfejs na podstawie bieżących wartości pól State.

didUpdateWidget

didUpdateWidget jest wywoływany, gdy rodzic przebudowuje StatefulWidget z nowymi parametrami. State uzyskuje dostęp do starego widgetu przez oldWidget i może porównać go z nowym. Jeśli parametry się zmieniły, można zaktualizować stan, załadować nowe dane lub zrestartować animację.

dispose — zwalnianie zasobów

dispose — metoda końcowa, w której zwalniane są wszystkie zasoby: kontrolery, subskrypcje, timery. Po dispose State jest oznaczany jako martwy: mounted zwraca false, wywołanie setState rzuca wyjątek. Wymagane jest wywołanie super.dispose() w ostatniej linii metody.

MetodaKiedy wywoływanaWymagany super
initStatePrzy tworzeniu StateTak, w pierwszej linii
didChangeDependenciesPo initState i przy zmianie InheritedWidgetTak
buildPo initState, didChangeDependencies, setStateNie
didUpdateWidgetPrzy nowym widgetcie od rodzicaTak
setStateNa wywołanie programistyNie
disposePrzy usunięciu z drzewaTak, w ostatniej linii

Jak działa State?

Mechanizm działania State opiera się na trzech kluczowych zasadach: asocjacji z Element, reaktywności przez setState i dostępie do rodzica przez właściwość widget. Gdy Flutter buduje drzewo elementów i napotyka StatefulElement, wywołuje createState powiązanego widgetu. Utworzony State jest zapisywany w elemencie i istnieje, dopóki element nie zostanie usunięty.

Przy wywołaniu setState State oznacza siebie jako „brudny„ (dirty) i planuje przebudowę na następną klatkę. Ważne: setState nie wywołuje build natychmiast — tylko rejestruje konieczność przebudowy. Flutter zbiera wszystkie brudne elementy w bieżącej klatce i przebudowuje je zbiorczo, co optymalizuje wydajność. Po wywołaniu build State wraca do stanu „czysty„ (clean).

Właściwość widget pozwala State odczytywać parametry przekazane do konstruktora StatefulWidget. Ponieważ StatefulWidget jest niemutowalny (jak StatelessWidget), jego pola się nie zmieniają — przy zmianie parametrów rodzic tworzy nowy widget, a State otrzymuje go przez didUpdateWidget. Gwarantuje to, że State zawsze pracuje z aktualnymi danymi rodzica.

Przykłady kodu w Dart

Podstawowy przykład State z polem zmienianym przez timer. Demonstruje initState, setState i dispose:

dart
class _TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  Timer? _timer;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(
      const Duration(seconds: 1),
      (_) => setState(() => _seconds++),
    );
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Text('$_seconds sekund upłynęło');
  }
}

Przykład z użyciem właściwości widget do dostępu do parametrów rodzica i reagowania na ich zmiany przez didUpdateWidget:

dart
class _GreetingState extends State<GreetingWidget> {
  String _displayName = '';

  @override
  void initState() {
    super.initState();
    _displayName = _formatName(widget.name);
  }

  @override
  void didUpdateWidget(GreetingWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.name != oldWidget.name) {
      setState(() {
        _displayName = _formatName(widget.name);
      });
    }
  }

  String _formatName(String name) => name.trim().isEmpty ? 'Guest' : name;

  @override
  Widget build(BuildContext context) {
    return Text('Witaj, $_displayName!');
  }
}

W drugim przykładzie State śledzi zmianę parametru wejściowego name i przeformatowuje wyświetlanie tylko przy rzeczywistej zmianie. Bez sprawdzenia widget.name != oldWidget.name metoda byłaby wywoływana przy każdej przebudowie rodzica, nawet jeśli nazwa się nie zmieniła — to zbędna praca dla frameworka.

State vs StatefulWidget

State i StatefulWidget to dwie różne klasy w architekturze Flutter, pełniące różne role. StatefulWidget — to lekka niemutowalna otoczka, która opisuje konfigurację widgetu i tworzy State. State — to ciężki obiekt, który przechowuje zmienne dane, zarządza subskrypcjami i buduje UI. Takie rozdzielenie pozwala Flutter niszczyć i tworzyć widgety bez utraty stanu.

Wszystkie pola StatefulWidget muszą być final i ustawiane w konstruktorze — nie zmieniają się po utworzeniu. State, przeciwnie, może zmieniać swoje pola w dowolnym momencie, ale wszystkie zmiany muszą być poprzedzone wywołaniem setState, aby Flutter dowiedział się o konieczności przebudowy. To kluczowa różnica: StatefulWidget — to „co pokazać„, State — „jak pokazać i jakie dane użyć„.

Według Flutter source code analysis (Flutter SDK, 2026), StatefulWidget zawiera tylko jedno obowiązkowe pole — createState, podczas gdy State ma dostęp do BuildContext, może subskrybować strumienie, zarządzać animacjami i kontrolerami. Zaleca się utrzymywanie StatefulWidget jak najprostszym, przenosząc całą logikę do State.

Dlaczego StatefulWidget nie może być State?

Rozdzielenie Widget i State — decyzja architektoniczna zapewniająca niemutowalność konfiguracji. Gdyby StatefulWidget sam przechowywał stan, przy każdej przebudowie rodzica stan by się tracił. Wyodrębniając stan do osobnego obiektu, Flutter gwarantuje, że dane przetrwają przebudowy, a widgety pozostają lekkie i porównywalne.

Zarządzanie stanem między widgetami

Obiekt State jest izolowany — nie ma bezpośredniego dostępu do State innych widgetów. Do wymiany danych między widgetami używa się InheritedWidget lub zewnętrznych narzędzi zarządzania stanem: Provider, Riverpod, Bloc, Redux. Każde podejście rozwiązuje zadanie na swój sposób: InheritedWidget działa przez drzewo widgetów, Provider — przez kontener DI, Bloc — przez strumienie zdarzeń.

Wybór narzędzia zależy od skali projektu. Dla małej aplikacji wystarczy InheritedWidget i lokalny State. Dla średniego i dużego projektu zaleca się Riverpod lub Bloc — zapewniają one testowalność, przewidywalność i oddzielenie logiki od UI. State przy tym jest używany tylko dla lokalnych danych widgetu (focus, scroll, animacja).

Według Flutter Community Survey 2025 (Flutter Foundation, grudzień 2025), Riverpod jest najpopularniejszym rozwiązaniem do zarządzania stanem w nowych projektach (38%), za nim plasują się Bloc (31%) i Provider (22%). Wszystkie trzy narzędzia są kompatybilne ze State i nie wymagają rezygnacji ze standardowego cyklu życia.

Stan lokalny vs globalny

  • Lokalny stan — w State konkretnego widgetu (pozycja scrolla, stan fokusa)
  • Globalny stan — w zewnętrznym magazynie (dane użytkownika, ustawienia, pamięć podręczna)
  • Zasada: jeśli dane są używane tylko przez jeden widget — przechowuj w State
  • Jeśli dane są używane przez 2+ widgety — wynieś do Riverpod/Bloc/Provider

Typowe błędy

Pierwszy błąd — zapomnieć sprawdzić mounted przed setState w asynchronicznym callbacku. Gdy widget został usunięty z drzewa (np. użytkownik opuścił ekran), ale operacja asynchroniczna (żądanie HTTP) wciąż jest wykonywana, po jej zakończeniu State jest już martwy. Wywołanie setState w martwym State rzuca wyjątek. Sprawdzenie if (mounted) setState(...) rozwiązuje problem.

Drugi błąd — inicjalizacja zależności InheritedWidget w initState zamiast w didChangeDependencies. W initState kontekst nie jest jeszcze zamontowany, więc MediaQuery.of(context) rzuci wyjątek. Wszystkie zależności od InheritedWidget powinny być konfigurowane w didChangeDependencies lub w build.

Trzeci błąd — mutacja pól bez wywołania setState. Jeśli programista zmienia pole State bez setState, Flutter nie dowie się o zmianie i UI nie zostanie zaktualizowane. Na przykład: _list.add(item) bez późniejszego setState((){}) zmieni listę, ale ekran pozostanie taki sam.

Sprawdzenie mounted przed setState

Wzorzec bezpieczeństwa dla operacji asynchronicznych w State:

dart
Future<void> _fetchData() async {
  final data = await ApiService.fetch();
  if (mounted) {
    setState(() => _data = data);
  }
}

Sprawdzenie mounted gwarantuje, że setState jest wywoływany tylko dla żywego State, zapobiegając wyjątkowi „setState called after dispose„.

Często zadawane pytania

Czym State różni się od StatefulWidget?

StatefulWidget — niemutowalna konfiguracja widgetu, a State — zmienny obiekt przechowujący dane i zarządzający cyklem życia. Widget może być odtworzony, State — nie. StatefulWidget tworzy State przez createState.

Ile obiektów State tworzy się dla jednego StatefulWidget?

Dokładnie jeden. Metoda createState jest wywoływana jednorazowo przy pierwszym wbudowaniu StatefulWidget w drzewo. Nawet jeśli rodzic przebudowuje się wielokrotnie, obiekt State pozostaje ten sam, dopóki nie zmieni się typ lub Key widgetu.

Czym jest mounted w State?

mounted — flaga logiczna pokazująca, czy State znajduje się w drzewie widgetów. Po wywołaniu dispose mounted staje się false. Używana do sprawdzenia przed setState w asynchronicznych callbackach, aby uniknąć wyjątku.

Czy można używać State bez StatefulWidget?

Nie. State jest zawsze powiązany z konkretnym StatefulWidget przez generyk: State<T extends StatefulWidget>. Utworzenie State bezpośrednio, bez asocjacji z widgetem, jest architektonicznie niemożliwe.

Co się stanie przy wywołaniu setState w dispose?

Zostanie rzucony wyjątek „setState called after dispose„. Po wywołaniu dispose State jest uważany za martwy i wszelkie próby przebudowy UI przez setState są zabronione. Rozwiązanie — sprawdzać mounted przed każdym setState.

Podsumowanie

  • State — obiekt zarządzania danymi StatefulWidget, przechowujący zmienne pola i inicjujący przebudowę UI przez setState
  • Cykl życia obejmuje obowiązkowe metody initState, didChangeDependencies, build, didUpdateWidget i dispose, każda ze swoim przeznaczeniem
  • mounted — krytyczna flaga bezpieczeństwa zapobiegająca wywołaniu setState po usunięciu widgetu z drzewa
  • widget — właściwość State do dostępu do parametrów powiązanego StatefulWidget, aktualizowana przez didUpdateWidget
  • Izolacja — State nie ma dostępu do innych State; interakcja między widgetami realizowana przez InheritedWidget lub zewnętrzne narzędzia
  • setState — nie wywołuje build natychmiast, a jedynie oznacza State jako brudny do przebudowy w następnej klatce
  • Zasada — używaj State dla lokalnych danych widgetu; stan globalny wynoś do zewnętrznych warstw (Riverpod, Bloc)

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również