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. При уништењу виџета, Bloc аутоматски затвара Stream кроз метод close(). Цурење реактивних претплата је немогуће — BlocProvider управља животним циклусом Bloc, повезујући га са рутом или страницом.
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 виџетима потомцима. При иницијализацији виџета, BlocProvider креира Bloc, а при уништењу — га аутоматски затвара кроз close(). BlocProvider може бити постављен на нивоу MaterialApp (глобални Bloc) или на нивоу конкретне руте (локални 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 — виџет који прецртава UI при сваком новом State. BlocListener — за споредне ефекте (једнократна обрада State без прецртавања UI): приказ SnackBar, навигација на други екран. BlocConsumer — комбинација Builder и Listener за случајеве када су потребни и прецртавање и споредни ефекат. BlocSelector — за селективно прецртавање само при промени одређеног поља State.
MultiBlocProvider — виџет за угнежђене BlocProvider без повећања нивоа угнежђења. Flutter апликација са 10-15 Bloc користи MultiBlocProvider на коренском нивоу за регистрацију свих Bloc доступних целој апликацији: AuthenticationBloc, CartBloc, SettingsBloc.
BLoC се тестира изоловано без Flutter виџета. Довољно је увести 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 тестира се са моковима кроз mocktail. Репозиторијум се мокује на нивоу апстракције, Bloc добија моковане зависности кроз конструктор. 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 за филтрирање прецртавања — виџет се ажурира само при промени одређеног услова. Close гарантује да неактивни Bloc не троше меморију. Према Flutter DevTools, BLoC додаје мање од 1% на величину пакета.
Миграција са Provider: BLoC лако коегзистира са Provider у истом пројекту. Постепена миграција: прво се замењују најсложенији Provider са Bloc, затим остали. BlocProvider је компатибилан са стаблом Provider: стари виџети могу користити 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 виџете. Концепт 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође