Row, Column и Flex-вёрстка
Row выстраивает виджеты в строку, Column — в столбец; выравнивание задаётся по двум осям.
Суть:
RowиColumnраскладывают детей вдоль главной оси, аmainAxisAlignmentиcrossAxisAlignmentуправляют выравниванием по главной и поперечной осям.
За кулисами Row и Column устроены на основе модели Flex — той же идеи, что и flexbox в вебе, но выраженной виджетами. Дети могут быть жёсткими (занимают столько, сколько просят) или гибкими (делят свободное место). Этим гибким поведением управляют Expanded и Flexible, которые мы затронем подробнее, но уже сейчас полезно держать в голове: пространство ряда распределяется в два этапа — сначала жёсткие дети, затем остаток между гибкими.
Любой экран — это комбинация горизонтальных и вертикальных рядов. Шапка с аватаркой слева и именем справа — это Row. Лента карточек сверху вниз — это Column. Оба берут список детей в свойстве children и расставляют их вдоль своей оси.
Column(
mainAxisAlignment: MainAxisAlignment.center, // по вертикали
crossAxisAlignment: CrossAxisAlignment.start, // по горизонтали
children: const [
Text('Заголовок'),
Text('Подзаголовок'),
Icon(Icons.star),
],
)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Icon(Icons.menu),
Text('Название'),
Icon(Icons.search),
],
)
Ключ к пониманию — две оси. У Row главная ось горизонтальна, у Column — вертикальна. mainAxisAlignment двигает детей вдоль главной оси, crossAxisAlignment — поперёк.
Как работают оси под капотом
Запомнить легко: main = по направлению виджета, cross = поперёк. Значения MainAxisAlignment — это способы распределить свободное место: start, center, end, spaceBetween (промежутки между детьми), spaceAround, spaceEvenly. Flutter сначала измеряет детей, потом распределяет остаток пространства согласно выбранному выравниванию.
Column (главная ось — вертикаль |) +-------------------+ | [Заголовок] | crossAxisAlignment.start | [Подзаголовок] | (прижато влево) | [звезда] | +-------------------+ main двигает вверх/вниз/центр Row (главная ось — горизонталь -->) +-----------------------------------+ |[меню] [Название] [поиск] | spaceBetween +-----------------------------------+ main --> распределяет по ширине; cross — по высоте
# Модель распределения по главной оси (spaceBetween и center)
def layout(children, alignment, total=30):
used = sum(len(c) for c in children)
free = max(0, total - used)
if alignment == 'center':
pad = free // 2
return ' ' * pad + ' '.join(children)
if alignment == 'spaceBetween' and len(children) > 1:
gap = free // (len(children) - 1)
return (' ' * gap).join(children)
return ' '.join(children)
print('|' + layout(['A', 'B', 'C'], 'center') + '|')
print('|' + layout(['A', 'B', 'C'], 'spaceBetween') + '|')
Частые ошибки
- Переполнение (overflow). Если детей в
Rowбольше, чем ширины, появляются жёлто-чёрные полосы. ЛечитсяExpandedили прокруткой. - Путать оси. В
ColumnmainAxisAlignment— это вертикаль, а не горизонталь. - Ждать центрирования без указания размера.
Columnпо умолчанию занимает всю высоту,Row— всю ширину; это влияет на выравнивание.
Best practices
- Запомните мнемонику: main = вдоль виджета, cross = поперёк.
- Для гибкого распределения ширины/высоты используйте
ExpandedиFlexible(следующий урок). - Если содержимое может не влезть — оборачивайте в прокручиваемый виджет, не надейтесь, что поместится.
Частый практический приём — комбинировать Row и Column во вложенные структуры. Карточка товара — это Column из картинки сверху и Row снизу с названием слева и ценой справа. Почти любой реальный макет раскладывается на такие вложенные ряды и столбцы. Тренируйте взгляд: глядя на дизайн, мысленно режьте его на горизонтальные и вертикальные полосы — это и будет ваша структура из Row и Column.
Итог: Row и Column — это скелет любой вёрстки Flutter. Освойте две оси и выравнивания — и вы сможете расположить что угодно. Дальше добавим гибкость через Expanded и контейнеры.