tezvyn:

Classic BLoC with Streams and Sinks

AI-drafted, machine-checkedintermediate

Classic BLoC separates UI from business logic by exposing inputs as Sinks and outputs as Streams. The widget pushes events into sinks, the BLoC processes them, and emits new state on streams that the UI rebuilds from, keeping logic testable and…

WHY IT EXISTS Classic BLoC emerged to solve a recurring Flutter problem: business logic tangled inside widgets is hard to test and reuse. Introduced by Google engineers, the pattern's goal was a clean boundary so the same logic could run across Flutter and AngularDart, with the UI reduced to a thin reactive shell.

THE MENTAL MODEL Think of a BLoC as a sealed box with two pipes. One pipe, the Sink, takes input events flowing in; the other, the Stream, carries output state flowing out. The widget never calls methods that mutate UI state directly. Instead it drops an event into the sink and later receives whatever new state the box chooses to emit. The box knows nothing about widgets, only data in and data out.

HOW IT WORKS Internally the BLoC holds StreamControllers. Its sink is the controller's sink, exposing add for incoming events; its stream is the controller's stream, exposing emitted state. The BLoC listens to its input stream, runs business logic when an event arrives, and adds the resulting state to the output controller. In the widget tree a StreamBuilder subscribes to the output stream and rebuilds on each new state, while gesture handlers call add on the input sink. The BLoC must be disposed to close its controllers and avoid leaks.

WHEN IT MATTERS It matters when you want logic decoupled from the framework and easily unit tested without pumping widgets. It shines for cross-platform sharing and for teams that prefer explicit reactive streams. In modern Flutter most teams reach for the flutter_bloc package or other state solutions, but understanding the raw stream-and-sink mechanics clarifies what those abstractions automate.

ONE CONCRETE EXAMPLE A counter BLoC exposes an incrementSink and a countStream. A button's onPressed calls incrementSink.add(()). The BLoC listens, increments an internal value, and adds the new count to the count controller. A StreamBuilder wrapping the Text widget receives the new count and rebuilds, showing the updated number, all without the widget ever holding mutable counter state.

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.