BLoC (Business Logic Component) — модел за управление на състоянието за Flutter, представен от Google през 2018 г. на DartConf. BLoC разделя бизнес логиката от потребителския интерфейс чрез реактивни потоци (Stream): UI изпраща Event, BLoC го обработва и връща нов State чрез Stream. Според данните от pub.dev, пакетът flutter_bloc е събрал над 11 хиляди харесвания и се използва в хиляди Flutter приложения.
Основни точки
BLoC (Business Logic Component) — архитектурен модел за Flutter, при който бизнес логиката е изнесена в отделен клас, изолиран от UI. BLoC получава входни данни чрез поток от събития (Event) и произвежда изходни данни чрез поток от състояния (State). Презентационният слой (Widget) само се абонира за потока State и визуализира UI, без никога да изпълнява бизнес логика директно.
Концепцията на BLoC се основава на реактивното програмиране и модела Observer. Всеки компонент на BLoC е отделен модул с ясен договор: известни са наборът от Event (какво може да се случи) и наборът от State (какво може да се покаже). Разработчикът не може "случайно" да промени състоянието от UI — само чрез конкретен Event. Това прави кода предвидим и тестваем.
Според проучването Flutter Community 2025, BLoC заема второ място по популярност сред решенията за управление на състоянието във Flutter след Provider. Основни предимства: строга типизация, изолация на логиката, вградена поддръжка на Stream, богата екосистема от помощни средства (BlocProvider, BlocListener, BlocSelector).
Архитектурата на BLoC е изградена около три обекта: Event (вход), Bloc (обработчик) и State (изход). Widget изпраща Event чрез метода add(). Bloc приема Event в метода mapEventToState или on<Event>, изпълнява бизнес логика и произвежда нов State чрез yield. Widget получава State чрез Stream и се прерисува.
abstract class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0);
@override
Stream<int> mapEventToState(CounterEvent event) async* {
if (event is Increment) {
yield state + 1;
} else if (event is Decrement) {
yield state - 1;
}
}
}Безопасност на типовете: Bloc е параметризиран с два типа — Event и State. Компилаторът на Dart проверява, че Widget извиква само декларирани Event, а Bloc връща само декларирани State. Грешки по време на изпълнение от типа "Неизвестно действие" са изключени.
Close и Dispose: Bloc имплементира интерфейса Closeable. При унищожаване на widget-а, Bloc автоматично затваря Stream чрез метода close(). Изтичане на реактивни абонаменти е невъзможно — BlocProvider управлява жизнения цикъл на Bloc, свързвайки го с route или страница.
Cubit — опростена имплементация на Bloc без Event, представена в пакета flutter_bloc 6.0. Cubit декларира методи директно вместо класове Event: increment(), fetchData(). Вътрешно Cubit използва същия механизъм, базиран на Stream, но скрива слоя Event. Това намалява boilerplate с 40-50% за прости сценарии.
| Характеристика | Bloc | Cubit |
|---|---|---|
| Класове Event | Задължителни | Не са необходими |
| Boilerplate | Висок | Нисък |
| Проследяване на действия | Чрез тип Event | Само име на метод |
| Подходящ за | Сложни сценарии | Прости състояния |
| Аналитика | Автоматична чрез Event | Ръчна |
Кога да изберем Cubit: състояние с 2-3 варианта (loading, loaded, error), прости формуляри, броячи, UI състояния (отворено/затворено). Кога да изберем Bloc: сложна бизнес логика с множество действия: завършване на поръчка, авторизация, синхронизация на данни. Bloc предоставя детайлно проследяване на всяко действие чрез Event — всяко извикване се записва в BlocObserver.
BlocObserver — глобален наблюдател, който проследява всички Bloc и Cubit в приложението. Позволява записване на Event, State, грешки и преходи. Достатъчно е да свържете един екземпляр: Bloc.observer = AppBlocObserver(), и цялото проследяване на състоянието на приложението е достъпно централизирано.
BlocProvider — InheritedWidget от flutter_bloc, който предоставя Bloc на дъщерните widget-и. При инициализация на widget-а, BlocProvider създава Bloc, а при унищожаване — го затваря автоматично чрез close(). BlocProvider може да бъде поставен на ниво MaterialApp (глобален Bloc) или на ниво конкретен route (локален Bloc).
BlocProvider(
create: (context) => CounterBloc(),
child: Column(
children: [
BlocBuilder<CounterBloc, int>(
builder: (context, state) => Text('$state'),
),
ElevatedButton(
onPressed: () => context.read<CounterBloc>().add(Increment()),
child: Text('+'),
),
],
),
)BlocBuilder — widget, който преизгражда UI при всяко ново State. BlocListener — за странични ефекти (еднократна обработка на State без преизграждане на UI): показване на SnackBar, навигация към друг екран. BlocConsumer — комбинация от Builder и Listener за случаи, когато са необходими както преизграждане, така и страничен ефект. BlocSelector — за селективно преизграждане само при промяна на конкретно поле на State.
MultiBlocProvider — widget за вложени BlocProvider без увеличаване на нивото на влагане. Flutter приложение с 10-15 Bloc използва MultiBlocProvider на кореново ниво за регистрация на всички Bloc, достъпни в цялото приложение: AuthenticationBloc, CartBloc, SettingsBloc.
BLoC се тества изолирано без Flutter widget-и. Достатъчно е да импортирате Dart пакета flutter_test и пакета bloc_test. Тестов сценарий: създайте Bloc, добавете Event, проверете State. blocTest — инструмент, който автоматизира последователността: build → act → expect.
blocTest<CounterBloc, int>(
'emits [1] when Increment is added',
build: () => CounterBloc(),
act: (bloc) => bloc.add(Increment()),
expect: () => [1],
)Mocking: BLoC, който зависи от хранилище или API, се тества с mock-ове чрез mocktail. Хранилището се mock-ва на ниво абстракция, Bloc получава mock-нати зависимости чрез конструктора. Hydrated Bloc — разширение за автоматично запазване/възстановяване на състояние в локално хранилище. Тества се с HydratedBlocStorage и временно файлово хранилище.
Папки и файлове: типична структура на Flutter проект с BLoC: bloc/counter_bloc.dart, bloc/counter_event.dart, bloc/counter_state.dart. За 30+ екрана се препоръчва групиране по функционалност: features/auth/bloc/, features/cart/bloc/. Всеки Bloc — отделен файл, всеки Event и State — или в отделни файлове, или в един файл с Bloc.
Производителност: BLoC не създава допълнително натоварване върху празни Stream. BlocBuilder използва buildWhen за филтриране на преизграждания — widget-ът се актуализира само при промяна на конкретно условие. Close гарантира, че неактивните Bloc не консумират памет. Според Flutter DevTools, BLoC добавя по-малко от 1% към размера на пакета.
Миграция от Provider: BLoC лесно съществува съвместно с Provider в един проект. Поетапна миграция: първо най-сложните Provider се заменят с Bloc, след това останалите. BlocProvider е съвместим с дървото на Provider: старите widget-и могат да използват Provider, новите — BlocProvider, в едно и също приложение.
Често задавани въпроси
BLoC използва Event + Stream за изолация на бизнес логика и строга типизация. Provider — обвивка около InheritedWidget за просто инжектиране на зависимости и ChangeNotifier. BLoC е по-подходящ за сложни сценарии с множество състояния, Provider — за локално UI състояние. BLoC изисква повече boilerplate, но предоставя пълна проследимост чрез Event.
Hydrated Bloc — разширение от пакета hydrated_bloc, което автоматично запазва последното State в локално хранилище (по подразбиране Hive). При рестартиране на приложението, Bloc възстановява запазеното състояние вместо началното. Това решава проблема с персистентността без ръчно извикване на запазване: вход, количка, настройки се запазват автоматично между сесиите.
Грешка в BLoC се обработва чрез try-catch вътре в mapEventToState или on<Event>. При грешка, Bloc връща State на грешка: yield LoadError(error.message). На UI, BlocListener или BlocConsumer проверява State за типа на грешката и показва SnackBar или диалог. BlocObserver глобално записва всички необработени изключения.
BLoC е модел, специфичен за Flutter, тъй като използва Dart Stream и Flutter widget-и. Концепцията Event → Bloc → State може да бъде адаптирана за AngularDart и Server-side Dart, но основната екосистема (BlocProvider, BlocBuilder, BlocObserver) е свързана с Flutter. За React Native използвайте Redux или MobX, за SwiftUI — Combine + MVVM.
Cubit — за прости състояния (брояч, toggle, формуляр с 2-3 полета). Bloc — за сложна логика (новинарски поток, завършване на поръчка, авторизация). Основно правило: ако е необходимо проследяване на всяко действие (Event) за аналитика или отстраняване на грешки — Bloc. Ако са достатъчни методи, които променят състоянието — Cubit. И двата модела съществуват съвместно в един проект.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също