Pull to refresh
EPAM
Компания для карьерного и профессионального роста

2 шага к построению адаптивной верстки Flutter-приложения

EPAMFlutter
Tutorial

В этой статье речь пойдет о том, как создать Flutter-приложение, которое умеет адаптироваться к разным экранам и ориентациям. Статья будет полезна как начинающим, так и опытным пользователям Flutter. Первые найдут шаблон для изучения, вторые — еще один взгляд на этот вопрос.

Постановка задачи или проблемы адаптивной верстки

«Делай то, что тебе нравится». Flutter

Звучит как духовно обогащенный мотиватор, но это — реальный взгляд разработчиков фреймворка на эту проблему. У Flutter нет одного решения, «прибитого гвоздями», здесь у разработчика полная свобода и возможность выбирать способ решения этих проблем (заодно и собрать грабли по пути).

На данный момент Flutter поддерживает мобильные платформы (Android, iOS), Web, редко используется для desktop. Это значит, что приложение должно поддерживать широкий диапазон разрешений экранов устройств и ориентации. Также мобильное устройство (если оно не квадратное) может быть повернуто пользователем в портретную или ландшафтную ориентацию. Пользователи мобильных устройств любят и умеют это делать во время работы приложения, чтоб рассмотреть подробнее содержимое экрана. Так что, чтобы не разочаровывать пользователя, мы должны позаботиться о проблеме поворота экрана во время работы приложения.

И при всем этом приложению желательно еще и работать, отображая информацию о своей жизнедеятельности, несмотря на характеристики и параметры устройства, на которое его занесло, и на то, какие действия с ним может совершать пользователь.

«Смешать, но не взбалтывать». Проблема мультиплатформенного дизайна

Редко встретишь дизайнера, который понимает проблемы мультиплатформенного дизайна. И задача дизайнера Flutter-приложения — приготовить такой коктейль из элементов мобильного дизайна, который не вызывает отторжения у пользователя.

Ведь нужно как-то совместить эти элементы с привычным UX на платформах, на которых их раньше не было. Например, FAB (floating action button in material design) и другие элементы и подходы дизайна, привычные на мобильных платформах, на вебе и десктопе немного в новинку, и наоборот — приемы, привычные для веба, смотрятся диковато на мобилках.

Обычно обновления дизайна происходят итерационно. Так, дизайнер вряд ли сразу выдаст окончательный вариант под все ориентации и разрешения. Поэтому нужно предусмотреть возможность добавления и изменения, не трогая то, что уже работает.

Как это уже работает в Android

Проблема и способы ее решения известны. На Android в свое время это называлось отзывчивый (responsive) дизайн. По этому поводу даже есть даже целые гайдлайны: Responsive UI - Layout (правда сейчас уже в архиве), Support different screen sizes.

Как это выглядит на практике для разработчика? В самом начале существует один макет, к которому по мере необходимости добавляем макеты для разных по разрешению экрана устройств, а затем обе ориентации для каждого типа устройств. Далее эти макеты выбираются в зависимости от того, на каком устройстве (с каким разрешением) и в какой ориентации они работают.

Для экранов с большой площадью обычно используют макеты с большим количеством информации, которую можно отобразить. Хороший пример — master/detail flow из шаблонов новых приложений в Android Studio. Это классика того, как рекомендуется работать с экранами большой площади in android way.

Долго ли сказка говорится, начнем писать код. Для этого используем модную нынче методологию BDD.

Реализация

Перед тем как писать код, опишем сценарии:

  1. В пределах одного макета на разных устройствах с разной плотностью пикселей должно выполнятся масштабирование контента (как при использовании dpi указания размеров).

  2. Я хочу иметь возможность добавлять макеты в любом порядке (сделали макет для landscape — отлично, используем его везде, сделали для portrait — отлично, автоматически подставляем его для портретной ориентации).

  3. Я хочу иметь возможность добавлять макеты как в Android Studio для landscape, для portrait, для более широких экранов, для более узких.

  4. Добавление новых макетов не должно влиять на существующие макеты.

  5. Цепочка вызовов для созданного макета не должна без моего ведома изменяться со временем.

  6. Я хочу, чтобы логика выбора макета не изменялась, ее не нужно было изменять при добавлении/удалении макетов.

  7. Логика выбора должна быть одинаковой и предсказуемой.

Код примера, рассматриваемого в статье и пакет, его реализующий, находится по ссылкам:

Пакет

Пример в папке example

Инициализация

Начнем с инициализации пакета. Вызов нашей MaterialApp оборачиваем в вызов инициализации виджета подстройки размеров виджетов под dpi экранов:

@override
Widget build(BuildContext context) {
 return LayoutBuilder(
   builder: (context, constraints) {
     return OrientationBuilder(
       builder: (context, orientation) {
         SizerUtil().init(constraints, orientation);
         return MaterialApp(

Этот код находится в example lib\main.dart

Шаг 1. Настройка виджетов под изменяемое разрешение экрана

Мы хотим отображать виджеты на разных экранах наиболее близко к задуманному при первоначальном дизайне. Эта часть заимствована из библиотеки sizer и работает так же как в ней.

image_tooltip
image_tooltip
image_tooltip
image_tooltip

Также можно использовать следующий метод, основанный на SizerUtil.orientation свойстве для более вариабельной настройки параметров виджета. Пример:

appBar: AppBar(
 title: SizerUtil.orientation == Orientation.portrait
     ? const Text('portrait')
     : const Text('landscape'),
),

На AppBar выдается заголовок с названием текущей ориентации. Этот код находится в example lib\screens\home_screen.dart

Проверим соответствие сценариям.

Этот способ выполняет наше пожелание из пункта 1 списка Перед тем как писать код, опишем сценарии: «1. В пределах одного макета на разных устройствах с разной плотностью пикселей должно выполнятся масштабирование контента (как при использовании dpi указания размеров)».

Шаг 2. Используем специализированный виджет для работы с разными разрешениями экрана и ориентацией

Поддержка изменения ориентации и переключения виджетов под разные размеры (разрешения) экрана реализована с использованием виджета ResponsiveWidget. В его поля мы подставляем макет (виджет) для каждой пары разрешения/ориентации. В начале у него есть одно обязательное поле (по аналогии с default xml in android) landscapeLargeScreen. Дополнительные виджеты под другие значения разрешения/ориентации опциональны, т.е их можно добавить по мере появления:

  • landscapeMediumScreen

  • landscapeSmallScreen

  • portraitMediumScreen

  • portraitSmallScreen

  • portraitLargeScreen

В приведенном ниже примере с помощью виджета WelcomePage создается страница в ориентации landscape и строится макет с использованием прокрутки контента в вертикальной плоскости, а для ориентации portrait используется такой же виджет с таким же набором страниц, только контент прокручивается в горизонтальной плоскости:

body: ResponsiveWidget(
 landscapeLargeScreen: WelcomePage(
   pageIndex: 0,
   scrollDirection: Axis.vertical,
   children: listOfPages,
 ),
 portraitLargeScreen: WelcomePage(
   pageIndex: 0,
   scrollDirection: Axis.horizontal,
   children: listOfPages,
 ),
),

Для визуалов:

https://github.com/NickZt/sizer_mod/raw/master/example/images/portrait_land_mob.gif

image_tooltip
image_tooltip

https://github.com/NickZt/sizer_mod/raw/master/example/images/portrait_mob.gif

image_tooltip
image_tooltip

Мы получили экран, который имеет разные макеты для портретной и альбомной ориентации. Когда устройство меняет ориентацию, мы перестраиваем наш макет.

Как мы обнаруживаем изменения ориентации? Мы смотрим на отношение ширины к длине страницы. И в зависимости от этого переключаем макеты.

Проверим соответствие сценариям

Это выполняет наши пожелания, указанные со 2 по 7 пункт из Перед тем как писать код, опишем сценарии:

2. Я хочу иметь возможность добавлять макеты в любом порядке (сделали макет для landscape — отлично, используем его везде, сделали для portrait — отлично, автоматически подставляем его для портретной ориентации).

3. Я хочу иметь возможность добавлять макеты как в Android Studio для landscape, для portrait, для более широких экранов, для более узких.

4. Добавление новых макетов не должно влиять на существующие макеты.

5. Цепочка вызовов для созданного макета не должна без моего ведома изменяться со временем.

6. Я хочу, чтобы логика выбора макета не изменялась и ее не нужно было изменять при добавлении/удалении макетов.

7. Логика выбора должна быть одинаковой и предсказуемой.

Выводы

По рецепту из моей статьи можно приготовить приложение, в котором размер и ориентация экрана на всех 3-х платформах будут работать одинаково. Надеюсь, этот способ будет полезен и в вашем приложении.

Буду рад общению по теме (и не по теме тоже :)) в комментариях.

К статье добавил опрос, результаты которого помогут мне выбрать тип приложения, который я возьму в качестве примера для второй части статьи. В ней я собираюсь более подробно остановиться на виджетах, помогающих сделать приложение отзывчивым. А также хочу рассказать о некоторых нюансах работы с ними в сравнении с элементами дизайна, принятыми в Android. Также хочу рассмотреть способы автоматического импорта дизайна из Figma.

Код примера, рассматриваемого в статье, и пакет, его реализующий, находятся здесь:

Пакет

Git-репозиторий

→ Пример — в папке example


Полезные ссылки
Only registered users can participate in poll. Log in, please.
Примеры из какой сферы/направления вы бы хотели увидеть в следующей статье?
44.44% адаптивная посадочная страница (лендинг) 8
38.89% e-commerce 7
33.33% адаптивный сайт 6
11.11% приложение для связи между аккаунтами в социальных сетях и e-commerce 2
16.67% умный дом 3
5.56% телемедицина 1
5.56% B2B 1
55.56% типичный стартап: лендинг приложения, приложение для клиентов (участников) 10
18 users voted. 5 users abstained.
Tags:flutteradaptive design
Hubs: EPAM Flutter
Total votes 11: ↑8 and ↓3 +5
Views5.8K

Top of the last 24 hours

Information

Founded
1993
Location
США
Website
www.epam.com
Employees
over 10,000 employees
Registered
Representative
vesyolkinaolga