Padding vs SizedBox. Что выбрать для вёрстки отступов Column и Row
Здравы будьте! С вами на связи руководитель Flutter-направления Mad Brains Николай Омётов. В этой статье я проведу разбор особенностей вёрстки отступов с помощью Padding и SizedBox и расскажу, что выбрала наша команда для создания единого стиля кода.
Вообще, во Flutter есть несколько способов создать свободное пространство между элементами. Самыми распространёнными являются Padding и SizedBox. Технически использовать можно оба, а иногда их даже комбинируют. Но мы приверженцы красивого кода, хотим сделать его понятнее, чище для чтения и рефакторинга. Мы провели мини-исследование и выяснили, какой виджет более универсален для этой цели.
Пример ситуации
Было
Должно стать
SizedBox решение:
Row(
children: [
Expanded(
child: ColoredBox(
color: Colors.red,
child: Center(
child: Text('1' * 1000),
),
),
),
// Пустота
const SizedBox(width: 16),
Expanded(
child: ColoredBox(
color: Colors.green,
child: Center(
child: Text('2' * 1000),
),
),
),
// Пустота
const SizedBox(width: 16),
const ColoredBox(
color: Colors.yellow,
child: Icon(Icons.check),
),
],
),
Padding решение:
Row(
children: [
Expanded(
// Пустота
child: Padding(
padding: const EdgeInsets.only(right: 8),
child: ColoredBox(
color: Colors.red,
child: Center(
child: Text('1' * 1000),
),
),
),
),
Expanded(
// Пустота
child: Padding(
padding: const EdgeInsets.only(left: 8),
child: ColoredBox(
color: Colors.green,
child: Center(
child: Text('2' * 1000),
),
),
),
),
// Пустота
const Padding(
padding: EdgeInsets.only(left: 16),
child: ColoredBox(
color: Colors.yellow,
child: Icon(Icons.check),
),
),
],
),
Сравниваем Padding и SizedBox
Название
Мы чаще читаем код чем пишем его. Поэтому название должно быть однозначным и конкретным.
Padding говорит, что добавляет «заполнитель» (что и нужно в нашем случае). Поэтому название говорит само за себя.
SizedBox говорит, что добавляет коробочку какого-то размера. И эта коробочка как раз таки, может использоваться как «заполнитель», но это неочевидно из названия. У SizedBox может быть несколько назначений в коде (задать конкретный размеры ребёнку, заполнить пустое пространство на максимум, заглушка для пустого виджета (привет, shrink) и т.д.), когда быстрым взглядом читаешь это название, то не всегда можешь однозначно сказать, что виджет применяется тут для создания отступа.
Вывод: название у Padding лучше отражает цель вёрстки.
Читаемость
Padding позволяет видеть дерево виджетов, но зачастую не позволяет оценить общее направление пустот без запуска кода и DevTools. Если он опоясывает один из похожих виджетов, то в дереве смотрится неказисто из-за того, что один из них выдвинут на уровень дальше. Но в тоже время Padding обособляет элементы и их можно рассматривать более независимо. А SizedBox похож на хвост, наличие которого приходится всегда проверять. Особенно при перемещении виджета.
SizedBox помогает оценить последовательность элементов в списке и увидеть без запуска кода, как будет отображена колонка или строка. Но он растягивает список: то есть не выстраивается в дерево. Также есть ограничения для Column => SizedBox (width) и Row => SizedBox (height).
Вывод: Padding больше вписывается в стиль Flutter, так как поддерживает вид «дерева» виджетов.
Добавление
Существует кнопка «Wrap with Padding» или подобная, встроенная в IDE: создаётся фиксированный паддинг, который нужно переделывать под ситуацию.
Если для SizedBox сделать снипеты «sw» и «sh», то можно будет быстро его создать:
const SizedBox(width: Курсор,);
Вывод: большой разницы нет, всё создаётся быстро.
Удаление
В SizedBox — 1 или 3 строки в зависимости от форматирования.
[
const SizedBox(width: 1),
const SizedBox(
width: 1,
),
],
Через Padding нужно удалять строки сверху и строку снизу (закрывающая скобка), а она может быть далеко. Если удалять не вручную, то есть кнопка «Remove widget» или подобная. Так что больших различий в удалении по сравнению с SizedBox нет.
Количество строк кода
Padding — от 3-х до 8-ми и более строк, SizedBox — от 1-ой до 3-х в зависимости от форматирования.
Количество в списке
Padding: от одного (когда меняется уровень в дереве одного из элементов) до всех, как в начальном примере, когда мы должны сохранить отношение в Expanded.
SizedBox: чаще всего от n-1, где n — количество элементов, которые нужно разделить.
Ситуации бывают разные, поэтому сложно дать оценку частоте.
Отображение в DevTools
Padding
SizedBox
При использовании Padding отступ четко виден, это удобнее.
Константность
Если Padding и его «ребёнок» (child) объявлены как const, они не будут пересоздаваться при каждом вызове build (). EdgeInsets (класс, который используется для задания отступов) зачастую const.
В SizedBox почти в 100% наших случаев — const. Значит, вычислен заранее. Но так как могут в приложении быть разными (width: 4, 6, 8, 12, 16, 20 …), то не всегда происходит переиспользование.
Вывод: SizedBox канонизирован почти всегда, а Padding редко.
Гибкость
У EdgeInsets в Padding есть несколько конструкторов для задания отступов:
— all (double value): одинаковый отступ со всех сторон;
— symmetric ({double vertical, double horizontal}): задает отступы вертикально и горизонтально;
— only ({double left, double top, double right, double bottom}): позволяет задать отступы для каждой стороны отдельно;
— fromLTRB (double left, double top, double right, double bottom): аналогично only, но с позиционными аргументами.
В SizedBox только два основных параметра для настройки — width и height, за исключением child.
Вывод: Padding даёт больше возможностей.
Включение виджета в список (возможность добавить / убрать по условию)
Padding (просто):
[
if(isEnabled)
Padding(child: child),
]
SizedBox (костыльно):
[
if (isEnabled)
...[
Widget(),
SizedBox(),
]
]
Не используйте в этом случае SizedBox или замените уже существующий на Padding.
Работа с динамическими списками
Здесь мы не берём в расчёт большие списки данных, которые нужно скроллить или пагинировать. Рассматриваются списки небольшого количества одинаковых элементов: 5 звёзд, точки для для ввода пин-кода и прочее. В данном случае используются генераторы списков.
В случае с SizedBox необходимо чередовать виджеты и разделитель, при этом учитывается количество элементов 2n-1, где n — количество виджетов, которые отделяли.
Для Padding будет достаточно обернуть элемент, и EdgeInsets указать общие размеры. Для крайних элементов нужно будет учесть случаи кастомного паддинга, что тоже является нюансом про который стоит помнить.
Производительность
Существенных различий выявлено не было. Можно заметить только нюанс при работе с массивами: количество элементов детей меньше при использовании Padding, чем SizedBox. Это влияет на работу метода build, когда происходит процесс создания элементов и сравнения их при перестроении дерева. Если смотреть со стороны асимптотической сложности этих действий, то оно одинаковое и будет O (n), где n — количество элементов в списке детей.
Итог: Padding лучше! Почему?
По назначению и функциональности он идеально подходит для нашей задачи писать код красиво. По сути Padding делает пустоту частью элемента, а SizedBox отделяет пустоту от элемента, делает их несвязанными.
SizedBox выглядит больше как удобная хитрость, которая иногда показывает свои минусы (которые есть и у Padding). Но если стандартизировать кодстайл, то мы за Padding. А вы?