Состояние и поток данных
Состояние — это данные, которые меняются во время работы приложения, влияют на то, что видно на экране, и участвуют в логике. Управление состоянием — это по большей части не выбор библиотеки, а решение о том, где каждому куску данных место и кому позволено его менять.
Почему это важно. Неудачно размещённое состояние — самая частая структурная проблема приложения. Слишком локальное — и его приходится протаскивать через пять виджетов; слишком глобальное — и ни один кусок нельзя рассмотреть отдельно от остальных. И то и другое усложняет любое последующее изменение, и ни то ни другое не лечится сменой библиотеки.
Что нужно понимать
- Где должно жить конкретное состояние — исходя из того, кто его читает и кто пишет
- Что хранится, а что вычисляется из чего-то другого
- Кому позволено его менять и каким способом
- Как 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, — а различает эти решения именно модель.