Build1 distinct publisher3 min readPublished
Dart allows one superclass, so giving a TextEditingController its own state container meant hand-writing a forwarding wrapper. Version 1.2.0 replaces that with two mixins that add nine names to the host class and leave teardown unexplained.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
The wrapper cost was never the typing. A proxy class holding an internal `_cubit` has two copies of state that must agree, and the post is candid about the upkeep: method forwarding plus manual synchronisation [4]. The two-object variant is worse in practice because the halves live in different places. The widget holds a `TextEditingController`, something else holds the `SearchCubit`, and the join between them is a listener added in `initState` and a pair of disposals you have to remember [12].
The mixin deletes the second object. You write `class UserProfileRepository extends BaseRepository with CubitSignalMixin<UserProfileState>`, call `initCubitSignal(initialState: const UserProfileInitial())` in the constructor body, and `emit()` is then a method on the repository itself [10]. `CubitSignalMixin` implements `BlocSignalBase<StateType>`, so the host class is the container rather than a shell in front of one [7]. Add `BlocSignalMixin` and you also get `on<SubmitOrder>` with `transformer: droppable()`, which the author says discards duplicate taps while a submit is in flight [11]. The object that owns the text now owns the concurrency policy too. That is good factoring, and it is the part of this release I would actually adopt.
Two things in the write-up are assertions rather than evidence. The first is the description of the result as a "0ms reactive" container [6]. There is no workload, no baseline and no timing method anywhere in the text we have [14]. Read charitably, 0ms describes synchronous notification on `emit` rather than a measured duration, but the post does not say that, and a latency figure with no method attached transfers to your app only by coincidence.
The second is lifecycle. `CubitSignalMixin` contributes `close()` among its six members [8]. The classes this feature exists to serve are `TextEditingController`, `ChangeNotifier` and `AnimationController` subclasses [3], and those are torn down with `dispose()`. The post's own complaint about the old approach was having to dispose both objects [12], and collapsing to one object halves the objects, not the teardown entry points. Whether `initCubitSignal` registers `close()` against the host's dispose path is not stated [15]. That is the first thing I would read the source for, before the benchmark that is not there.
Now the arithmetic behind "zero namespace pollution" [6]. `CubitSignalMixin` adds `state`, `stateValue`, `emit`, `equals`, `createEffect` and `close` [8]. `BlocSignalMixin` adds `on<E>()` and `add()` [9]. With `initCubitSignal` that is nine identifiers merged into a class whose supertype you very likely do not control [16]. "Lean API surface" is a fair description of nine names; it is not the same statement as no collisions. `equals` is the one I would grep the base class for first.
The context that decides this: if you own the class and it extends nothing, the mixin buys you little over extending a Cubit directly. The narrow case is the real one. Your class already extends something you cannot change, such as an enterprise `BaseRepository<T>` or an `EntityStore` client [3], and the alternative on the table was refactoring a base class and breaking third-party contracts [4]. Against that, nine names and an undocumented teardown interaction is a cheap trade. Worth noting the debounced-search example, the case the post builds toward, is truncated before the wiring appears [17], so the headline demo is still unverified from the text alone.
Ranked by verification strength, evidence, and original report placement.
The post shows the blocked declaration 'class SearchController extends TextEditingController, CubitSignal<SearchState>' as impossible in Dart because multiple inheritance is forbidden.
The post lists classes that already sit in a hierarchy: a search field controller extending Flutter's TextEditingController (itself extending ValueNotifier<TextEditingValue>), a view controller extending ChangeNotifier or AnimationController, and a domain repository extending an enterprise BaseRepository<T>, EntityStore, or microservices client.
The post names three historical fallbacks: a wrapper/proxy class holding an internal _cubit reference requiring tedious method forwarding and manual synchronisation; duplicate controller lifecycles managing two separate objects in the widget tree; and refactoring existing base classes, often breaking third-party library contracts or enterprise architectures.
bloc_signals 1.2.0 introduces CubitSignalMixin and BlocSignalMixin.
CubitSignalMixin implements BlocSignalBase<StateType>, and adoption is to mix it in and invoke initCubitSignal(initialState: ...) in the constructor.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 30, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Three bridge packages buy a seam between BLoC, Riverpod and synchronous signals1 distinct publisher
build
The duplicate snackbar is a modelling error, and the fix belongs in the state layer1 distinct publisher
build
bloc_signals 1.1.0 converts a Future into a state container in one call1 distinct publisher
build
Split Flutter CI from CD, or pay macOS rates on every pull request1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Concrete code, single interested witness
The mechanics are specific enough to type into an editor: a capability table, initCubitSignal in three constructors, droppable() on order submission, restartable() on search. That specificity is real evidence about what the API looks like. What is missing is anyone other than the author — no package listing, no changelog, no build by a team that does not ship bloc_signals — and the one performance word in the whole post, '0ms', arrives with no number attached.
A version number, nothing behind it
One thing has demonstrably happened: 1.2.0 exists and contains two mixins. Nobody outside the project is shown using them — no installs, no shipped app, no issue thread from someone who mixed CubitSignalMixin into a class that already had a close() of its own. A release is a starting gun, not a result.
Demolition language, undocumented foundations
'That single inheritance wall has been completely demolished' and 'zero namespace pollution' are absolutes the post never tests — by our own count nine identifiers land on the host class, which is modest but is not zero, and '0ms' is an adjective doing a benchmark's job. The overstatement is not all on dev.to's side: we said the release left teardown unexplained, and the search controller closes its signals in dispose() right there in the sample.
Maintainer announcing his own minor version
Author and beneficiary are the same party: the post says 'We have introduced', and its venue is a developer blog where the return is installs and mindshare. That alignment does not make the API description wrong — release notes are usually the most accurate account of code that exists — but it explains why the framing runs to demolition metaphors and why no one in this reporting weighs the costs of pouring a state container into a class that already has a lifecycle.
One self-interested document, read once too quickly
Two things hold this down. The record is a single post by the package's own team, so any error in it propagates undetected. And our first pass through it was partly wrong about the search example, which is a useful reminder of how little redundancy a one-source story has. The shape of the API we can state with some assurance; the latency claim and the ergonomics claim wait on someone who does not maintain bloc_signals.