BLoC — шта је то, Business Logic Component у Flutter

Аутор: IT Sectr Објављено: 2026-02-19 Време читања: 7 мин

BLoC (Business Logic Component) — образац управљања стањем за Flutter, представљен од стране Google 2018. године на DartConf. BLoC раздваја пословну логику од корисничког интерфејса кроз реактивне токове (Stream): UI шаље Event, BLoC га обрађује и враћа нови State кроз Stream. Према подацима pub.dev, пакет flutter_bloc је прикупио више од 11 хиљада лајкова и користи се у хиљадама Flutter апликација.

Главне тачке

  • Event — улазни сигнал који описује радњу: клик на дугме, учитавање података
  • State — излазно стање UI: подаци учитани, грешка, учитавање
  • Bloc — главна класа која прима Event и враћа State кроз Stream
  • Cubit — поједностављена верзија Bloc без Event, која директно позива функције
  • BlocProvider — Flutter виџет за убризгавање Bloc у стабло виџета

Шта је BLoC?

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

Архитектура BLoC се гради око три ентитета: Event (улаз), Bloc (обрађивач) и State (излаз). Widget шаље Event кроз метод add(). Bloc прима Event у методи mapEventToState или on<Event>, извршава пословну логику и емитује нови State кроз yield. Widget прима State кроз Stream и прецртава се.

Dart
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, повезујући га са рутом или страницом.

Bloc и Cubit: поређење

Cubit — поједностављена имплементација Bloc без Event, представљена у пакету flutter_bloc 6.0. Cubit декларише методе директно уместо Event класа: increment(), fetchData(). Унутрашње, Cubit користи исти механизам заснован на Stream, али скрива слој Event. Ово смањује boilerplate за 40-50% за једноставне сценарије.

КарактеристикаBlocCubit
Event класеОбавезнеНису потребне
BoilerplateВисокНизак
Праћење радњиКроз тип EventСамо име метода
Погодно заКомплексне сценаријеЈедноставна стања
АналитикаАутоматска кроз EventРучна

Када изабрати Cubit: стање са 2-3 варијанте (loading, loaded, error), једноставне форме, бројачи, UI стања (отворено/затворено). Када Bloc: сложена пословна логика са више радњи: завршетак поруџбине, ауторизација, синхронизација података. Bloc пружа детаљно праћење сваке радње кроз Event — сваки позив се бележи у BlocObserver.

BlocObserver — глобални посматрач који прати све Bloc и Cubit у апликацији. Омогућава бележење Event, State, грешака и прелаза. Довољно је повезати једну инстанцу: Bloc.observer = AppBlocObserver(), и целокупно праћење стања апликације је доступно централизовано.

BlocProvider и BlocBuilder

BlocProvider — InheritedWidget из flutter_bloc који обезбеђује Bloc виџетима потомцима. При иницијализацији виџета, BlocProvider креира Bloc, а при уништењу — га аутоматски затвара кроз close(). BlocProvider може бити постављен на нивоу MaterialApp (глобални Bloc) или на нивоу конкретне руте (локални Bloc).

Dart
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

BLoC се тестира изоловано без Flutter виџета. Довољно је увести Dart пакет flutter_test и пакет bloc_test. Тест сценарио: креирати Bloc, додати Event, проверити State. blocTest — алат који аутоматизује секвенцу: build → act → expect.

Dart
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 и привременим складиштем датотека.

BLoC у продукцији

Фасцикле и датотеке: типична структура 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 разликује од Provider у Flutter?

BLoC користи Event + Stream за изолацију пословне логике и строгу типизацију. Provider — омотач око InheritedWidget за једноставно убризгавање зависности и ChangeNotifier. BLoC је бољи за сложене сценарије са више стања, Provider — за локално UI стање. BLoC захтева више boilerplate, али пружа потпуну следљивост кроз Event.

Шта је Hydrated Bloc?

Hydrated Bloc — проширење из пакета hydrated_bloc које аутоматски чува последње State у локалном складишту (подразумевано Hive). При поновном покретању апликације, Bloc враћа сачувано стање уместо почетног. Ово решава проблем перзистентности без ручног позива чувања: пријава, корпа, подешавања се чувају аутоматски између сесија.

Како обрадити грешке у BLoC?

Грешка у BLoC се обрађује кроз try-catch унутар mapEventToState или on<Event>. При грешци, Bloc враћа State грешке: yield LoadError(error.message). На UI, BlocListener или BlocConsumer проверава State за тип грешке и приказује SnackBar или дијалог. BlocObserver глобално бележи све необрађене изузетке.

Може ли се BLoC користити са другим оквирима?

BLoC — образац специфичан за Flutter, јер користи Dart Stream и Flutter виџете. Концепт Event → Bloc → State може бити адаптиран за AngularDart и Server-side Dart, али главни екосистем (BlocProvider, BlocBuilder, BlocObserver) је везан за Flutter. За React Native користите Redux или MobX, за SwiftUI — Combine + MVVM.

Шта изабрати: Bloc или Cubit?

Cubit — за једноставна стања (бројач, toggle, форма са 2-3 поља). Bloc — за сложену логику (ток вести, завршетак поруџбине, ауторизација). Главно правило: ако је потребно праћење сваке радње (Event) за аналитику или отклањање грешака — Bloc. Ако су довољне методе које мењају стање — Cubit. Оба обрасца коегзистирају у истом пројекту.

Резиме

  • BLoC — образац управљања стањем Flutter кроз Event → Stream → State
  • Event — радња (клик, учитавање), State — реакција (подаци, грешка, учитавање)
  • Cubit — поједностављена верзија без Event, до 50% мање boilerplate
  • BlocProvider — убризгавање Bloc у стабло виџета са аутоматским close
  • BlocObserver — глобално праћење свих Bloc и Cubit у апликацији
  • Hydrated Bloc — аутоматска перзистентност стања кроз Hive
  • blocTest — алат за модуларно тестирање Bloc са изолацијом од Flutter

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође