Ответ
Для создания адаптивных интерфейсов во Flutter я использую комбинацию встроенных виджетов и определенных практик. Вот основные инструменты:
-
MediaQuery— отправная точка для получения метрик устройства.final size = MediaQuery.of(context).size; final isPortrait = size.height > size.width; final isTablet = size.shortestSide > 600; // Эмпирическое правило для планшета -
LayoutBuilder— ключевой виджет для создания layout, который зависит от доступных ограничений (constraints) родителя, а не от абсолютных размеров экрана.LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth > 1024) { return DesktopLayout(); } else if (constraints.maxWidth > 600) { return TabletLayout(); } else { return MobileLayout(); } }, ) -
FlexibleиExpanded— для создания гибких макетов внутриRowиColumn.Expandedзаставляет дочерний виджет заполнить всё доступное пространство, аFlexibleпозволяет задать коэффициент гибкости (flex). -
Wrap— автоматически переносит дочерние виджеты на следующую строку или столбец, если не хватает места. Отлично подходит для списков тегов или фильтров. -
AspectRatio— принудительно задает соотношение сторон дочернему виджету (например, для баннеров или изображений).AspectRatio( aspectRatio: 16 / 9, child: Image.network('...'), ) -
FractionallySizedBox— задает размеры в долях (процентах) от доступного пространства.FractionallySizedBox( widthFactor: 0.8, // Займет 80% ширины родителя child: ElevatedButton(...), ) -
OrientationBuilder— реагирует на изменение ориентации устройства.
Моя типичная стратегия:
Я начинаю с мобильного дизайна, а затем, используя LayoutBuilder и проверки на maxWidth, добавляю адаптацию для планшетов и десктопов. Часто я выношу логику определения типа layout (мобильный/планшет/десктоп) в отдельный провайдер или утилитный класс, чтобы не дублировать её по всему приложению. Для очень сложных адаптивных систем можно рассмотреть пакеты вроде responsive_framework или flutter_screenutil, но в большинстве случаев встроенных средств Flutter более чем достаточно.
Ответ 18+ 🔞
Давай разберём эту тему, а то у некоторых руки растут из одного места, и они потом удивляются, почему на планшете всё в кучу съехало.
Вот смотри, чтобы твой интерфейс не выглядел как пизда с ушами на разных экранах, во Flutter есть куча годных инструментов. Не надо ничего выдумывать, всё уже придумано.
MediaQuery — это твой базовый лом. Им первым делом снимаешь размеры устройства. Типа, вот так:
final size = MediaQuery.of(context).size;
final isPortrait = size.height > size.width;
final isTablet = size.shortestSide > 600; // Это просто эмпирика, но работает
Но тут есть подвох: MediaQuery даёт размер всего экрана, а не той конкретной области, где твой виджет живёт. Поэтому для лейаута есть оружие поточнее.
LayoutBuilder — вот это, блядь, король адаптивности. Он даёт тебе не размер экрана, а те ограничения (constraints), которые на него наложил родительский виджет. Это ключевая разница, ёпта.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 1024) {
return DesktopLayout();
} else if (constraints.maxWidth > 600) {
return TabletLayout();
} else {
return MobileLayout();
}
},
)
Вот это — основа основ. С него и начинай думать.
Дальше идут виджеты-помощники. Flexible и Expanded — это чтобы в Row и Column элементы растягивались и сжимались как надо, а не торчали как хуй с горы. Expanded жадный — займёт всё свободное место. Flexible — более воспитанный, ему можно flex задать.
Wrap — спаситель, когда у тебя список каких-нибудь кнопочек или чипов, а они не влезают в строку. Он их просто переносит на следующую. Ебушки-воробушки, как же без него жить.
AspectRatio — когда нужно жёстко зафиксировать пропорции. Например, для картинки баннера 16:9, чтобы её не размазало.
AspectRatio(
aspectRatio: 16 / 9,
child: Image.network('...'),
)
FractionallySizedBox — если хочешь сказать «займи 70% от доступной ширины». Удобно для центрированных контейнеров.
FractionallySizedBox(
widthFactor: 0.8,
child: ElevatedButton(...),
)
OrientationBuilder — на случай, если тебе реально важно, вертикально телефон держат или горизонтально. Но честно? Чаще хватает проверки ширины через LayoutBuilder.
Как я обычно делаю, чтобы не было волнения ебать:
Начинаю всегда с мобильной версии — она самая узкая, всё по вертикали. Потом, внутри LayoutBuilder, начинаю добавлять ветвления: если ширина больше 600 — планшетный лейаут, если больше 1024 — десктопный. Часто эту логику определения типа экрана выношу в какой-нибудь провайдер или утилитный класс, чтобы не писать одно и то же в каждом втором виджете.
А то некоторые сразу хватаются за пакеты типа responsive_framework. Это, конечно, мощно, но для 90% задач овердохуища не нужно. Встроенных средств Flutter хватает за глаза, если, конечно, руки не из жопы. Главное — понять разницу между размером экрана (MediaQuery) и доступным пространством (LayoutBuilder). Понял это — и половина проблем отпадает.