Вернуться к блогу

Мобильное приложение на Flutter для АЗС за 2 месяца

27.03.2024

Hola, Amigos! На связи агентство продуктовой разработки Amiga. Сегодня расскажем, как мы перенесли мобильное приложение с Битрикс на Flutter за 2 месяца, сохранив позиции в сторах и многотысячную базу клиентов.

О клиенте

Хакасская топливная компания (ХТК) — это сеть АЗС в республике Хакасия, где проживает 536 тыс.чел. Городам с небольшим населением присущ паттерн поведения пользователей — заправляться на одной и той же заправке годами.

У компании уже было приложение на Битрикс, в котором зарегистрировано более 70 000 пользователей. Им доступны следующие функции:

  • личный кабинет;
  • история транзакций;
  • программа лояльности;
  • информация об акциях;
  • форма обратной связи.

картнка в тексте

Бонусная программа позволяет копить баллы, которые в дальнейшем клиент может потратить на автомойку или СТО

Бизнес-задачи мобильного приложения АЗС:

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

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

3. Быстрое тестирование гипотез для управления бизнес-показателями.

картнка в тексте

В 2023 году Битрикс прекратил поддержку мобильного модуля, и для ХТК это грозило следующими последствиями:

  • Приложение может исчезнуть из магазинов App Store и Google Play.
  • Клиенты потеряют связь с компанией и свои данные: бонусы, баллы, историю покупок, которые они копили годами.
  • Минус один канал коммуникации с клиентами, утрата доверия.

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

Наши задачи

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

2. Разработать приложение как можно быстрее, чтобы не потерять доверие клиентов.

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

Сложности проекта

Ранее у ХТК не было полноценного приложения. С помощью специального модуля Битрикс была создана «мобильная платформа» с контентом, который подгружался с сайта на Битрикс. Нам нужно было передать этот контент с Битрикс в новое приложение на Flutter. Для этого необходимо создать специальный «мост» — в мире IT это называется внешним интерфейсом. Этот мост позволяет данным легко перемещаться между сайтом и мобильным приложением.

Вызов заключался в отсутствии возможности создания внешнего интерфейса для обмена данными с мобильным приложением в рамках Битрикс CRM. Нам пришлось создать свой собственный «переводчик», т.е. промежуточный сервер, который помогал нашему новому приложению понимать и общаться с данными компании.

картнка в тексте

Схема передачи данных между CRM клиента и нашим сервером для бесперебойной работы нового приложения на Flutter

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

Решение

Учитывая сроки, бюджет и необходимость двух платформ (iOS и Android), использование нативных решений можно исключать. Разработка нативных приложений обычно занимает больше времени, чем кроссплатформенная разработка, из-за необходимости создания и поддержки отдельного кода для каждой платформы. Это также относится к процессу отладки, развертывания и публикации приложения в магазинах приложений, который может потребовать дополнительных шагов и времени.

Реализацию нового приложения на Flutter мы начали с упрощения логики его функционала. Для этого разработали пользовательские сценарии, описывающие типичные пути использования приложения различными категориями пользователей. Каждый сценарий содержал последовательность шагов, которые пользователь выполняет для достижения определенной цели. Например, сценарии регистрации нового пользователя в бонусной программе, получение бонусов и т.д.

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

картнка в тексте

Дизайн интерфейса приложения «До» и «После»

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

картнка в тексте

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

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

Разработка backend с опережением frontend на неделю представляла собой стратегическое решение, направленное на оптимизацию и ускорение процесса разработки всего приложения.

Тестирование функционала проводилось каждые 3 дня для оперативного выявления и устранения технических ошибок.

Почему именно Flutter:

  • стандартизация UI-кита позволила экономить время на верстку до 30%;
  • использование готовых решений; одновременный релиз на обе платформы iOS и Android;
  • экономия на коммуникациях между командами (одна команда на Flutter вместо 2-х нативных команд).

картнка в тексте

Первый релиз (MVP) включал в себя следующие функции:

  • Авторизация/регистрация пользователей.
  • Главная страница с программой лояльности.
  • Интеграция с бонусным центром для начисления бонусов пользователям за покупки.
  • Страница акций.
  • Обратная связь.
  • Профиль пользователя.
  • История операций.
  • Пуш-уведомления.
  • Offline режим для возможности начислить бонусы при отсутствии интернет соединения.

картнка в тексте

Результат

От старта работ до выпуска первой версии приложения в App Store и Google Play прошло 2 месяца. Мы разработали полноценное мобильное приложение с полностью обновленным дизайном, сохранив данные уже зарегистрированных пользователей: доступы в приложение, бонусные баллы, транзакции.

Состояния жизненного цикла приложения

Flutter имеет 5 состояний приложения.

Возобновленное состояние (resumed)

В этом состоянии приложение видно пользователю и активно (на экране). Можно отлавливать события с помощью слушателя AppLifycycleListener - onInactive.

Неактивное состояние (inactive)

В этом состоянии приложение может по-прежнему быть видимым для пользователя (например, в фоне), но оно не получает вводимые пользователем данные и может перейти в состояние паузы в любое время. 

На Android это может произойти, когда фокус переключается на другое действие, например телефонный звонок, системный диалог, приложение с разделенным экраном или всплывающее окно. В iOS это может произойти при телефонном звонке, входе в переключатель приложений, ответе на запрос TouchID. Здесь также можно воспользоваться методом onResume (при восстановлении работы) и onHide (при скрытии приложения) для обработки событий.

Скрытое состояние (hidden)

Все представления приложения скрыты, либо потому, что приложение вот-вот будет приостановлено (на iOS и Android), либо потому, что оно свернуто или помещено на рабочий стол, который больше не отображается. Тут работают методы onPause (переход в состояние паузы) и onShow (переход в inactive).

Состояние паузы (paused)

В этом состоянии приложение не видно пользователю и оно не отвечает на вводимые пользователем данные и работает в фоновом режиме. Методы: onRestart (переход в перезапуск) и onDetach (переход в отделенное состояние).

Отделенное состояние (detached)

Приложение вообще не запущено. Возможные условия: когда экран инициализируется впервые (в процессе присоединения состояния) или уничтожается из Navigator.pop() (например, нажатие кнопки «Назад» на домашней странице приложения).

Обработка событий жизненного цикла приложения

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

didChangeAppLifecycleState: этот метод вызывается всякий раз, когда приложение переходит между состояниями жизненного цикла. Он получает AppLifecycleState объект, который обозначает новое состояние. Можно использовать этот метод при переходе приложения между состояниями.

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

Вот несколько примеров использования этих методов для обработки событий жизненного цикла приложения:

class _AppLifeCycleExampleState extends State
        with WidgetsBindingObserver {
    AppLifecycleState? _appLifecycleState;
    @override
    void initState() {
        super.initState();
        WidgetsBinding.instance.addObserver(this);
}
    @override
    void dispose() {
        WidgetsBinding.instance.removeObserver(this);
        super.dispose();
}
    @override
    void didChangeAppLifecycleState(AppLifecycleState state) {
        super.didChangeAppLifecycleState(state);
        setState(() {
            _appLifecycleState = state;
           });  }

В этом примере мы используем didChangeAppLifecycleState метод для обновления состояния приложения. Также используем dispose метод для удаления observer’a, добавленного в initState метод. 

Другой способ отслеживания жизненного цикла: 

Пример кода:

late final AppLifecycleListener _listener;
    final ScrollController _scrollController = ScrollController();
    final List _states = [];
    late AppLifecycleState? _state;
    @override
    void initState() {
        super.initState();
        _state = SchedulerBinding.instance.lifecycleState;
        _listener = AppLifecycleListener(
            onShow: () => _handleTransition('show'),
            onResume: () => _handleTransition('resume'),
            onHide: () => _handleTransition('hide'),
            onInactive: () => _handleTransition('inactive'),
            onPause: () => _handleTransition('pause'),
            onDetach: () => _handleTransition('detach'),
            onRestart: () => _handleTransition('restart'),
            // This fires for each state change. Callbacks above fire only for
            // specific state transitions.
            onStateChange: _handleStateChange,
        );
        if (_state != null) {
            _states.add(_state!.name);
        }
    }
    @override
    void dispose() {
        _listener.dispose();
        super.dispose();
    }
    void _handleTransition(String name) {
        setState(() {
            _states.add(name);
        });
        // обработка каждого перехода
    }
    void _handleStateChange(AppLifecycleState state) {
        setState(() {
            _state = state;
        });
     }

Здесь инициализируем слушатель со всеми методами для обработки состояния приложения.

Отслеживание состояния жизненного цикла приложения – очень полезная “история”, так как задачи, связанные с жизненным циклом, могут быть самыми разными. Кто знает, с каким кейсом вам придется столкнуться на практике :)

Например, обработка пуш-уведомлений, обновление приложения или же конкретный кейс: когда добавляешь какие-либо товары в корзину и при открытии приложения из состояния, отличного от resumed, нужно провалидировать эту самую корзину (проверить товары на доступность).

Спасибо за внимание! Надеюсь, что статья была полезна. Подписывайтесь на Flutter.Много и делитесь своим опытом работы с жизненным циклом приложения в комментариях.

Другие статьи

Хотите связаться с владельцем
компании напрямую?
Дмитрий Тарасов
Дмитрий Тарасов
СЕО

НАПИСАТЬ