Создание прогрессивного веб-приложения (PWA) с помощью Flutter
Представьте, вы только что закончили создание приложения, которое изменит мир, вы отправили Android apk
в Google Play Store и iOS ipa
Apple App Store и ожидаете проверки. Но когда вы проверяете свою электронную почту, вы обнаруживаете, что ваше приложение для IOS было отклонено. Учитывая срочность выпуска, что вы сделаете сейчас?
Хорошая новость заключается в том, что ваше приложение было создано с помощью Flutter, который является отличным инструментом для создания прогрессивных веб-приложений (PWA). Но что такое PWA?
В этом руководстве обсуждается, что такое PWA, как создать Flutter PWA, что следует учитывать при его создании, эффективно обрабатывать веб-навигацию, создавать и публиковать веб-приложение Flutter с помощью Codemagic, как установить PWA и преимущества PWA.
Чтобы получить максимальную отдачу от этого руководства по Flutter PWA, вы должны как минимум хорошо разбираться во Flutter framework и читать код Flutter. По крайней мере, некоторое знакомство с Navigator 2.0 необходимо для раздела «Навигация» в этом посте.
Кроме того, вам понадобится учетная запись Codemagic для завершения сборки и развертывания. Если у вас его нет, вы можете бесплатно зарегистрироваться.
Что такое PWA?
Прогрессивное веб-приложение, или сокращенно PWA, — это просто веб-страница, которую можно сохранить (установить) из Интернета, чтобы обеспечить ощущение родного приложения. PWA работают в автономном режиме и поддерживают уведомления, как и нативные приложения.
Flutter позволяет вам создавать несколько приложений с одной базой кода, поэтому вы можете довольно быстро создать веб-приложение или PWA, если у вас уже есть кодовая база для Android и iOS.
Чтобы понять плюсы и минусы PWA, можно ознакомиться со статьей о PWA и нативных приложениях.
Создание Flutter PWA
Создать PWA во Flutter так же просто, как создать веб-приложение Flutter. Что может быть лучше, чем сейчас, чтобы настроить наш демонстрационный проект Flutter?
Настройка
Клонируйте начальный код, выполнив команду git clone -b starters-code git@github.com:JasperEssien2/pwa_demo.git
.
Зависимости, используемые в этом проекте, показаны в таблице ниже.
Зависимость | Сценарии использования |
font_awesome_flutter | Чтобы получить доступ к значкам, которых нет в значке материала |
Beamer | Используется для эффективной, сложной навигации |
Вы должны сделать все возможное, чтобы ориентироваться в кодовой базе, чтобы понять, что делает каждый компонент, но вот разбивка основных компонентов.
/lib/widgets/detail_widgets.dart
: содержит код виджета страницы сведений/lib/widgets/job_list.dart
: содержит код виджета списка заданийlib/app_provider.dart
: содержит реализацию
InheritedWidget
с именем AppProvider
InheritedWidget
обеспечивает хороший способ передачи данных вниз по дереву виджетов Flutter.AppProvider
будет использоваться для передачи данных фиктивных заданий и achildBeamerkey
(подробнее об этом в разделе навигации).
lib/extensions.dart
: определяетisLargeScreen
,isExpanded
иprovider
геттерные функции вBuildContext
Dart extensions
позволяет определять пользовательские функции в существующих библиотеках.
lib/job_model.dart
: определяет классы моделей, которые использует приложениеlib/main.dart
: содержит точку входа в наше приложение и виджеты домашней страницы.
Давайте рассмотрим некоторые вещи, которые следует учитывать при создании веб-приложения во Flutter.
Аспекты при создании веб-приложения с Flutter
Есть несколько важных вещей, которые следует учитывать при создании веб-приложения с помощью Flutter. В этой статье рассматриваются два важных аспекта: отзывчивость и навигация.
Отзывчивость во Flutter
Учитывая, что наше веб-приложение может работать на экранах разного размера, мы, как разработчики Flutter, должны сделать все возможное, чтобы учесть их. Адаптивное приложение — это приложение, настроенное для разных размеров экрана. Давайте сделаем наше прогрессивное веб-приложение Flutter адаптивным.
В этом проекте мы будем использовать шаблон проектирования «список-подробности». Чтобы сделать его отзывчивым, мы будем использовать следующий подход:
- На экране меньшего размера представление списка занимает весь экран, и при нажатии на элемент списка открывается новый экран для отображения сведений о задании.
- На экране большего размера представление списка занимает левую панель, а виджет сведений отображается рядом с представлением списка на том же экране.
- На очень большом экране мы добавим поля слева и справа от главного экрана.
Подобная структура затрудняет реализацию навигации, поскольку пользователь может изменить размер окна или изменить ориентацию устройства. Но вы не хотите, чтобы ваши пользователи видели уродливый широкий экран подробностей после изменения размера окна с маленького на больший размер (если экран подробностей уже находится в поле зрения). Другими словами, вы хотите, чтобы навигация была синхронизирована, даже если пользователь внезапно изменит размер окна.
Навигация для Flutter Web
Хотя Navigator 1.0 (императивный API) отлично работает для большинства приложений Flutter, он очень неэффективен для сложной маршрутизации для веб-приложений и сложных прямых ссылок. Вот почему команда Flutter представила Navigator 2.0 (декларативный API) для Flutter. Хотя он намного эффективнее, он также вносит большую сложность.
С положительной стороны, мы можем использовать пакеты Flutter, такие как go_router
и Beamer
, которые построены на Navigator 2.0, чтобы упростить себе задачу.
Мы используем пакет Beamer для этого проекта из-за его гибкости в обработке вложенной навигации. (Он без проблем синхронизирует историю навигации как вложенного, так Router
и родительского Router
).
Реализация
Вы, наверное, уже заметили, что хотя большая часть кода уже написана, вам осталось решить некоторые задачи по созданию нашего Flutter PWA. Этот раздел проведет вас через реализацию этих TODO.
Первая задача: запуск с помощью Beamer
Наша первая задача — инициализировать BeamerDelegate
переменную. Найдите TODO: 1
в lib/main.dart
файле и назначьте приведенный ниже код routerDelegate
переменной.
final routerDelegate = BeamerDelegate(
locationBuilder: (routeInformation, _) {
return HomeLocation();
},
);
BeamerDelegate
— это реализация Navigation 2.0 от Beamer RouterDelegate, которая используется для создания и настройки навигационного виджета на основе всплывающих и проталкиваемых маршрутов, которые он получает от движка.
Помогает Beamer определить, какой экран locationBuilder
отображать, на основе конфигурации разработчика. Экземпляр, HomeLocation
определенный для этого проекта, передается в качестве аргумента в файл locationBuilder
. HomeLocation
является BeamLocation
реализацией.
Реализуя BeamLocation
, мы можем настроить, какой URI обрабатывать и как построить стек страниц на основе URI. Просмотрите комментарии в фрагменте кода ниже.
class HomeLocation extends BeamLocation<BeamState> {
@override
List<BeamPage> buildPages(BuildContext context, BeamState state) {
JobModel? job;
// Try to get the job with the [id] passed from the route path
try {
job = context.provider.jobs.firstWhere(
(element) => element.id.toString() == state.pathParameters['id']);
} catch (e) {
log(e.toString());
}
return [
/// Always have the [MyHomePage] in the stack of pages
const BeamPage(
key: ValueKey('home'),
title: "Home",
name: '/',
child: MyHomePage(
currentIndex: 0,
),
),
/// Only show the detail page over the [MyHomePage] when on a small screen and
//a job id is passed alongside the URI. Note that [MyHomePage] is configured to be responsive.
if (!context.isLargeScreen && state.pathParameters.containsKey('id'))
BeamPage(
key: ValueKey('job-${job!.id}'),
title: "${job.company.name} - ${job.role}",
/// The URL path to navigate to this page, the [id] is a placeholder for the actual job id to be passed
name: '/:id',
child: JobDetailWidget(
model: job,
),
),
];
}
/// This is where you indicate the URI patterns that can be handled by this [BeamLocation]
@override
List<Pattern> get pathPatterns => ['/:id'];
}
Несколько аналогичная реализация вышеописанного сделана для вложенного навигатора. Местоположение луча называется InnerJobLocation
, что определяет текущий экран сведений, на который осуществляется переход при отображении приложения на большом экране.
Вторая задача: определить, какой виджет вернуть для маленьких экранов.
Цель этого раздела — определить, какой виджет должен возвращаться, когда приложение работает на маленьком экране.
Найдите TODO: 2
в строке 123 и верните JobList
виджет;
return const JobList();
Третья задача: определить, какой виджет вернуть для больших экранов.
Замените TODO
строку 134 приведенным ниже кодом, чтобы отобразить виджет со списком сведений, когда приложение работает на большом экране.
return Container(
color: Colors.white70,
// Add a horizontal margin for the largest screen size
margin: EdgeInsets.symmetric(horizontal: horizontalMargin),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Display the [JobList] on the left pane and constrain its width
SizedBox(width: listviewMaxWidth, child: const JobList()),
/// Use a nested Router
SizedBox(
width: detailMaxWidth,
/// By using the [Beamer] widget, we add a nested Router
child: Beamer(
/// Pass in the [childBeamerKey] instantiated in the [AppProvider] class
key: context.provider.childBeamerKey,
/// Pass a routerDelegate that uses the [InnerJobLocations]
routerDelegate: innerRouterDelegate,
),
),
],
),
);
Четвертая задача: Избавиться от исключений
Удалите Exception
брошенный line 155
, который был добавлен, чтобы компилятор был доволен отсутствием оператора return.
Пятая задача: заставить луч работать
Последней задачей является создание фактического «просветления» — другими словами, навигации при нажатии виджета элемента задания.
Теперь мы столкнулись с небольшой проблемой. Мы реализовали два Router
s : родительский Router
и вложенный Router
, который добавляется в дерево виджетов при запуске приложения на устройстве с большим экраном. Поэтому нам нужно использовать другой экземпляр Beamer
для навигации в зависимости от размера экрана.
Перейдите к строке 40 lib/widgets/job_list.dart
и замените TODO
ее кодом ниже.
context.isLargeScreen
// Use the nested Beamer to navigate
// to the correct detail page for large screen
? context.provider.childBeamerKey.currentState?.routerDelegate
.beamToNamed('/${model.id}')
: context.beamToNamed('/${model.id}');
Теперь вы можете запустить свое веб-приложение, запустив flutter run -d chrome
.
Я знаю, что вы хотите загрузить Flutter PWA на свое мобильное устройство, но нам нужно разместить наше мобильное веб-приложение. К счастью, Codemagic многое для нас упростил.
Публикация Flutter PWA с помощью Codemagic
Codemagic — отличный инструмент непрерывной интеграции/доставки (CI/CD), который работает с Flutter. Инструменты CI/CD автоматизируют процессы создания и развертывания, чтобы разработчики могли сосредоточиться на таких задачах, как выполнение требований к продукту, а также написание и сопровождение высококачественного кода.
С Codemagic вы можете автоматизировать процесс сборки для всех платформ, поддерживаемых Flutter. Здесь мы сосредоточимся на настройке веб-конвейера.
- Начните со входа в систему или создания учетной записи, если у вас ее нет, нажав здесь.
- Добавьте приложение, нажав кнопку Другое -> Добавить репозиторий.
- Введите URL-адрес репозитория, установите флажок «Общедоступный репозиторий» и выберите приложение Flutter в качестве типа проекта.
Вы можете использовать URL репозитория для полной версии: git@github.com:JasperEssien2/pwa_demo.git
- Выберите платформы, для которых вы хотите выполнить сборку, в разделе Сборка для платформ. Выберите пока только Интернет.
- Прокрутите вниз до раздела «Распространение», включите публикацию статических страниц Codemagic и введите нужный субдомен веб-страницы.
- Сохраните ваши настройки.
- Нажмите Начать новую сборку.
- Пройдет некоторое время, пока Codemagic выполняет тяжелую работу по созданию и публикации вашего веб-приложения. Codemagic уведомит вас, как только это будет сделано.
Теперь вы можете получить доступ к опубликованной веб-странице, посетив yourSubdomain/codemagic.app.
(yourSubdomain — это текст, который вы вводите в субдомен веб-страницы на шаге 5).
Вот приложение, работающее в браузере Chrome для настольных компьютеров.
Вероятно, вы хотите, чтобы ваше приложение размещалось на вашем собственном домене. На момент написания этой статьи Codemagic поддерживает настройку автоматической публикации ваших веб-приложений в AWS S3 Bucket.
Установка PWA
- iOS: посетите веб-страницу в браузере Safari, а затем нажмите «Поделиться» -> «Добавить на главный экран» -> «Добавить».
На момент написания этой статьи Chrome не поддерживал установку PWA на iOS.
- Android: посетите веб-страницу в браузере Chrome и коснитесь кнопки «Добавить pwa_demo на главный экран».
Вы можете найти PWA среди установленных приложений на вашем мобильном устройстве. Откройте приложение, и вы должны увидеть красивую заставку при запуске.
По умолчанию в PWA для Android включен экран-заставка. Чтобы добавить экраны-заставки для iOS PWA, вы должны указать это в /web/index.html
файле. Создавайте изображения экрана-заставки для определенных размеров экрана iOS с помощью инструментария PWA.
Преимущества PWA
- Предоставьте пользователям легальный способ установить ваше iOS-приложение без головной боли, связанной с использованием магазинов приложений.
- Быстрое время установки
- Небольшой размер приложения для ваших пользователей
- Нативные функции, подобные приложениям
- Мгновенные обновления
Вывод
Вы, вероятно, согласитесь, что Flutter — отличный инструмент для PWA и в сочетании с Codemagic делает работу намного проще и удобнее.
Когда вы столкнетесь с проблемой, описанной в начале этой статьи, не волнуйтесь — вы можете просто использовать Flutter Web, чтобы ваши пользователи могли установить PWA.
В настоящее время веб-приложение нормально работает в браузере до тех пор, пока не будет получен доступ к URL-адресу, содержащему недопустимый идентификатор задания (например, pwa_demo.codemagic.app/60). Задача состоит в том, чтобы направить пользователей на экран с ошибкой, который сообщает им, что задание, к которому они пытаются получить доступ, не существует.