Код-ревью Middle Жизненный цикл обновлено

Код-ревью: утечки контроллеров и подписок

Вопрос

Экран поиска. Найдите проблемы жизненного цикла.

class _SearchState extends State<Search> {
  final _controller = TextEditingController();
  late AnimationController _anim;
  StreamSubscription? _sub;

  @override
  void initState() {
    _anim = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
    _sub = widget.repo.results.listen((r) => setState(() => _results = r));
    final locale = Localizations.localeOf(context);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final focus = FocusNode();
    return TextField(controller: _controller, focusNode: focus);
  }

  @override
  void dispose() {
    super.dispose();
    _anim.dispose();
  }
}

Коротко

Правила: super.initState() первой строкой, super.dispose() последней. Всё, что создано в State (контроллеры, FocusNode, подписки), создаётся в initState и освобождается в dispose.

Что должен найти ревьюер

  1. Стильстрока 11
    super.initState() должен быть первой строкой initState.
  2. Ошибкастрока 10
    Localizations.localeOf(context) в initState: подписка на InheritedWidget здесь запрещена, перенести в didChangeDependencies.
  3. Ошибкастрока 9
    В колбэке подписки нет проверки mounted, а подписка не отменяется в dispose: утечка и setState после dispose.
  4. Ошибкастрока 16
    FocusNode создаётся в build на каждом перестроении и никогда не освобождается: утечка и потеря фокуса. Создать в initState, освободить в dispose.
  5. Ошибкастрока 23
    _controller.dispose() не вызван, _sub?.cancel() нет.
  6. Стильстрока 22
    super.dispose() должен быть последней строкой: сначала освобождаем свои ресурсы.
  7. Ошибкастрока 1
    Для vsync: this нужен миксин SingleTickerProviderStateMixin, иначе код не скомпилируется.

Разбор

Правила: super.initState() первой строкой, super.dispose() последней. Всё, что создано в State (контроллеры, FocusNode, подписки), создаётся в initState и освобождается в dispose. Ничего долгоживущего не создаётся в build. InheritedWidget читается в didChangeDependencies или build.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.