Лучший опыт

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

Адаптивный дизайн очень важен для тех, кто ориентируется на разные платформы, используя единую кодовую базу. И особенно это относится к разработке на Flutter, ведь он нацелен на все основные платформы. Как сделать адаптивный дизайн? Существует множество способов сделать адаптивный дизайн на Flutter. Самый простой из них  —  получить информацию с текущего экрана с помощью виджета MediaQuery: Size screenSize = MediaQuery.of(context).size; Orientation orientation = MediaQuery.of(context)
Адаптивный дизайн на разных уровнях Flutter...

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

Заключение

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