-->

Решаем трудности адаптации мобильных приложений с Responsive_framework

Hola, Amigos! Меня зовут Ярослав Цемко, я Flutter-разработчик в компании заказной разработки Amiga. Сейчас я расскажу об очень крутом плагине, который помог нам решить проблемы, связанные с адаптацией контента под разные дисплеи в одном из наших проектов.

Изначально задача состояла в том, чтобы в приложении на разных девайсах (веб, мобильный телефон и специальный планшет с разрешением 1920 x 1080 c низкой плотностью пикселей) был удобный в пользовании интерфейс и красивая картинка.

У нас было несколько путей реализации, и у каждого из них свои сложности и ограничения.

Встроенные средства

Expanded, Flexible и др., которые позволяют сделать хорошую адаптивность верстки. В данном случае они не подходят, так как необходимо соблюсти масштаб всех элементов. Нужно избежать ситуации: когда на экране с низким разрешением, кнопки и поля ввода большие, а на телефоне — нужного размера. К сожалению, данный подход не помог нам достичь нужного результата.

LayoutBuilder

Тоже очень классный инструмент, который:

  • Позволяет получить размер дисплея в любом месте по дереву виджетов.
  • Сделать разную верстку для разных дисплеев.
  • Задавать для разных дисплеев размеры элемента и масштабировать их.

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

responsive_sizer

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

Пример: ширина кнопки должна составлять 20% от ширины экрана. Для этого можно написать в параметре width 20.w, и мы получим нужный размер на разных экранах. Также это можно делать для высоты элементов с помощью параметра 20.h, что будет значить 20% от высоты экрана. Шрифты тоже можно масштабировать, но уже с помощью другого модификатора — sp, что означает плотность пикселей.

Например, если мы хотим чтобы у нас 14ый шрифт масштабировался в зависимости от дисплея, мы пишем в fontSize 14.sp, и шрифт на разных дисплеях будет выглядеть красиво. Это очень похоже на CSS. Однако на этапе требований к задаче заказчик попросил, чтобы в реализации были брейкпоинты, то есть заранее заданные диапазоны экранов, от которых дальше будет производится подстройка размера элементов. Заказчик посчитал этот вариант удобным поскольку ему в дальнейшем будет проще дорабатывать приложение с таким подходом.

Из всех вариантов больше всего понравился responsive_framework, поскольку он отвечает всем требованиям по задаче. После небольшого освоения становится очень удобным инструментом для решения проблем. Почему?

Для нас, разработчиков это хорошо потому что:

  • Брейкпоинты, то есть диапазоны ширины экрана встроены по умолчанию, что позволяет не городить собственный огород, тратя время. Причем эти самые брейкпоинты удобно располагаются в дереве виджетов.
  • Если мы делаем масштабирование элементов, то больше не будет нужды прописывать масштабирование шрифтов для каждого элемента, так как можно обернуть нужный экран в виджет, и все масштабирование будет производится автоматически. Это касается и масштаба элементов.
  • Позволяет удобно задавать размеры для портретной и ландшафтной ориентации.

Для бизнеса:

  • Быстрая скорость разработки.
  • Качественный результат.
  • Легко развивать приложение и не переживать об адаптации для разных устройств.

Исходя из вышеперечисленного мы можем довольно быстро получить нужный нам результат.

В нашем проекте на адаптацию одной страницы с LayoutBuilder было потрачено 12 часов, а с responsive_framework всего 4 часа.

Теперь рассмотрим основные компоненты, которые нам помогут с адаптацией приложения под разные дисплеи.

Breakpoint

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

В качестве имени брейкпоинтам есть константные значения DESKTOP, MOBILE, PHONE и TABLET. Но можно в том числе задать строкой собственное имя.

Breakpoint(start:  0 , end: 400, name: MOBILE)

Где параметры:
start — от какой ширины начинается диапазон;
end — до какой ширины он заканчивается;
name — название брейкпоинта, которое можно будет использовать уже дальше.

ResponsiveBreakpoint

Обертка, которая группирует все заданные диапазоны, затем позволяет через ResponsiveBreakpoint.of(context) получить размеры экрана, ориентацию и имя текущего брейкпоинта.

ResponsiveBreakpoint(

breakpoints: const [

Breakpoint(start: 0, end: 400, name: MOBILE),

Breakpoint (start: 401, end: 1400, name: PHONE),

],

child: child!,

),

Condition

Позволяет задать условия, при выполнении которого будет устанавливаться значение из value при портретной ориентации, либо landscapeValue при ландшафтной.

Можно как задавать значения вручную, так и использовать breakpoint. То есть у нас есть значение, например, ширины от 0 до 400, то будет ширина 200 в портретном режиме, landscapeValue позволяет задавать все в ландшафтном режиме.

Следующий пример показывает:

Condition.equals(name: PHONE, value: 200, landscapeValue: 400)

Если ширина экрана попадает в диапазон, то такое условие вернет 200 в портретной ориентации, а в ландшафтной 400.

Список возможных условий:

.equals — попадающее в диапазон брейкпоинта;
.largerThan — больше чем брейкпоинт;
.smallerThan — меньше чем брейкпоинт;
.between — между значениями ширины. В этом случае диапазон ширины экрана можно задать в параметрах start и end без использования объявленных ранее брейкпоинтов.

Condition.betweeen(start: 400, end: 800, value: 300)

ResponsiveValue

Возвращает значение на основании перечисленных условий заданных в conditionalValues. Используется для виджетов, которым требуется задавать размер на основании перечисленных условий. Например ширины.

Пример:

width: ResponsiveValue<double>(context, conditionalValues: 

[

Condition.equals(name:MOBILE, value: 400), 

Condition.equals(name:PHONE, value: 200)

]).value

В этом случае, если ширина экрана попадает в диапазон MOBILE, то для свойства width в портретном режиме будет выставлено значение 400, а если в диапазон PHONE, то 400.

ResponsiveScaledBox

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

ResponsiveScaledBox(

width: 100,

child: child

)

MaxWidthBox

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

MaxWidthBox(

maxWidth: 1300,

child: child);

Практические примеры

Для примера возьмем приложение со страницей авторизации для планшета и телефона.

На телефоне оно выглядит так:

Но как только мы пытаемся запустить тоже самое, но на планшете с разрешением 1080x1920 при плотности пикселей 440 dpi, у нас все объекты становятся очень большими, а страница авторизации уже не влезает в экран.

Для начала внутри MaterialApp мы пропишем ResponsiveBreakpoints, чтобы в последствии мы могли использовать определенные брейкпоинты.

return MaterialApp(

 builder: (context, child) => ResponsiveBreakpoints(

   breakpoints: const [

     // Ширина от 0 до 400 для планшета с низким разрешением

     Breakpoint(start: 0, end: 400, name: MOBILE),

     // Ширина от 401 до 1400 для телефона

     Breakpoint(start: 401, end: 1400, name: TABLET),

   ],

   child: child!,

 ),

    …

);

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

return ResponsiveScaledBox(

 width: 1200,

 child: Scaffold(

   …

);

Получается симпатичный результат для планшета.

Но если мы запустим такое приложение на телефоне, то на нем уже элементы слишком маленькие.

В этой ситуации нам как раз и понадобятся брейкпоинты, которые мы объявили ранее, и их мы используем внутри ResponsiveValue.

return ResponsiveScaledBox(

 width: ResponsiveValue<double>(context, conditionalValues: [

    // Для планшета

   Condition.equals(name: MOBILE, value: 400),

    // Для телефона

   Condition.equals(name: TABLET, value: 1200),

 ]).value,

 child: Scaffold(

);

Таким образом, в зависимости от брейкпоинта, мы выставляем значение ширины 400 для телефона, а для планшета все те же 1200. Результат:

Заключение

На этом все! Надеюсь, мне удалось объяснить, какие есть компоненты в responsive_framework и как их можно использовать. Мы сравнили также другие варианты решения данной проблемы и продемонстрировали разные способы использования данного плагина.

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

Наша команда Flutter-разработки в Amiga ведёт телеграм-канал Flutter.Много, где мы рассказываем о своем личном опыте, делимся переводами интересных статей иностранных СМИ, кейсами и полезными советами. Приглашаю вас присоединиться к 1700 неравнодушных к кроссплатформенности!

Хотите связаться с владельцами компании напрямую?
Константин Франгуриди
Константин Франгуриди
Account director

НАПИСАТЬ

Дмитрий Тарасов
Дмитрий Тарасов
СЕО

НАПИСАТЬ