Что такое MediaQuery в Flutter и для чего она используется?

Ответ

MediaQuery — это виджет, предоставляющий доступ к метаданным и характеристикам текущего устройства (размер экрана, плотность пикселей, ориентация, системные отступы). Это основной инструмент для создания адаптивного интерфейса в Flutter.

Данные извлекаются через MediaQuery.of(context).

Ключевые свойства:

  • MediaQueryData.size (Size) — доступная ширина и высота экрана.
  • MediaQueryData.orientation (Orientation) — портретная или альбомная ориентация.
  • MediaQueryData.padding (EdgeInsets) — системные отступы (например, для статус-бара, «челки» или панели навигации).
  • MediaQueryData.textScaleFactor (double) — коэффициент масштабирования текста, заданный пользователем в настройках системы.
  • MediaQueryData.devicePixelRatio (double) — плотность пикселей устройства.

Практический пример адаптивного дизайна:

Widget build(BuildContext context) {
  // Получаем данные о медиа
  final mediaData = MediaQuery.of(context);
  final screenWidth = mediaData.size.width;
  final isLandscape = mediaData.orientation == Orientation.landscape;

  // Логика в зависимости от размера экрана (брейкпоинты)
  final isWideScreen = screenWidth > 600;

  return Scaffold(
    body: Container(
      // Адаптивная ширина
      width: isWideScreen ? 400 : double.infinity,
      padding: EdgeInsets.all(isWideScreen ? 24.0 : 16.0),
      child: Column(
        children: [
          // Текст с учетом пользовательских настроек масштаба
          Text(
            'Адаптивный заголовок',
            style: TextStyle(fontSize: 20 * mediaData.textScaleFactor),
          ),
          // Меняем layout в зависимости от ориентации
          if (isLandscape)
            Row(children: [Expanded(child: Chart()), Expanded(child: Details())])
          else
            Column(children: [Chart(), Details()]),
        ],
      ),
    ),
  );
}

Важно: MediaQuery.of(context) зависит от BuildContext. Ее вызов в build() безопасен. Для использования в initState() данные нужно получать через MediaQueryData.fromWindow(WidgetsBinding.instance.window), но это менее предпочтительный способ.

Ответ 18+ 🔞

Ну что за тема, ёпта! Слушай, сейчас объясню про эту MediaQuery, чтобы ты не ходил как пизда с ушами по коду. Это, бля, такой виджет, который тебе всё про устройство расскажет — какой экран, как он повёрнут, где системные отступы и прочую хуйню. Без него адаптивный интерфейс собрать — это как хуй с горы катать, нихуя не получится.

Данные ты вытаскиваешь через MediaQuery.of(context). Запомни это, как «Отче наш».

Что там можно узнать, бля:

  • MediaQueryData.size (Size) — ну это ширина и высота экрана, доступные тебе. Основа основ.
  • MediaQueryData.orientation (Orientation) — телефон вертикально держат или горизонтально. Портрет или альбом, короче.
  • MediaQueryData.padding (EdgeInsets) — вот это важно, бля! Это отступы, которые система сама делает — сверху статус-бар, снизу панель навигации, или эта чёртова «чёлка» на айфонах. Чтобы твоя кнопка не уехала под эту хуйню.
  • MediaQueryData.textScaleFactor (double) — а вот это, сука, интересно! Пользователь в настройках телефона мог увеличить шрифт системы. И если ты проигнорируешь этот коэффициент, твой красивый текст налезет на другой текст, и будет пиздец, а не интерфейс. Доверия ебать ноль к пользователю, он всегда сделает не так.
  • MediaQueryData.devicePixelRatio (double) — плотность пикселей. Нужно, если ты с картинками высокого разрешения работаешь.

Смотри, как на практике выглядит, чувак:

Widget build(BuildContext context) {
  // Вот тут вытаскиваем все данные разом
  final mediaData = MediaQuery.of(context);
  final screenWidth = mediaData.size.width;
  final isLandscape = mediaData.orientation == Orientation.landscape;

  // Определяем, широкий у нас экран или нет. Условный брейкпоинт.
  final isWideScreen = screenWidth > 600;

  return Scaffold(
    body: Container(
      // Ширину делаем адаптивной: на широком экране фиксированную, на узком — на всю ширину.
      width: isWideScreen ? 400 : double.infinity,
      // Отступы тоже разные.
      padding: EdgeInsets.all(isWideScreen ? 24.0 : 16.0),
      child: Column(
        children: [
          // Текст умножаем на пользовательский масштаб! Это обязательно, иначе пользователь с плохим зрением тебя пошлёт нахуй.
          Text(
            'Адаптивный заголовок',
            style: TextStyle(fontSize: 20 * mediaData.textScaleFactor),
          ),
          // А тут меняем всю компоновку в зависимости от ориентации!
          if (isLandscape)
            Row(children: [Expanded(child: Chart()), Expanded(child: Details())])
          else
            Column(children: [Chart(), Details()]),
        ],
      ),
    ),
  );
}

Важный момент, бля, на котором все спотыкаются: MediaQuery.of(context) жёстко завязана на твой текущий BuildContext. В методе build() её вызывать — святое дело, всё работает. Но если ты попробуешь впихнуть её в initState(), то получишь удивление пиздец, потому что контекст ещё не готов. Для таких извращений есть костыль: MediaQueryData.fromWindow(WidgetsBinding.instance.window). Но это, бля, как ебать копать — можно, но неестественно и сложно. Старайся без этого обходиться.