Архитектурный паттерн BLoC в проекте: используем легкий Cubit

Привет! Меня зовут Юрий Петров, я Flutter-разработчик в Friflex и автор канала о мобильной разработке Мобильный разработчик. В этой статье я хотел бы обсудить библиотеку flutter_bloc и одну из ее реализаций Cubit.

Для чего нужна библиотека flutter_bloc

Для начала, необходимо понять, что такое BLoC. Это архитектурный паттерн, акроним от Business Logic Component («компонент бизнес-логики»). C помощью этого паттерна мы можем легко отделить бизнес-логику приложения от пользовательского интерфейса.

Библиотека flutter_bloc и реализует этот паттерн и является менеджером состояния приложения. С помощью этой библиотеки мы можем:

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

У библиотеки flutter_bloc есть две реализации: Bloc и Cubit.

Для того, чтобы изменить состояние в реализации Bloc, нам необходимо отправить специальный event.

Изменение состояния в Bloc

Cubit же немного отличается тем, что events отсутствует, и напрямую мы обращаемся в Сubit, который в свою очередь генерирует (emit) новое состояние.

Изменение состояния в Cubit

Многие считают, что Cubit – это урезанная версия Bloc. Но это не так. Cubit – довольно мощный и гибкий инструмент, который может решать широкий спектр задач.

Возможности Cubit: пример авторизации в приложении

И для того, чтобы продемонстрировать возможности Cubit, попробуем рассмотреть простой пример регистрации пользователя в приложении. Если копнуть глубже – обнаружим, что необходимо авторизовать пользователя после успешной регистрации, то есть изменить глобальный AppState нашего приложения.

Как это можно реализовать? Для начала нужно понять, что такое AppState. AppState – это глобальное состояние нашего приложения. В целом, состояний может быть два – авторизован пользователь в приложении или нет. От этого состояния зависит многое, например, как будут вести себя другие блоки, какие виджеты будут нарисованы на экране и так далее. На примере приложения AliExpress, видно как меняется интерфейс при изменении состояния приложения «Авторизован/Не авторизован».

Для решения задачи регистрации будем использовать три Cubit: AuthCubit, RegistrationCubit, UserCubit.

Согласно этой схеме, мы видим, что при успешной регистрации RegistrationCubit получает АuthData, в которой хранится apiToken. Эти данные мы передаем в AuthCubit, который меняет своё состояние на «Авторизован». Далее мы видим, что у нас есть UserCubit, который подписан с помощью Stream Subscription на AuthCubit. И в случае изменения состояния в AuthCubit, UserCubit обращается к репозиторию и получает данные о пользователе.

И сразу встает вопрос: как пересоздавать интерфейс при изменении состояния AuthCubit? Есть несколько решений. Первое и самое простое – в методе build() мы будем проверять с помощью условного оператора If, в каком состоянии AuthCubit. Но есть и более гибкое решение.

Для этого мы создадим обертку AuthBuilder и в зависимости от состояния AuthCubit будем создавать нужный виджет. В этом случае не нужно проверять, в каком состоянии AuthCubit, так как AuthCubitBuilder сам везде перестроит интерфейс.

В коде это реализовано так

Таким образом мы перестраиваем интерфейс.

Данные Сubit

AuthCubit отвечает за глобальное состояние нашего приложения. В данном случае его задача – изменить свое состояние, авторизован пользователь или нет. Код данного Cubit очень простой. У него есть два метода: onAuthorized() и logOut(). Метод onAuthorized() принимает AuthData (модель хранит apiToken для валидации запросов в api). В нашем случае, при регистрации мы будем получать и записывать apiToken в эту модель. Далее мы изменяем состояние нашего cubit из UnAuthorizedState в AuthorizedState. А метод logOut(), просто переводит состояние Cubit в UnAuthorizedState.

Задача RegistrationCubit – провести успешную регистрацию и вызвать у AuthCubit метод onAuthorized и передать AuthData.

И теперь самое интересное: как уведомить UserCubit, что AuthСubit изменил состояние? Для решения этой задачи мы реализуем механизм подписок с помощью StreamSubsription. В конструкторе подписываемся на изменения AuthСubit, и как только поменялось состояние, мы получаем данные о пользователе. Тут обязательно необходимо закрыть нашу подписку в методе close().

Полный код по решению этих задач можно посмотреть на GitHub

И видео моего выступления на митапе Flutter в большом проекте

0
6 комментариев
Написать комментарий...
Сергей Ковзов

Смотрел видео доклад по этой теме от автора. Отличный материал! Автору большое спасибо за статью!

Ответить
Развернуть ветку
Игорь Кравченко

Честно говоря ваш подход очень похож на event, обычно emit делают на простых свойствах или моделях(ИМХО), не делая нормальные state. 
Я не поклонник cubit, на мой взгляд он не совсем универсален и дает меньшее разделение логики и отрисовки.
Свой подход и опыт я описал в https://vas3k.club/post/10567/
Вам советую обратить внимание на https://marketplace.visualstudio.com/items?itemName=gornivv.vscode-flutter-files - позволяет сократить работу с шаблонами для flutter_bloc , шаблонов под cubit из коробки нет, но есть возможность сделать свои.

Ответить
Развернуть ветку
Yura Petrov

У вас очень интересная статья👍. Но если вы пройдёте на мой гитхаб, то увидите,что я реализовал стейты разными способами. Просто в статье я не акцентировал внимание на этом. 

Ответить
Развернуть ветку
Yuri Trenin

Спасибо, очень актуально

Ответить
Развернуть ветку
Andrey Gordeev

Давно использую BLoC и кубиты в частности. Очень удобно.

Ответить
Развернуть ветку
Yura Petrov

Благодарю за отзыв!

Ответить
Развернуть ветку
3 комментария
Раскрывать всегда