Расскажи про жизненный цикл StatefulWidget

«Расскажи про жизненный цикл StatefulWidget» — вопрос из категории Жизненный цикл, который задают на 36% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Жизненный цикл StatefulWidget управляется его ассоциированным объектом State. Основные этапы:

  1. createState(): Вызывается фреймворком при вставке виджета в дерево. Создает экземпляр класса State.
  2. initState(): Вызывается один раз сразу после createState(). Идеальное место для инициализации данных, подписки на Stream'ы или создания контроллеров (например, AnimationController, ScrollController).
    @override
    void initState() {
      super.initState();
      _animationController = AnimationController(
        vsync: this,
        duration: const Duration(seconds: 1),
      );
      _dataStreamSubscription = myService.dataStream.listen(_updateData);
    }
  3. didChangeDependencies(): Вызывается после initState() и при изменении зависимостей виджета (например, когда обновился InheritedWidget, на который он подписан через context.dependOnInheritedWidgetOfExactType).
  4. build(): Вызывается многократно для построения UI в ответ на setState() или изменения внешних данных. Должен быть чистой функцией от состояния (State) и свойств (widget.someProp).
  5. didUpdateWidget(oldWidget): Вызывается, когда родительский виджет перестраивается и создает новый экземпляр StatefulWidget с тем же runtimeType, но этот State объекта переиспользуется. Позволяет сравнивать старые и новые свойства (oldWidget vs widget).
    @override
    void didUpdateWidget(MyWidget oldWidget) {
      super.didUpdateWidget(oldWidget);
      if (widget.itemId != oldWidget.itemId) {
        _loadNewItem(widget.itemId);
      }
    }
  6. setState(): Метод для уведомления фреймворка об изменении внутреннего состояния. Вызывает перестроение виджета через build().
  7. deactivate(): Вызывается, когда State удаляется из дерева, но может быть повторно вставлен в другое место дерева (например, внутри GlobalKey).
  8. dispose(): Вызывается, когда State объект удаляется навсегда. Критически важно освобождать ресурсы: отменять таймеры, закрывать контроллеры, отписываться от Stream'ов.
    @override
    void dispose() {
      _animationController.dispose(); // Обязательно!
      _dataStreamSubscription.cancel();
      super.dispose();
    }

Важно: После вызова dispose(), свойство mounted становится false. Любой последующий вызов setState() выбросит исключение.