BlaC is a direct descendant of flutterbloc. The name is not a coincidence: _Business Logic Components
is the Flutter pattern, and BlaC carries the same core idea — a class owns a slice of logic and emits
state — from Dart into TypeScript. If you have shipped flutter_bloc apps, most of the mental model
travels straight across. What changes is idiomatic Dart vs idiomatic TypeScript, and the React binding
layer.
flutter_bloc ships two classes: Cubit (methods you call) and Bloc (events you dispatch, handlers you
register). BlaC only has Cubit. If you used flutter’s Bloc class, translate each on<Event> handler
to a method:
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
In flutter_bloc you place a BlocProvider in the widget tree so descendants can look up the Cubit via
context. BlaC has no equivalent provider. The registry is a global singleton keyed on the class itself.
Two calls to useBloc(CounterCubit) always return the same instance, regardless of where in the React
tree they sit.
For scoped instances — an editor with per-document state — make the scoping value part of args
instead of using a wrapping provider:
Each docId gets an independent EditorCubit. When all components using that docId unmount,
the instance is disposed automatically (same ref-counting lifecycle as flutter_bloc’s BlocProvidercreate + close).
The select option replaces buildWhen (re-render when the returned array changes). Status side-effects
go in a plain useEffect. For global, non-React listeners, use watch:
flutter_bloc commonly uses plain Dart primitives as state (Cubit<int>, Cubit<bool>) or sealed
classes. BlaC state is always an object literal in practice — TypeScript works best with typed record
shapes, and patch only makes sense on an object:
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
For discriminated-union state (the Dart sealed-class pattern), use a TypeScript union type. The view
switches on state.status and TypeScript narrows each branch — same ergonomics as Dart when:
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
The persist plugin saves and restores state via IndexedDB. For React Native, swap the storage adapter
(the plugin ships an interface; pass an AsyncStorage-backed adapter). See Persistence.
If you used Cubit in Flutter (not the full Bloc event layer), migrating to BlaC is mostly syntax
translation. If you used Bloc events, collapse each on<Event> handler into a method.