1. Введение в реактивное программирование и концепцию BLoC: Streams и Sinks
Введение в реактивное программирование и концепцию BLoC: Streams и Sinks
Добро пожаловать в курс «BLoC паттерн во Flutter: Управление состоянием». Это первая статья, в которой мы заложим фундамент для понимания одного из самых популярных и мощных архитектурных паттернов во Flutter — BLoC (Business Logic Component).
Прежде чем мы начнем писать код и создавать сложные приложения, нам необходимо разобраться с базовыми концепциями, на которых строится этот паттерн. Речь пойдет о реактивном программировании, потоках данных (Streams) и точках входа (Sinks).
Что такое реактивное программирование?
В традиционном императивном программировании мы привыкли явно приказывать компьютеру, что делать. Например: «возьми значение А, прибавь к нему Б и запиши результат в Ц». Если значение А изменится позже, значение Ц останется прежним, пока мы снова явно не выполним команду обновления.
Реактивное программирование — это парадигма, ориентированная на потоки данных и распространение изменений. В этой модели, если меняется А, то Ц обновляется автоматически, потому что между ними установлена постоянная связь.
Представьте себе таблицу в Excel. Если вы зададите ячейке формулу =A1 + B1, то при любом изменении чисел в ячейках A1 или B1, результат обновится мгновенно. Вам не нужно нажимать кнопку «Пересчитать». Это и есть простейший пример реактивного поведения.
Во Flutter, который является декларативным фреймворком, реактивный подход работает идеально. Интерфейс (UI) — это просто отражение текущего состояния приложения. Когда состояние меняется, интерфейс перерисовывается.
Анатомия потоков: Streams и Sinks
В языке Dart, на котором написан Flutter, основой реактивности является класс Stream. Чтобы понять BLoC, вы должны чувствовать себя уверенно при работе с потоками.
Давайте используем аналогию с водопроводом.
!Схема работы потока данных: вход через Sink, передача через Stream, получение через Subscription.
1. Stream (Поток)
Stream — это источник асинхронных событий данных. Представьте его как конвейерную ленту, по которой к вам едут коробки (данные). Вы не знаете точно, когда приедет следующая коробка, но вы стоите у ленты и ждете. Как только коробка появляется, вы на нее реагируете.
В Dart потоки могут передавать: * Данные (значения, объекты). * Ошибки (если что-то пошло не так). * Сигнал о завершении («Done», когда поток закрывается).
2. Sink (Воронка/Вход)
Если Stream — это то, откуда данные выходят, то Sink — это то, куда мы эти данные кладем. Это входное отверстие нашей трубы. В контексте BLoC мы будем добавлять события (нажатия кнопок, запросы) именно в Sink.
3. StreamController (Контроллер потока)
Чтобы управлять этой системой, в Dart существует класс StreamController. Это «пульт управления», который объединяет в себе и Stream, и Sink.
Рассмотрим простой пример на Dart:
Когда вы запустите этот код, вы увидите в консоли:
Типы потоков
Важно знать, что в Dart существует два основных типа потоков:
StreamController.broadcast().Концепция BLoC
Теперь, когда мы понимаем, как работают потоки, мы можем перейти к самому паттерну BLoC. Аббревиатура расшифровывается как Business Logic Component.
Этот паттерн был разработан Google для того, чтобы отделить бизнес-логику от пользовательского интерфейса (UI). Это критически важно для создания поддерживаемых, тестируемых и масштабируемых приложений.
Как это работает?
BLoC — это «черный ящик», который преобразует поток Событий (Events) в поток Состояний (States).
!Архитектура BLoC: UI отправляет события, BLoC обрабатывает их и возвращает состояния.
Sink.Stream.Почему именно потоки?
Использование Streams для BLoC не случайно. Потоки обеспечивают:
* Асинхронность: Интерфейс не зависает, пока выполняется тяжелая логика.
* Независимость: BLoC не знает, кто его слушает. Он просто «вещает» состояния. Это позволяет легко тестировать логику отдельно от верстки.
Простой пример счетчика на чистых потоках
Чтобы закрепить материал, давайте представим, как выглядел бы простейший BLoC для счетчика без использования сторонних библиотек.
В этом примере:
* UI будет вызывать bloc.incrementEventSink.add(null), когда пользователь нажмет кнопку.
* UI будет подписан на bloc.counterStream, чтобы обновлять цифру на экране.
Преимущества и недостатки подхода
Преимущества:
Недостатки:
К счастью, сообщество Flutter создало отличную библиотеку flutter_bloc, которая скрывает большую часть рутинной работы с StreamController и делает код чище. Именно её мы будем изучать в следующих уроках.
Заключение
Сегодня мы разобрали фундамент паттерна BLoC. Мы узнали, что: * Reactive Programming — это про автоматическое распространение изменений. * Stream — это конвейер, доставляющий данные асинхронно. * Sink — это вход для данных. * BLoC принимает события через Sink и отдает состояния через Stream.
В следующей статье мы перейдем от теории к практике и разберем, как устроена библиотека flutter_bloc, которая является стандартом индустрии для управления состоянием во Flutter.
Готовы проверить свои знания? Попробуйте ответить на вопросы ниже.