Какие виджеты Flutter позволяют создавать адаптивный интерфейс под разные размеры экранов?

Ответ

Для создания адаптивных интерфейсов во Flutter я использую комбинацию встроенных виджетов и определенных практик. Вот основные инструменты:

  1. MediaQuery — отправная точка для получения метрик устройства.

    final size = MediaQuery.of(context).size;
    final isPortrait = size.height > size.width;
    final isTablet = size.shortestSide > 600; // Эмпирическое правило для планшета
  2. LayoutBuilder — ключевой виджет для создания layout, который зависит от доступных ограничений (constraints) родителя, а не от абсолютных размеров экрана.

    LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth > 1024) {
          return DesktopLayout();
        } else if (constraints.maxWidth > 600) {
          return TabletLayout();
        } else {
          return MobileLayout();
        }
      },
    )
  3. Flexible и Expanded — для создания гибких макетов внутри Row и Column. Expanded заставляет дочерний виджет заполнить всё доступное пространство, а Flexible позволяет задать коэффициент гибкости (flex).

  4. Wrap — автоматически переносит дочерние виджеты на следующую строку или столбец, если не хватает места. Отлично подходит для списков тегов или фильтров.

  5. AspectRatio — принудительно задает соотношение сторон дочернему виджету (например, для баннеров или изображений).

    AspectRatio(
      aspectRatio: 16 / 9,
      child: Image.network('...'),
    )
  6. FractionallySizedBox — задает размеры в долях (процентах) от доступного пространства.

    FractionallySizedBox(
      widthFactor: 0.8, // Займет 80% ширины родителя
      child: ElevatedButton(...),
    )
  7. 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). Понял это — и половина проблем отпадает.