Найти тему
Nuances of programming

Адаптивный дизайн на разных уровнях Flutter

Оглавление

Источник: Nuances of Programming

Адаптивный дизайн очень важен для тех, кто ориентируется на разные платформы, используя единую кодовую базу. И особенно это относится к разработке на 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.

Адаптивный WidgetBuilder
Адаптивный WidgetBuilder

Заключение

Вот и все о пакете responsive_property. Цель проста: обеспечить гибкость адаптивного дизайна, избавляя от необходимости написания операторов if.

Читайте также:

Читайте нас в Telegram, VK

Перевод статьи Wenkai Fan: Responsive design at different levels in Flutter