Перейти к основному содержимому

Состояние и поток данных

Состояние — это данные, которые меняются во время работы приложения, влияют на то, что видно на экране, и участвуют в логике. Управление состоянием — это по большей части не выбор библиотеки, а решение о том, где каждому куску данных место и кому позволено его менять.

Почему это важно. Неудачно размещённое состояние — самая частая структурная проблема приложения. Слишком локальное — и его приходится протаскивать через пять виджетов; слишком глобальное — и ни один кусок нельзя рассмотреть отдельно от остальных. И то и другое усложняет любое последующее изменение, и ни то ни другое не лечится сменой библиотеки.

Что нужно понимать

  • Где должно жить конкретное состояние — исходя из того, кто его читает и кто пишет
  • Что хранится, а что вычисляется из чего-то другого
  • Кому позволено его менять и каким способом
  • Как UI узнаёт об изменении и какая его часть перестраивается
  • Что с ним происходит при закрытии экрана или приложения

Основные темы

Виды состояния

  • Эфемерное состояние UI — раскрытый dropdown, выбранная вкладка, незаполненное поле
  • Состояние фичи — одна часть приложения, несколько виджетов
  • Состояние приложения — текущий пользователь, тема, настройки
  • Серверные данные, которые являются кешем и должны рассматриваться именно так
  • Состояние навигации и форм, у которых свои правила

Размещение

  • Подъём состояния к ближайшему общему предку
  • Почему «по умолчанию глобально» — это решение перестать рассуждать
  • Границы: что принадлежит фиче, а что может выйти за её пределы
  • Производное состояние и отказ хранить то, что можно вычислить

Механика

  • setState и вполне достойный круг задач для него
  • Provider, Riverpod, BLoC — их модели, а не синтаксис
  • Неизменяемость и предсказуемые обновления
  • Точечные перестроения вместо пробуждения половины дерева
  • Где живут побочные эффекты, чтобы build оставался чистым

Серверные данные

  • Загрузка, пустой результат, ошибка и устаревшие данные как полноценные состояния
  • Кеширование, инвалидация и обновление при возврате на экран
  • Оптимистичные обновления и согласование результата
  • Отказ от дублирования серверных данных в трёх разных хранилищах

Уровни

УровеньКак это выглядит
JuniorКорректно пользуется setState и провайдером. Выводит данные на экран, обрабатывая загрузку и ошибку.
MiddleРазмещает состояние осознанно, отделяет серверный кеш от состояния UI, контролирует область перестроения, обрабатывает все четыре состояния данных.
SeniorПроектирует поток данных для целого приложения: владение, границы, инвалидация и поведение при расхождении двух источников.

Практика

Для начала

  • Список со всеми состояниями Соберите экран с настоящими состояниями загрузки, пустого результата, ошибки и повтора — не просто со спиннером.

  • Поднимите состояние Найдите состояние, которое передаётся вниз через три уровня, и перенесите туда, где ему место.

  • Уберите хранимое производное состояние Найдите поле, которое синхронизируется вручную, и начните вычислять его.

Дальше

  • Многошаговая форма Разберитесь с зависимыми полями, асинхронной валидацией и возвратом назад без потери введённого.

  • Обновите оптимистично Покажите результат до подтверждения сервером и аккуратно откатитесь, если он откажет.

  • Сузьте перестроения Найдите изменение, из-за которого перестраивается весь экран, и добейтесь перестроения одного виджета.

Проверьте себя

  • Как вы решаете, живёт состояние в виджете или выше?
  • Какие из ваших данных на самом деле серверные, но обрабатываются как локальные?
  • По каким признакам видно, что кусок состояния разросся?
  • Где одна и та же информация хранится в двух местах?
  • Что происходит с незавершённым состоянием, когда пользователь уходит с экрана и возвращается?
  • Как вы выясните, что именно перестраивается при изменении одного значения?

Ресурсы

  • State management — официальное введение, честно называющее настоящим решением именно разделение на эфемерное состояние и состояние приложения.
  • Guide to app architecture — собственные архитектурные рекомендации Flutter, в том числе о том, где данные и репозитории располагаются относительно состояния UI.
  • Riverpod documentation — стоит прочитать ради рассуждений об областях видимости, времени жизни и освобождении ресурсов, даже если вы пользуетесь чем-то другим.
  • Bloc library documentation — самое внятное изложение модели «событие → состояние» и тестирования переходов между состояниями.
  • Making sense of all those Flutter state management options — сравнение, построенное вокруг моделей, а не API, — а различает эти решения именно модель.