Ответ
В Flutter адаптивная и отзывчивая вёрстка — это подход к проектированию пользовательского интерфейса, при котором приложение корректно отображается и удобно используется на устройствах с разными размерами экранов (от маленького телефона до большого планшета или десктопа) и ориентациями.
Основные инструменты и техники:
-
MediaQuery— для получения характеристик экрана.final mediaData = MediaQuery.of(context); final screenWidth = mediaData.size.width; final screenHeight = mediaData.size.height; final pixelRatio = mediaData.devicePixelRatio; final isPortrait = screenHeight > screenWidth; -
LayoutBuilder— для реагирования на размеры родительского виджета. Более предпочтителен, чемMediaQuery, когда виджет должен адаптироваться к доступному пространству, а не ко всему экрану.LayoutBuilder( builder: (context, constraints) { // constraints.maxWidth — максимальная доступная ширина if (constraints.maxWidth > 600) { return _buildWideLayout(); // Макет для планшета/десктопа } else { return _buildNarrowLayout(); // Макет для телефона } }, ) -
Использование гибких виджетов (
Flexible,Expanded) внутриRowиColumn.Row( children: [ Expanded( // Занимает всё оставшееся пространство flex: 2, child: Container(color: Colors.red), ), Expanded( flex: 1, child: Container(color: Colors.blue), ), ], ) -
OrientationBuilder— для реакции на изменение ориентации.OrientationBuilder( builder: (context, orientation) { return orientation == Orientation.portrait ? _buildPortraitScaffold() : _buildLandscapeScaffold(); }, ) -
Определение брейкпоинтов (breakpoints) для разных категорий устройств.
class Breakpoints { static const double mobile = 600; // До 600px — телефон static const double tablet = 900; // 600-900px — планшет // >900px — десктоп } // Использование if (screenWidth < Breakpoints.mobile) { // Мобильный UI } else if (screenWidth < Breakpoints.tablet) { // Планшетный UI } else { // Десктопный UI }
Практический пример: адаптивный список деталей (master-detail):
class AdaptiveDetailPage extends StatelessWidget {
final Widget masterView;
final Widget detailView;
const AdaptiveDetailPage({
super.key,
required this.masterView,
required this.detailView,
});
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
// На широком экране показываем master и detail рядом
if (screenWidth > 600) {
return Row(
children: [
SizedBox(width: 300, child: masterView),
Expanded(child: detailView),
],
);
}
// На узком экране используем навигацию между экранами
else {
return masterView; // При тапе на элемент открываем detailView на новом экране
}
}
}
Ключевые принципы:
- Относительные единицы: Использовать
FractionallySizedBox, проценты,flexвместо фиксированных пикселей. - Тестирование: Обязательно тестировать UI на эмуляторах/устройствах с разными диагоналями и плотностями пикселей.
- Содержание прежде всего: Макет должен адаптироваться, чтобы контент оставался читаемым и доступным.
Видео-ответы
▶
▶
▶
▶
▶