Компоновка и отрисовка
Как Flutter решает, где что окажется: ограничения спускаются вниз по дереву, размеры возвращаются наверх, а положение ребёнка задаёт родитель. Одна фраза — и почти любая ошибка вёрстки оказывается её следствием.
Почему это важно. Ошибки компоновки во Flutter славятся тем, что кричат громко и не помогают ничем, пока модель вам незнакома. С моделью «unbounded height» перестаёт быть загадкой и становится конкретным утверждением о конкретном виджете.
Что нужно понимать
- Какие ограничения получает этот виджет и от кого
- Хочет ли родитель, чтобы размер выбрал ребёнок, или навязывает его сам
- Откуда взялось неограниченное измерение
- Что рисуется из того, чего всё равно никто не увидит
- Почему та же вёрстка ведёт себя иначе внутри прокручиваемой области
Основные темы
Модель
- Ограничения вниз, размеры вверх, положение задаёт родитель
- Жёсткие (tight), свободные (loose) и неограниченные (unbounded) ограничения
BoxConstraintsи чтение их в инспекторе- Intrinsic-размеры и то, во что они обходятся
Повседневная вёрстка
- Row, Column и смысл
mainAxisSizeиAlignment ExpandedиFlexible— в чём разница- Stack и позиционирование
LayoutBuilder— как взять ограничения в свои рукиIntrinsicHeightи его родственники: запасной выход и его цена
Прокрутка
- Viewport и sliver
- Почему
ColumnвнутриSingleChildScrollViewведёт себя иначе - Ленивые списки: строится только то, что видно
- Вложенная прокрутка и как удержать её в разумных рамках
Отрисовка
- Порядок отрисовки и то, как
Stackскладывает слои - Обрезка, прозрачность и тени как реальная плата за кадр
- Границы перерисовки
- Impeller и что изменилось в том, как рисуются кадры
Уровни
| Уровень | Как это выглядит |
|---|---|
| Junior | Собирает вёрстку из Row, Column и Padding. Ошибки overflow чинит перебором вариантов. |
| Middle | Читает ошибку об ограничениях и понимает, какой виджет её вызвал. Осознанно применяет Expanded, LayoutBuilder и sliver-виджеты. |
| Senior | Проектирует вёрстку, которая выдерживает разные размеры и разное содержимое, при необходимости пишет собственную компоновку и рассуждает о стоимости отрисовки. |
Практика
Для начала
-
Прочитать ошибку по-настоящему Специально вызвать ошибку unbounded height, а затем объяснить, какой виджет выдал какое ограничение.
-
Посмотреть на ограничения Через инспектор прочитать реальные
BoxConstraintsв трёх точках дерева. -
Expanded против Flexible Собрать Row, в котором замена одного на другой заметно меняет результат, и объяснить почему.
Глубже
-
Перевести на sliver-виджеты Превратить прокручиваемый экран из стопки секций в
CustomScrollView, сохранив ленивость. -
Компоновка от ограничений С помощью
LayoutBuilderменять на пороговой ширине саму компоновку, а не только размеры. -
Снизить стоимость отрисовки Найти экран с лишними слоями обрезки или прозрачности, убрать их и измерить результат до и после.
Проверьте себя
- Что означает «неограниченное ограничение», если сказать одним предложением?
- Когда нужен именно
Expanded, а неFlexible? - Почему
ListViewвнутриColumnможет упасть и какие есть три способа это починить? - Чего стоит
IntrinsicHeightи когда эта цена оправдана? - Какие части интерфейса перерисовываются, когда меняется одна небольшая деталь?
- Где помог бы
RepaintBoundaryи чем вы это докажете?
Ресурсы
- Understanding constraints — страница, которую стоит читать, пока она не уляжется в голове. Всё остальное про компоновку следует из неё.
- Layouts in Flutter — практическое руководство по виджетам компоновки и по тому, когда какой уместен.
- Slivers explained — от команды Flutter и до сих пор самое ясное введение в viewport и ленивую прокрутку.
- Decoding Flutter: Unbounded height and width — официальное короткое видео о самой частой ошибке вёрстки.
- Impeller — текущий движок отрисовки: зачем он появился и что изменил в подтормаживаниях из-за компиляции шейдеров.