Расскажи про жизненный цикл виджета в Flutter

Ответ

В Flutter есть два фундаментальных типа виджетов с разными жизненными циклами: StatelessWidget и StatefulWidget.

StatelessWidget

Иммутабельный виджет, который описывает часть UI, зависящую только от своих конфигурационных параметров (final поля, переданные в конструктор). Его жизненный цикл прост:

  1. Конструктор: Принимает key и другие параметры.
  2. build(BuildContext context): Вызывается фреймворком, когда виджет должен отрисоваться.
  3. Уничтожение: Экземпляр может быть собран сборщиком мусора. Если внутри build создаются подписки или контроллеры (что не рекомендуется), их нужно управлять через родительский StatefulWidget.
class MyStatelessButton extends StatelessWidget {
  const MyStatelessButton({
    super.key,
    required this.label,
    required this.onPressed,
  });

  final String label;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: Text(label),
    );
  }
}

StatefulWidget

Виджет, который может менять свое состояние во времени. Его жизненный цикл сложнее и сосредоточен в отдельном объекте State.

Последовательность методов State объекта:

  1. createState() (метод StatefulWidget)
  2. mounted = true
  3. initState()
  4. didChangeDependencies()
  5. build() (многократно)
  6. didUpdateWidget() (при обновлении конфигурации)
  7. setState() (триггер из кода)
  8. deactivate()
  9. dispose()
  10. mounted = false

Ключевое отличие: StatefulWidget сам по себе — это всего лишь конфигурация (и он пересоздается при каждом обновлении родителя). Долгоживущая логика и состояние хранятся в ассоциированном объекте State, который фреймворк может переиспользовать при обновлении родительского виджета с тем же runtimeType и key.

Ответ 18+ 🔞

Давай разберём эту тему, чтобы в голове всё встало на свои места, а не болталось как хуй в проруби.

Вот смотри, в Flutter'е есть два главных типа виджетов, и вся разница между ними — как между табуреткой и трансформером. Первый — StatelessWidget. Это как та самая простая табуретка: собрали один раз из трёх досок и гвоздя, и всё. Больше она не меняется. Всё, что ей нужно для жизни — это параметры, которые ты в неё вбил при создании. final поля, конструктор — и поехали. Его жизненный цикл проще пареной репы: конструктор, метод build, и на этом всё, приехали. Собрали, отрисовали, забыли. Если ты внутри build начнёшь создавать какие-то подписки или контроллеры — это пиздец, чувак, так делать нельзя. Это всё равно что приколачивать к табуретке мотор от мопеда — вроде и прикрутил, а толку? Управлять этим нечем. Для такого дерьма нужен уже второй тип.

class MyStatelessButton extends StatelessWidget {
  const MyStatelessButton({
    super.key,
    required this.label,
    required this.onPressed,
  });

  final String label;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: Text(label),
    );
  }
}

А вот второй — StatefulWidget. Это уже наш трансформер. Он может меняться, шевелиться, обновляться. И вся его магия, вся соль — спрятана в отдельном объекте под названием State. Сам по себе StatefulWidget — это просто конфиг, бумажная инструкция. Он пересоздаётся каждый раз, когда родительский виджет обновляется. А вот объект State — это уже живая душа, которая может пережить своего создателя и хранить в себе все данные.

Жизненный цикл у этого State — целая эпопея, ёпта. Запоминай:

  1. Вызывается createState() (это ещё метод виджета).
  2. Флаг mounted становится true. Это важно, иначе потом будешь охуевать от ошибок.
  3. initState() — святое место. Тут инициализируешь всё, что должно жить долго: контроллеры, подписки, загружаешь данные. Вызывается один раз за жизнь State.
  4. didChangeDependencies() — может вызваться несколько раз. Срабатывает, когда зависящие от контекста штуки (вроде InheritedWidget) меняются.
  5. build() — а вот это вызывается дохуя раз. Здесь ты описываешь, как должен выглядеть интерфейс прямо сейчас, на основе текущего состояния.
  6. didUpdateWidget() — если родитель обновил наш виджет с новыми параметрами, но State решили оставить старый, то вызывается этот метод. Тут можно сравнить старый и новый виджет и что-то подкрутить.
  7. setState() — это не метод жизненного цикла, а твой главный инструмент. Ты вызываешь его из кода, когда нужно сказать фреймворку: «Эй, состояние поменялось, перерисуй виджет, ёпта!». Внутри него меняешь данные и триггеришь новый build.
  8. deactivate() — виджет временно убирают из дерева, но State ещё может вернуться. Редко используется.
  9. dispose() — финал, капут. Здесь ОБЯЗАТЕЛЬНО нужно почистить за собой: закрыть все контроллеры, отписаться от стримов, освободить ресурсы. После этого State умрёт.
  10. Флаг mounted становится false. Теперь если попробуешь вызвать setState() — получишь ошибку, что виджет не смонтирован. Сам от себя охуеешь, но виноват будешь только ты.

Вот и вся разница, бля. Stateless — статичный кусок интерфейса. Stateful — динамичная хуйня с мозгом (State), которая умеет реагировать, меняться и помнить. Главное — не перепутай, где что использовать, а то получится пиздопроебибна архитектура.