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

Компоновка и отрисовка

Как 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 — текущий движок отрисовки: зачем он появился и что изменил в подтормаживаниях из-за компиляции шейдеров.