Хроники управления состоянием Flutter 10.

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

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

Здесь я предполагаю, что у вас есть предварительные знания о провайдерах (не слишком обязательно), построении контекста и уведомителях, таких как ChangeNotifiers и StateNotifiers. А если нет, пожалуйста, прочтите эту прекрасную статью, написанную вашим покорным слугой.

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

Я действительно скажу вам, чтобы вы заботились.

Почему?

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

Так что, если у вас есть приложение, сильно зависящее от потоков, или вам нужно управление «подобное потоку», рассмотрите возможность использования блока.

Это еще не все, не так ли?

Конечно, нет.

На самом деле Блок нам помогает

  1. Отделите бизнес-логику от представления и получите один источник достоверности для состояний и средство связи между логикой и представлением. Да, я знаю, что большинство решений для управления состоянием делают это, но стоит отметить, что это делает и блок.
  2. Блок делает изменения состояния предсказуемыми, а также позволяет легко воспроизводить предыдущие состояния и отслеживать изменения состояния, поскольку представляет собой поток значений состояния. Звучит круто, верно? Реализация чего-то вроде отмены и повтора становится легкой.
  3. Это облегчает повторное использование кода в вашем приложении и за его пределами, поскольку код Bloc — это чистый код дротика, который может быть гибким и повторно используемым между угловым дротиком и флаттером и всеми разновидностями внешнего интерфейса дротика.
  4. Реактивное программирование в dart (RxDart) довольно мощное, и тот факт, что блок построен поверх него, означает, что вы можете использовать возможности RxDart, если это необходимо, но если нет, блок предоставляет достаточно простых абстракций, которые отлично подходят для простых решений. . Стоит отметить, что тот факт, что блок построен поверх RxDart и т. д., не делает его тяжелым или громоздким, в конце концов, RxDart — это просто Dart.

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

Отлично, теперь, когда мы позаботились о блоке😅, давайте посмотрим, как его использовать.

Для начала у блока есть две разные абстракции, cubit и bloc (которые расширяют класс BlocBase).

Cubit — это простой блок case, компонент потока, который использует функции для передачи взаимодействий от виджетов с cubit, и поток, который потребляют виджеты, для которого выдаются изменения состояния. (на самом деле речь идет не только о виджетах как источнике взаимодействия, но и о том, что кубиты могут предоставлять функции, используемые для запуска изменений состояния и т. д.).

По структуре Cubit очень похож на StateNotifiers, что делает его приемлемым для тех, кто приходит от Provider (фон Riverpod).

Давайте рассмотрим простой пример с Cubit

Для начала установим блок,

Здесь есть две проблемы: блок и флаттер-блок. Bloc является ядром пакета bloc и содержит основные строительные блоки, такие как Blocs и Cubits, и не зависит от флаттера (может использоваться любой платформой dart). Flutter_bloc — это пакет, содержащий элементы, которые вы будете использовать при создании пользовательских интерфейсов, таких как BlocProvider и BlocBuilder, которые зависят от флаттера.

Вы можете использовать функцию добавления зависимостей в коде vs и найти flutter_bloc и bloc или другим способом, мы также можем запустить flutter pub add flutter_bloc и flutter pub add bloc, чтобы добавить два пакеты.

Затем они будут добавлены в ваш pubspec.yaml.

Теперь, когда у нас это работает, давайте попробуем реализовать простой генератор случайных чисел, который возвращает число в диапазоне от 1 до 6, скажем, низкоуровневый бросок кости.

Итак, мы начинаем с создания локтя для обработки этого

Cubit — это просто класс, который расширяется (наследуется) от BlocBase, и из-за своего определения ему всегда требуется начальное состояние. Если мы не указываем тип состояния, автоматически предполагается, что состояние имеет динамический тип.

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

Идея такова: у нас есть синий кубик (на самом деле это просто синий квадрат :-)), который при нажатии показывает нам случайную сторону кубика (отображает любое число в диапазоне от 1 до 6). ), изначально перед любым кликом мы хотим предложить пользователю щелкнуть. Для начала, как и в случае с inheritedWidgets и Providers, чтобы иметь возможность использовать любой созданный нами кубит, нам нужно внедрить его в дерево виджетов в точке, которая будет доступна для виджета, который мы предоставляем. В нашем случае мы внедряем его в виджет MyApp, используя виджет из библиотеки flutter_bloc под названием BlocProvider.

BlocProvider Принимает функцию [Create], которая отвечает за создание [Bloc] или [Cubit] и [child], который будет иметь доступ к экземпляру через BlocProvider.of(context). Он используется в качестве виджета внедрения зависимостей (DI), так что один экземпляр [Bloc] или [Cubit] может быть предоставлен нескольким виджетам в поддереве.

Таким образом, создание и предоставление нашего Cubit в виджете MyApp означает, что каждый виджет под ним будет иметь доступ к Cubit.

После этого приступаем к использованию нашего локтя. Мы можем использовать его либо с помощью виджета BlocBuilder как такового

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

Но почему они отличаются, спросите вы?

  • Это основное ограничение для нас, разработчиков флаттера, на производительность наших приложений, чтобы убедиться, что мы используем минимальное количество перестроений для наших виджетов, поэтому возможность ограничить перестроения только необходимыми виджетами очень поможет в производительность. Поэтому, если у вас есть автономный виджет, который полностью зависит от состояния, предоставляемого Cubit, и поэтому всегда будет перестраиваться при изменении состояния, то использование context.watch — это то, что вам нужно. Но если у вас есть более крупный виджет, в котором только определенная часть виджета зависит от Cubit/блока, уместно ограничить перестроение только этим виджетом с помощью BlocBuilder.
  • BlocBuilder создан специально для управления жизненным циклом блока и поэтому очень подходит для использования в дереве виджетов, за исключением случаев, когда вы хотите использовать состояние или кубит/блок в функции, например, в onTap, где методы расширения затем пригодится, или если конкретный виджет, который нуждается в перестроении с областью действия, зависит от более чем 1 кубита/блока, и в этом случае вы что-то используете.

давайте вернемся к делу, не так ли? 😅

Итак, у нас есть кубит, используемый в нашем виджете, но если мы осознаем, что у нас нет возможности изменить значение, так что же нам делать?

В нашем локте мы хотим иметь возможность создавать новое состояние, к счастью для нас, есть открытая переменная состояния, которую мы можем использовать, к сожалению, мы не можем просто присваивать новые значения состояния переменной состояния, потому что это открытое переменная состояния на самом деле является геттером под капотом, что означает, что мы можем только прочитать ее значение, а не напрямую устанавливать новое. Это имеет смысл, потому что мы хотим, чтобы всякий раз, когда мы устанавливаем новое состояние, мы хотели, чтобы оно уведомляло все области, использующие состояние, о том, что оно было обновлено, в случае необходимости перестроения, а затем предоставляло новое обновленное значение для тех, кто использовал ChangeNotifiers , вы бы поняли, что для этого мы обновляем состояние, а затем вызываем функцию «notifyListeners», которая сделает эту работу за нас. Вместо этого библиотека блоков обрабатывает это для нас и предоставляет нам метод с именем emit, этот метод emit делает именно то, что он говорит, выдает новое состояние всем тем, кто его слушает, и ничего не делает, если выданное состояние совпадает с предыдущее состояние, это помогает нам, по сути, предотвратить ненужные пересборки, когда нет видимых изменений.

А затем мы обновляем наш метод onTap, чтобы он вызывал метод rollDice при касании как таковой.

Итак, мы закончили, мы изучили класс Cubit и познакомились с flutter_bloc. Вы можете найти полный рабочий код ниже. В следующей статье мы подробно расскажем о классе Bloc, о том, что это за обновление по сравнению с Cubit, и как максимально эффективно использовать пакет bloc.

https://github.com/kefeh/bloc_example/blob/main/lib/main.dart

Спасибо за прочтение.