Build1 distinct publisher3 min readPublished
The bloc_signals release claims first-class peer interop in both directions, which would end the rewrite-or-adapter choice for Flutter teams. The import block shows what peer status costs, and the microtask boundary shows where it stops.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
The leg worth checking is Bloc to signal. The post describes Classic BLoC as heavily reliant on asynchronous Dart Stream microtasks [3], and Signals as synchronous with sub-microsecond reactivity [5]. If a bridge feeds a signal by subscribing to a bloc's stream, the value lands on the next microtask regardless of what the wrapper is called, and a `computed` built on top of it sits one microtask behind `emit` [1]. On a counter nobody will ever see that. On a gesture-driven readout at a high refresh rate, that lag is the reason you reached for signals in the first place. The useful version of the peer claim is narrower than the motto: a signal graph is as synchronous as its slowest input.
Which is why the missing benchmark table is the first thing I want filled in. The sub-microsecond figure is asserted without a measurement or a method anywhere in the post [15]. For it to transfer to your app, it would have to be measured on a release build on a physical device, and reported as rebuild counts and frame times for the same widget driven once through the stream path and once through the signal path. Dispatch cost in isolation is rarely what makes a Flutter frame late.
Adoption cost, on the other hand, is legible straight from the sample's import block. Seven imports [11]: one from the Flutter SDK, six pub packages, and three of those six are bloc_signals, bloc_signals_bloc and bloc_signals_riverpod [12]. Standing all three engines side by side means carrying three bridge packages, each of which tracks someone else's public API.
The Riverpod side of the sample is a plain `Notifier` subclass, with `build()` returning zero and an `increment()` that mutates state [13]. No generated provider appears in the shown code. That matters, because the post's own complaint about Riverpod is its drift toward mandatory code generation and the build_runner iteration tax [4]. A bridge that binds to the hand-written class instead of the generated one is the harder and more useful choice, and it is the part of this release I would trust first.
The published excerpt cuts off mid-declaration, at `final riverp` [14], so neither the 65-line count nor the disposal wiring can be checked against the text. The post says integration is lifecycle-managed [7], and lifecycle is exactly where the StreamController-plus-disposer pattern it replaces goes wrong [9], so that is the assertion carrying the most weight and showing the least code. The byline credits Randal L. Schwartz and a few million TPU cycles [16], which is a more candid statement of provenance than most release posts manage. My read, in my context: peer interop takes integration cost off the list of reasons to standardise on one library, and leaves the reason that one mental model for why a widget rebuilt is cheaper to keep in a team's head than two.
Ranked by verification strength, evidence, and original report placement.
A dev.to post by Randal L. Schwartz announces the release of bloc_signals_bloc and a major update to bloc_signals_riverpod.
The post characterises Classic BLoC as strict and heavily reliant on asynchronous Dart Stream microtasks.
The post characterises Riverpod as offering compile-time safety and declarative dependency graph plumbing while steering increasingly toward mandatory code generation and a build_runner iteration tax.
The post characterises Signals as offering raw sub-microsecond synchronous reactivity and fine-grained UI rebuilding.
The sample's import block lists package:flutter/material.dart, package:flutter_riverpod/flutter_riverpod.dart, package:bloc/bloc.dart, package:bloc_signals/bloc_signals.dart, package:bloc_signals_bloc/bloc_signals_bloc.dart, package:bloc_signals_riverpod/bloc_signals_riverpod.dart and package:signals_flutter/signals_flutter.dart.
In the sample, the Riverpod participant is a hand-written class RiverpodCounter extending Notifier<int>, with build() returning 0 and increment() incrementing state; no generated provider appears in the shown code.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 29, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
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
build
One non-ASCII letter broke four toolchains: path checks belong in CI, not folklore1 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.
One self-published post; the code is the only witness
Everything verifiable in this story is printed on the page rather than confirmed off it: the seven imports, the hand-written Notifier, the two toBlocSignal() calls. Everything that would decide whether the seam is worth taking — timing, disposal, the closing lines of the example — is either asserted or missing, and the listing stops mid-widget before it can be counted.
Packages exist; no user visible but the author
A release date and three package names are the whole of it. No download count, no app shipping the seam, no issue thread, no team reporting a migration — the only event we can point to is the announcement itself, written by the person who published the packages.
Zero-penalty language, zero measurements
'Sub-microsecond', 'zero microtask drag', 'zero microtask penalties' — and then, four paragraphs earlier, the same post concedes that Classic BLoC rides asynchronous Dart Stream microtasks. Both cannot be true of the bloc leg in the example, which reaches the computed total through exactly that Stream. The engineering underneath is modest and real: two adapter calls that spare you a StreamController and a disposer. The terminal-with-golden-sunbeams framing is doing considerably more work than the code.
The maintainer writing his own launch copy
This is a release note wearing an essay's clothes: the author ships the packages, characterises the competing libraries himself, and publishes on a platform with no editor between draft and post. The byline credits 'a few million TPU cycles', so part of the enthusiasm is machine-generated by design — which explains the metaphor density but does nothing to make the rivals' weaknesses independently established.
Firm on what the code shows, blind past it
We can be quite sure of the two things that matter for a decision — what you must import, and where synchrony breaks — because both are legible in the listing and in Dart's Stream semantics. Beyond that we are reading a single self-published page with its ending clipped, so any judgement about stability, uptake or real latency would be invention.