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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Данные Сubit

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

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

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

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

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

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

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

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

3030
6 комментариев

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

2
Ответить

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

2
Ответить

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

Ответить

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

1
Ответить

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

1
Ответить

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

Ответить