Сервис на языке Dart: flutter web-страница

—

от автора

Оглавление

  1. 1. Введение
  2. 2. Backend
  3. 2.1. Инфраструктура.
  4. 2.2. Доменное имя. SSL
  5. 2.3. Серверное приложение на Дарт
  6. …
  7. 3. Web
  8. 3.1. FlutterWeb страница (мы находимся здесь)
  9. …
  10. 4. Mobile
  11. …

Подготовка

В прошлый раз мы закончили на том, что наш веб-сервер получил доменное имя и научился устанавливать безопасное соединение с клиентом. Однако нам пока совсем нечего показать нашему будущему пользователю. Хотя мы уже можем поделиться идеей стартапа и сообщить дату релиза MVP. Для такой задачи подойдёт информационная web-страница. Напишем её на Dart с использованием фреймворка FlutterWeb. Все наши клиентские приложения сервиса станут расширением именно этой страницы. Постараемся вести разработку максимально адаптивно и структурировано, чтобы развитие и сборки под нужные платформы (web-android-iOS) стали просто рутиной.



Начнём с установки Flutter:

  • Установим git
  • Склонируем репозиторий с beta версией Flutter командой
    git clone https://github.com/flutter/flutter.git -b beta
  • Для запуска команд flutter из командной строки необходимо указать в операционной системе путь до его исполняемых файлов. Откроем переменные ОС, для этого начнём вводить «изменение переменных среды текущего пользователя» в строке поиска

    В окне выберем переменную Path и нажмём Изменить. В открывшемся списке создадим новую строку с адресом до исполняемых файлов flutter в файловой системе, например C:\flutter\bin

  • Установим расширение VScode для flutter
  • Перезапустим VScode (чтобы применились новые переменные ОС) и в терминале проверим состояние flutter командой
    flutter doctor

    здесь самое важное, что flutter установлен в beta версии (с поддержкой web разработки)

  • Теперь активируем веб разработку командой
    flutter config --enable-web

Создание нового проекта и запуск отладки

Создаём новый проект командой

flutter create <название проекта>

Сразу откроем его в VScode командой

code <название проекта>

Откроем файл main.dart в папке lib и запустим отладку командой F5:

Возможно при первом запуске отладки понадобится выбрать Chrome устройством для отладки:

Удалим содержимое файла main.dart. Добавим пустой метод main и корневой класс приложения, возвращающий в методе build() экземпляр MaterialApp:

Далее создадим следующий набор вложенных папок проекта:

Кратко опишем назначение каждой из них:

  • di — механизм для связи между компонентами приложения. Здесь будут создаваться и регистрироваться все необходимые сервисы, репозитории, сетевые клиенты, необходимые для работы приложения. Я буду использовать библиотеку GetIt
  • domain — data-объекты. Классы представления данных
  • res — цвета, строки, импорты путей к картинкам и шрифтам. Всё, что связано со статическими ресурсами
  • service — сервисы для работы с данными
  • ui — интерфейс
  • utils — вспомогательные классы

В файле pubspec.yaml добавим необходимые зависимости:

Подготовка к масштабированию элементов UI

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

Начнём с картинок заднего фона. Их подготовка не входит в тему статьи, поэтому просто оставлю здесь эти две ссылки:

  • Pixabay.com — хранилище контентных фотографий
  • Paint.net — графический редактор

Готовые картинки разместим в папке /assets/images, в файле pubspec.yaml добавим этот путь к ресурсам:

Я предпочитаю доступ к ресурсам в виде дерева c параметрами. В данном случае путь к картинке заднего фона заглушки:

images.background(bool isPortrait).stub

Для этого в папке res создадим файл images.dart с классами адресов картинок:

Для масштабирования размеров интерфейса и шрифтов мы подключили библиотеку ScreenUtil. Её функциональность сводится к двум вещам:

  • Регистрация «базового» размера экрана. Здесь необходимо задать ширину и высоту экрана, для которого ведётся основная верстка и необходимость масштабирования шрифтов.
  • Набор расширений, позволяющий для чисел (num) применить масштабирующий коэффициент. Например 100.w означает, что результатом этого выражения будет для экрана шириной 1920dp => 100dp, а для экрана iPhone8 с шириной 414dp => 100х(414/1920) = 21,6dp. То есть в пять раз меньше. Также предусмотрены расширения для параметра высоты и размера шрифтов.

Создадим файл /utils/screen_util_ext.dart и статический метод инициализации в нём:

Вызов метода инициализации масштабирования добавим в метод build() корневого виджета:

Расширим функциональность библиотеки масштабирования несколькими дополнительными расширениями в файле /utils/screen_util_ext.dart:

Инъекция зависимостей

Пришло время внедрить механизм создания и регистрации компонентов приложения с помощью библиотеки GetIt. В папке lib/DI/ создадим файл di_container.dart. В нём напишем статический метод getItInit() и инициализируем экземпляр контейнера GetIt. Зарегистрируем первый компонент — экземпляр класса Images:

Вызов метода инициализации добавим в main():

Доступ к компоненту Images будет выглядеть так:

Таким же образом зарегистрируем класс с ресурсами строками.

Страница-заглушка

Теперь в папке UI создадим файл stub.dart с классом страницы заглушки StubScreen, расширим базовый класс StatelessWidget и переопределим его абстрактный метод build(). Наша страница представляет собой картинку на заднем плане и два информационных блока перед ней, размещающихся в зависимости от ориентации экрана.

Репозитории и сервис

Для динамического отображения оставшегося до релиза времени необходимо:

  1. Получить с сервера настройки с датами начала разработки и релиза
  2. Создать поток событий изменения оставшегося времени
  3. Объединить эти данные, передав в выходной поток для отображения на UI

Опишем доменные объекты (POJO) для этих данных:

Репозитории для получения настроек и создания потока событий:

Сервис для логики событий:

Зарегистрируем эти компоненты в DI контейнере:

Виджет оставшегося времени

Оставшееся до релиза время можно представить как 4 числа: дни, часы, минуты, секунды. Представим эти параметры в виде перечисления:

Добавим функциональности параметрам с помощью расширения:

Виджет для отображения круговой шкалы, числа и подписи будет анимированным, для этого расширим класс StatefulWidget. Его особенность в том, что Element (построенное и отображаемое представление) соотносится не с самим виджетом, а с его состоянием (State). Состояние, в отличие от виджета мутабельно. То есть его поля могут быть изменены без полного пересоздания экземпляра.

Здесь необходимо уточнить, что такое Animation, AnimationController и TickerProviderStateMixin. Итак AnimationController — обёртка над простым параметром double value. Значение этого параметра меняется линейно в пределах от 0,0 до 1,0 (также его можно менять в обратную сторону или сбрасывать в 0,0). Однако для изменения этого параметра используется специальный объект TickerProviderStateMixin, который является обязательным параметром для AnimationController и сообщает ему, что графический движок готов построить новый кадр. Получив такой сигнал AnimationController рассчитывает сколько времени прошло от предыдущего кадра и вычисляет насколько нужно изменить значение своего value. Объекты Animation подписываются на AnimationController и содержат в себе некоторую функцию зависимости выходного значения от линейного (по времени) изменения значения AnimationController.

Метод инициализации состояния initState() вызывается один раз при создании:

При уничтожении состояния виджета вызывается метод dispose():

Представлением виджета будет стек (Stack), с помещёнными в него AnimatedBuilder для числа и шкалы:

Остаётся реализовать графический примитив в виде дуги:

Добавим 4 таких виджета на экран заглушки:

Сборка и релиз

Перед сборкой приложения необходимо заменить название и описание приложения в файлах ./web/index.html ./web/manifest.json и pubspec.yaml.

Останавливаем отладку и собираем релиз приложения командой

flutter build web

Готовое приложение находится в директории ./build/web/. Обратите внимание, что файлы .last_build_id и main.dart.js.map служебные и могут быть удалены.
Разместим приложение на сервере, подготовленном в предыдущей статье. Для этого достаточно скопировать содержимое директории ./build/web/ в /public/ нашего сервера:

scp -r ./* root@91.230.60.120:/public/

Результат

Исходный код github

Вопросы и комментарии приветствуются. Пообщаться с автором можно в Telegram канале.

Вместо заключения

Наше клиентское приложение уже готово получить первые данные с сервера — сведения о дате релиза. Для этого в четвертой статье мы создадим каркас серверного приложения и разместим его на сервере.

ссылка на оригинал статьи https://habr.com/ru/company/surfstudio/blog/514640/