Адаптивный дизайн очень важен для тех, кто ориентируется на разные платформы, используя единую кодовую базу. И особенно это относится к разработке на Flutter, ведь он нацелен на все основные платформы.
Как сделать адаптивный дизайн?
Существует множество способов сделать адаптивный дизайн на Flutter. Самый простой из них — получить информацию с текущего экрана с помощью виджета MediaQuery
:
Size screenSize = MediaQuery.of(context).size;
Orientation orientation = MediaQuery.of(context).orientation;
Затем уже по-своему, основываясь на этой информации, создать собственный виджет. Или же использовать пакет, упрощающий этот процесс. Вместе с пакетом обычно предоставляется такой интерфейс:
ResponsiveBuilder(
builder: (context, sizingInformation) {
// Здесь проверяется информация о размерах и возвращается пользовательский интерфейс
if (sizingInformation.deviceScreenType == DeviceScreenType.desktop) {
return Container(color:Colors.blue);
}
if (sizingInformation.deviceScreenType == DeviceScreenType.tablet) {
return Container(color:Colors.red);
}
if (sizingInformation.deviceScreenType == DeviceScreenType.watch) {
return Container(color:Colors.yellow);
}
return Container(color:Colors.purple);
},
},
);
}
Или же предоставляются некоторые заранее заданные типы экранов:
ScreenTypeLayout.builder(
mobile: (BuildContext context) => Container(color:Colors.blue),
tablet: (BuildContext context) => Container(color:Colors.yellow),
desktop: (BuildContext context) => Container(color:Colors.red),
watch: (BuildContext context) => Container(color:Colors.purple),
);
(Приведенные выше примеры кода взяты из пакета responsive_builder).
Другой интерфейс, по примеру MaterialStateProperty
При написании адаптивного кода нужно добиться хорошей гибкости. Иногда гибким надо сделать лишь одно значение (например, целое число crossAxisCount
в GridView
). А иногда нужны совершенно разные виды дизайна пользовательского интерфейса для разных экранов (например, TabBarView
для мобильных устройств и Row
для настольных компьютеров). Поэтому нужно сделать адаптивным либо целое число,либо виджет, не записывая снова и снова операторы if
/switch
. Кроме того, люди расходятся во мнениях относительно того, где устанавливать контрольные точки. Поэтому нужно предоставить им самим свободно определять собственные контрольные точки.
Встроенные компоненты Material (например, как в случае с кнопкой ElevatedButton
) изменяют свой внешний вид в зависимости от того, нажата кнопка или выбрана, или на нее наведен курсор. Эти компоненты не используют какой-то один экземпляр цвета для цвета фона. За них различные значения цвета, основываясь на внутреннем состоянии компонента, генерирует MaterialStateProperty
.
property?.resolve(_states)
А что если аналогичный интерфейс задействовать для целей адаптивности?
Состоянием теперь будет некая целевая конфигурация экрана, называющаяся в нашем случае ScreenScope
:
class ScreenScope {
final double minWidth;
final double maxWidth;
final double minHeight;
final double maxHeight;
final Orientation? orientation;
...
}
У нас уже имеются заранее заданные параметры этой конфигурации:
mobileScreenScope (0px - 480px width)
tabletScreenScope (480px - 840px width)
desktopScreenScope (840px - width)
mobilePortraitScreenScope (0px - 480px width, portrait)
tabletPortraitScreenScope (480px - 840px width, portrait)
desktopPortraitScreenScope (840px - width, portrait)
mobileLandscapeScreenScope (0px - 840px width, landscape)
tabletLandscapeScreenScope (840px - 1200px width, landscape)
desktopLandscapeScreenScope (1200px - width, landscape)
Когда используется только одна контрольная точка:
smallScreenScope (0px - 600px width)
bigScreenScope (600px - width)
smallPortraitScreenScope (0px - 600px width, portrait)
bigPortraitScreenScope (600px - width, portrait)
smallPortraitScreenScope (0px - 1000px width, landscape)
bigPortraitScreenScope (1000px - width, landscape)
То объявляется адаптивный экземпляр, а фактическое значение получается вызовом в нем метода resolve
:
GridView.count(
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: Responsive({
mobileScreenScope: 2,
tabletScreenScope: 4,
desktopScreenScope: 6
}).resolve(context)!,
children: List.generate(
30,
(index) =>
Container(color: Colors.green, child: Text("SOME TEXT"))),
);
А вот что получается, когда нужно использовать разные виджеты для разных экранов:
Widget widget = Responsive({
mobileScreenScope: mobileWidget,
tabletScreenScope: tabletWidget,
desktopScreenScope: desktopWidget,
}).resolve(context);
Чтобы сэкономить вам время, мы сделали вспомогательный виджет ScreenBuilder
:
ScreenBuilder(
mobile: mobile,
tablet: tablet,
desktop: desktop,
);
ScreenBuilder.builder(
mobileBuilder: mobileBuilder,
tabletBuilder: tabletBuilder,
desktopBuilder: desktopBuilder,
)
В его синтаксисе используется WidgetBuilder
, чтобы иметь доступ к BuildContext
.
Заключение
Вот и все о пакете responsive_property. Цель проста: обеспечить гибкость адаптивного дизайна, избавляя от необходимости написания операторов if
.
Читайте также:
- Кастомизируем дефолтную заставку во Flutter
- GitHub Codespaces: быстрая разработка на ходу с Flutter
- Прикольные анимации для Flutter-приложений
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Wenkai Fan: Responsive design at different levels in Flutter