Что такое StatefulWidget и как он работает?

Ответ

StatefulWidget — это виджет, который хранит изменяемое состояние (State) в течение своего жизненного цикла. Он используется для частей UI, которые должны динамически реагировать на данные, пользовательский ввод или другие события.

Ключевая механика: StatefulWidget состоит из двух классов:

  1. Сам виджет (иммутабельный, пересоздаётся при перестроении родителя).
  2. Объект State (мутабельный, сохраняется между перестроениями виджета).

Пример счётчика:

class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0; // Состояние хранится здесь

  void _increment() {
    // setState уведомляет фреймворк об изменении состояния
    // и запускает перестроение виджета
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('Текущее значение: $_count'),
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Увеличить'),
        ),
      ],
    );
  }
}

Жизненный цикл State:

  • initState(): Вызывается один раз при создании объекта состояния. Идеально для инициализации данных, подписки на Stream.
  • didUpdateWidget(oldWidget): Вызывается при перестроении родительского виджета с новыми свойствами.
  • setState(fn): Планирует перестроение виджета с обновлённым состоянием.
  • dispose(): Вызывается перед удалением состояния. Обязателен для отписки от таймеров, StreamSubscription, чтобы избежать утечек памяти.

Когда использовать: Для форм, анимаций, таймеров или любого компонента с внутренним, изменяемым состоянием. Для сложного состояния приложения предпочтительнее использовать менеджеры состояний (BLoC, Provider).

Ответ 18+ 🔞

А, ну это же классика, ёпта! StatefulWidget — это как тот чувак, который приходит на вечеринку и тащит с собой свой личный холодильник с пивом. Все остальные виджеты (Stateless) — они как гости: пришли, постояли, выпили что дали, и разошлись. А этот — свой стул, свою закуску, свою музыку. И главное, он этот холодильник (состояние, State) не бросает, пока его самого нахуй не выгонят.

В чём прикол, блядь: StatefulWidget — это на самом деле две рожи в одном флаконе.

  1. Сам виджет — это просто бумажка, инструкция. Его фреймворк может выкинуть и пересоздать на каждом чихе. Мудак беспамятный, короче.
  2. А вот объект State — это уже память, это хранилище. Это тот самый холодильник. Он живёт, пока живёт этот кусок интерфейса. В нём и хранится вся движуха: счётчики, текст из полей, флажки — всё, что меняется.

Смотри, как это выглядит на практике, обычный счётчик:

class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0; // Вот оно, состояние! Сидит тут, в тепле.

  void _increment() {
    // А вот магический пендель для фреймворка!
    setState(() {
      _count++; // Меняем цифру...
    });
    // ...и кричим: "Ёб твою мать, Flutter, я тут поменял кое-что, перерисуйся давай!"
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('Текущее значение: $_count'), // Сюда цифра и встаёт.
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Увеличить'),
        ),
      ],
    );
  }
}

А теперь про жизнь и смерть объекта State, это важно, блядь:

  • initState(): Вызывается один раз, когда объект состояния только родился. Как первый вдох. Тут ты подписываешься на стримы, запускаешь таймеры, грузишь данные из сети. Делаешь всё, чтобы виджет ожил. Запомни: BuildContext тут ещё не готов для навигации! Не лезь с ним, обосрёшься.
  • didUpdateWidget(oldWidget): Вызывается, когда родительский виджет пересоздали с новыми параметрами. Типа: "Слушай, а вот тебе новая инструкция, старую нахуй выкинули. Сам-то ты остался, но посмотри, может, там что поменялось". Тут можно сверить старые и новые свойства.
  • setState(fn): Это твоя волшебная дубинка. Внутри неё ты меняешь переменные, а потом Flutter, получив по башке, идёт и перерисовывает виджет с новыми данными. Без setState() ты можешь хоть до овердохуища менять переменные — на экране нихуя не изменится.
  • dispose(): Финальный аккорд. Вызывается прямо перед тем, как виджет накроется медным тазом и его выкинут в помойку. Здесь ОБЯЗАТЕЛЬНО надо отписаться ото всех стримов, отменить все таймеры и почистить за собой! Иначе будут утечки памяти, а это, я тебе скажу, пиздец как некрасиво. Твой виджет умрёт, а его призрак будет в памяти болтаться и жрать ресурсы.

Когда эту штуку юзать? Да когда угодно, где есть хоть какое-то движение. Форма ввода? StatefulWidget. Анимация кнопки? StatefulWidget. Простой таймер? StatefulWidget. Но вот если состояние начинает разрастаться, как хитрая жопа, и его нужно таскать между десятью экранами — это уже повод задуматься о нормальных state-менеджерах вроде Provider или BLoC. А то получится пиздопроебибна, где StatefulWidget'ы друг другу в жопу данные передают через костыли и глобальные переменные.