Архитектурный паттерн BLoC в проекте: используем легкий Cubit
Привет! Меня зовут Юрий Петров, я Flutter-разработчик в Friflex и автор канала о мобильной разработке Мобильный разработчик. В этой статье я хотел бы обсудить библиотеку flutter_bloc и одну из ее реализаций Cubit.
Для чего нужна библиотека flutter_bloc
Для начала, необходимо понять, что такое BLoC. Это архитектурный паттерн, акроним от Business Logic Component («компонент бизнес-логики»). C помощью этого паттерна мы можем легко отделить бизнес-логику приложения от пользовательского интерфейса.
Библиотека flutter_bloc и реализует этот паттерн и является менеджером состояния приложения. С помощью этой библиотеки мы можем:
- точно понимать, в каком состоянии находится наше приложение в любой момент времени;
- легко тестировать;
- повторно использовать компоненты;
- разрабатывать быстрые и реактивные приложения.
Тут можно посмотреть официальную документацию библиотеки flutter_bloc.
У библиотеки flutter_bloc есть две реализации: Bloc и Cubit.
Для того, чтобы изменить состояние в реализации Bloc, нам необходимо отправить специальный event.
Cubit же немного отличается тем, что events отсутствует, и напрямую мы обращаемся в Сubit, который в свою очередь генерирует (emit) новое состояние.
Многие считают, что 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 в большом проекте ✅
Смотрел видео доклад по этой теме от автора. Отличный материал! Автору большое спасибо за статью!
Честно говоря ваш подход очень похож на event, обычно emit делают на простых свойствах или моделях(ИМХО), не делая нормальные state.
Я не поклонник cubit, на мой взгляд он не совсем универсален и дает меньшее разделение логики и отрисовки.
Свой подход и опыт я описал в https://vas3k.club/post/10567/
Вам советую обратить внимание на https://marketplace.visualstudio.com/items?itemName=gornivv.vscode-flutter-files - позволяет сократить работу с шаблонами для flutter_bloc , шаблонов под cubit из коробки нет, но есть возможность сделать свои.
У вас очень интересная статья👍. Но если вы пройдёте на мой гитхаб, то увидите,что я реализовал стейты разными способами. Просто в статье я не акцентировал внимание на этом.
Спасибо, очень актуально
Давно использую BLoC и кубиты в частности. Очень удобно.
Благодарю за отзыв!