<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Randal L. Schwartz</title>
    <description>The latest articles on DEV Community by Randal L. Schwartz (@randalschwartz).</description>
    <link>https://dev.to/randalschwartz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F121966%2Fa587f86d-abcb-484e-9a8a-09213b6a54b3.jpg</url>
      <title>DEV Community: Randal L. Schwartz</title>
      <link>https://dev.to/randalschwartz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/randalschwartz"/>
    <language>en</language>
    <item>
      <title>How We Achieved Full BLoC API &amp; Protocol Parity in BlocSignal 0.2.0</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Tue, 21 Jul 2026 14:04:54 +0000</pubDate>
      <link>https://dev.to/randalschwartz/how-we-achieved-full-bloc-api-protocol-parity-in-blocsignal-020-mdp</link>
      <guid>https://dev.to/randalschwartz/how-we-achieved-full-bloc-api-protocol-parity-in-blocsignal-020-mdp</guid>
      <description>&lt;p&gt;The BLoC (Business Logic Component) pattern is one of the most reliable and trusted state management architectures in the Flutter ecosystem. By separating event inputs from state outputs, it provides outstanding predictability, trace-ability, and testability.&lt;/p&gt;

&lt;p&gt;However, classic BLoC is built entirely on asynchronous Dart &lt;code&gt;Stream&lt;/code&gt;s. While powerful, streams run on the microtask queue, which introduces frame-level delays and can lead to transient UI glitches or coordination issues when chaining multiple reactive nodes.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/strong&gt;—a state management library that bridges the structural discipline of BLoC with the synchronous, glitch-free, fine-grained rebuild performance of &lt;strong&gt;reactive signals&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;0.2.0 release&lt;/strong&gt;, we set out to achieve complete class and protocol parity with classic BLoC (&lt;code&gt;package:bloc&lt;/code&gt; and &lt;code&gt;package:flutter_bloc&lt;/code&gt;), making it seamless to migrate or pair-program using familiar paradigms. &lt;/p&gt;

&lt;p&gt;Here is how we did it, including how we solved complex element-caching leaks and the few necessary exceptions we made due to the nature of signals.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Core Observability: Changes, Transitions, and Observers
&lt;/h2&gt;

&lt;p&gt;Classic BLoC provides a robust global &lt;code&gt;BlocObserver&lt;/code&gt; that tracks the lifecycle, events, transitions, and errors of every bloc in the app. In &lt;code&gt;BlocSignal 0.2.0&lt;/code&gt;, we implemented the exact same observability protocol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The Change class tracks state mutations (used by both Blocs and Cubits)&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;currentState:&lt;/span&gt; &lt;span class="n"&gt;oldState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;nextState:&lt;/span&gt; &lt;span class="n"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The Transition class tracks event-triggered updates (used by Blocs)&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;currentState:&lt;/span&gt; &lt;span class="n"&gt;oldState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;event:&lt;/span&gt; &lt;span class="n"&gt;IncrementEvent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nl"&gt;nextState:&lt;/span&gt; &lt;span class="n"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Global Interception
&lt;/h3&gt;

&lt;p&gt;You can now subclass &lt;code&gt;BlocSignalObserver&lt;/code&gt; and hook into every lifecycle phase across your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyObserver&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignalObserver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BlocSignalBase&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Created: &lt;/span&gt;&lt;span class="si"&gt;${bloc.runtimeType}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BlocSignalBase&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Change&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Change in &lt;/span&gt;&lt;span class="si"&gt;${bloc.runtimeType}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;$change&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BlocSignalBase&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Legacy support&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BlocSignalBase&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Closed: &lt;/span&gt;&lt;span class="si"&gt;${bloc.runtimeType}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Local Overrides
&lt;/h3&gt;

&lt;p&gt;We also aligned subclass method signatures so you can override &lt;code&gt;onChange(Change)&lt;/code&gt; and &lt;code&gt;onTransition(Transition)&lt;/code&gt; directly in your custom blocs to execute local side-effects, logging, or analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterBloc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CounterBloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;initialState:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Keep observer updated&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Local change detected: &lt;/span&gt;&lt;span class="si"&gt;$change&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Flutter Bindings Parity
&lt;/h2&gt;

&lt;p&gt;To bring &lt;code&gt;bloc_signals_flutter&lt;/code&gt; to full parity, we overhauled the widget lifecycle behaviors to match &lt;code&gt;flutter_bloc&lt;/code&gt; 1:1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy Provider Initialization
&lt;/h3&gt;

&lt;p&gt;In previous versions, &lt;code&gt;BlocSignalProvider&lt;/code&gt; eagerly created the BLoC on mount. In &lt;code&gt;0.2.0&lt;/code&gt;, we aligned with classic BLoC by defaulting to &lt;code&gt;lazy: true&lt;/code&gt;. The BLoC's &lt;code&gt;create&lt;/code&gt; factory is deferred until the first time a descendant widget looks up the bloc (via &lt;code&gt;context.read()&lt;/code&gt;, &lt;code&gt;context.watch()&lt;/code&gt;, or builders).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BlocSignalProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;create:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HeavyLoadingBloc&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nl"&gt;lazy:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Deferred until accessed&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyWidget&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditional Listening (&lt;code&gt;listenWhen&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;We added the &lt;code&gt;listenWhen&lt;/code&gt; filter parameter to &lt;code&gt;BlocSignalListener&lt;/code&gt; and &lt;code&gt;BlocSignalConsumer&lt;/code&gt;. This allows developers to conditionally filter listener triggers based on the &lt;code&gt;previous&lt;/code&gt; and &lt;code&gt;current&lt;/code&gt; states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BlocSignalListener&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterBloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;listenWhen:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEven&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;listener:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Only runs when the count changes to an even number&lt;/span&gt;
    &lt;span class="n"&gt;showCelebrationDialog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CounterView&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also implemented &lt;strong&gt;&lt;code&gt;MultiBlocSignalListener&lt;/code&gt;&lt;/strong&gt;, letting you chain multiple independent listeners linearly to keep your widget trees flat and readable.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Solving the &lt;code&gt;context.select&lt;/code&gt; Memory Leak (Under the Hood)
&lt;/h2&gt;

&lt;p&gt;Implementing &lt;code&gt;context.select&amp;lt;T, R&amp;gt;(selector)&lt;/code&gt; with reactive signals was our greatest technical challenge. &lt;/p&gt;

&lt;p&gt;In classic BLoC, &lt;code&gt;context.select&lt;/code&gt; listens to a stream and rebuilds the calling widget element &lt;em&gt;only&lt;/em&gt; when the selected sub-state changes. With signals, we wanted to use a &lt;code&gt;Computed&lt;/code&gt; signal to wrap the selector callback and rebuild the calling &lt;code&gt;Element&lt;/code&gt; whenever the computed signal value changed.&lt;/p&gt;

&lt;p&gt;To make this O(1), we used an &lt;code&gt;Expando&lt;/code&gt; keyed on the calling &lt;code&gt;Element&lt;/code&gt; to store and reuse &lt;code&gt;_SelectSubscription&lt;/code&gt; objects across builds.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Bug: Strong Reference Cycle Leak
&lt;/h3&gt;

&lt;p&gt;An &lt;code&gt;Expando&lt;/code&gt; uses weak keys, meaning entries are automatically garbage-collected when the key (&lt;code&gt;Element&lt;/code&gt;) is no longer referenced. &lt;/p&gt;

&lt;p&gt;However, our initial implementation of the &lt;code&gt;effect&lt;/code&gt; callback captured a strong reference to the &lt;code&gt;Element&lt;/code&gt; to trigger &lt;code&gt;element.markNeedsBuild()&lt;/code&gt;. Because the &lt;code&gt;effect&lt;/code&gt; was reachable from the subscription stored in the &lt;code&gt;Expando&lt;/code&gt;, this created a strong reference cycle:&lt;br&gt;
&lt;code&gt;Element (Key) -&amp;gt; SelectorState -&amp;gt; Subscription -&amp;gt; Effect -&amp;gt; Element (Key)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This cycle prevented the &lt;code&gt;Element&lt;/code&gt; key from ever being garbage-collected, creating a massive memory leak!&lt;/p&gt;
&lt;h3&gt;
  
  
  The Fix: &lt;code&gt;WeakReference&lt;/code&gt; to the Rescue
&lt;/h3&gt;

&lt;p&gt;To break this cycle, we wrapped the &lt;code&gt;Element&lt;/code&gt; in a &lt;code&gt;WeakReference&lt;/code&gt; inside our subscription model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_SelectSubscription&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignalBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;_SelectSubscription&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt; &lt;span class="kt"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;Element&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;  &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_bloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_elementRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WeakReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_computed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_bloc&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;_selectedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_computed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;_dispose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_computed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;_selectedValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_selectedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_elementRef&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;markNeedsBuild&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the closure only holds a weak reference to the element. Once the widget unmounts, the &lt;code&gt;Element&lt;/code&gt; key is naturally garbage-collected, which automatically evicts the subscription from the &lt;code&gt;Expando&lt;/code&gt; and cleans up the reactive effect!&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Necessary Exceptions (Because of Streams)
&lt;/h2&gt;

&lt;p&gt;While we achieved API parity, there are a few features from classic BLoC that &lt;strong&gt;cannot&lt;/strong&gt; be replicated directly due to the underlying shift from Streams to Signals. &lt;/p&gt;

&lt;p&gt;Here is what is missing, and how to achieve the same result using signals:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Custom &lt;code&gt;EventTransformer&lt;/code&gt;s
&lt;/h3&gt;

&lt;p&gt;Classic BLoC lets you supply custom event transformers (like &lt;code&gt;concurrent&lt;/code&gt;, &lt;code&gt;sequential&lt;/code&gt;, or &lt;code&gt;restartable&lt;/code&gt; from &lt;code&gt;package:bloc_concurrency&lt;/code&gt;) to control how events queue up. Because &lt;code&gt;BlocSignal&lt;/code&gt; executes event handlers synchronously upon dispatch, there is no event queue stream.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;droppable (Throttle) Equivalent&lt;/strong&gt;: If you want to drop events while an async action is running, check your loading state synchronously:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FetchPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stateValue&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;PageLoadInProgress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Drop event&lt;/span&gt;
    &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PageLoadInProgress&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;restartable (SwitchMap) Equivalent&lt;/strong&gt;: If you want to cancel the current request when a new event arrives, track a &lt;code&gt;CancelableOperation&lt;/code&gt; and cancel it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DetailBloc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DetailEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DetailState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;CancelableOperation&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_activeOperation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FetchDetails&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_activeOperation&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Cancel active operation&lt;/span&gt;
      &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DetailLoadInProgress&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CancelableOperation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromFuture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;_activeOperation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCanceled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DetailLoadSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Debouncing Events
&lt;/h3&gt;

&lt;p&gt;In classic BLoC, you might debounce event dispatching (e.g. search box text entry). In &lt;code&gt;BlocSignal&lt;/code&gt;, you can achieve this by managing a simple &lt;code&gt;Timer&lt;/code&gt; in your bloc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SearchBloc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SearchEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SearchState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_debounceTimer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;SearchTextChanged&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_debounceTimer&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;_debounceTimer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_performSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Bonus: Automated Migrations with AI Coding Skills
&lt;/h2&gt;

&lt;p&gt;We know that refactoring state management across a large codebase can be daunting. To solve this, the &lt;code&gt;bloc_signals&lt;/code&gt; repository exposes a built-in &lt;strong&gt;AI Coding Skill&lt;/strong&gt; under &lt;a href="https://github.com/RandalSchwartz/BlocSignal/blob/main/skills/bloc-signals/migration.md" rel="noopener noreferrer"&gt;&lt;code&gt;skills/bloc-signals/migration.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This markdown document is structured specifically for LLMs and AI coding assistants (like Google Antigravity, Cursor, or Copilot). By pointing your AI agent to this skill, it can automatically rewrite and migrate your legacy stream-based BLoC widgets and state classes to reactive signals with high precision, eliminating almost all of the manual migration friction!&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;BlocSignal 0.2.0&lt;/code&gt;, you no longer have to choose between the architectural predictability of BLoC and the raw performance of reactive signals. By achieving class and protocol parity, you get the best of both worlds: O(1) state selections, synchronous propagation, full observer tracing, and zero frame-level rebuild delays.&lt;/p&gt;

&lt;p&gt;Check out the packages today on pub.dev:&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://pub.dev/packages/bloc_signals" rel="noopener noreferrer"&gt;bloc_signals&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://pub.dev/packages/bloc_signals_flutter" rel="noopener noreferrer"&gt;bloc_signals_flutter&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>statemanagement</category>
      <category>signals</category>
    </item>
    <item>
      <title>BLoC meets Signals: How to Pitch BlocSignal to Your Dev Leads</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Mon, 20 Jul 2026 16:12:04 +0000</pubDate>
      <link>https://dev.to/randalschwartz/bloc-meets-signals-how-to-pitch-blocsignal-to-your-dev-leads-3m98</link>
      <guid>https://dev.to/randalschwartz/bloc-meets-signals-how-to-pitch-blocsignal-to-your-dev-leads-3m98</guid>
      <description>&lt;p&gt;Flutter state management is a battleground. Every few seasons, a new paradigm emerges, claiming to solve all our development woes. We’ve seen standard &lt;code&gt;setState&lt;/code&gt;, scoped model, Provider, BLoC, MobX, and Riverpod. &lt;/p&gt;

&lt;p&gt;Recently, &lt;strong&gt;Signals&lt;/strong&gt; (derived from the Preact architecture) has taken the frontend world by storm. It offers synchronous, fine-grained, and glitch-free reactivity. But raw signals can feel too chaotic for large teams, leading developers to modify state directly from the UI without any structure.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;BlocSignal&lt;/strong&gt;—a framework that bridges the architectural discipline of the BLoC pattern (Events in, States out) with the extreme performance and synchronous reactivity of Signals. &lt;/p&gt;

&lt;p&gt;But when you bring a new library to an architectural review board, you will face pushback. Here is how to pitch &lt;strong&gt;BlocSignal&lt;/strong&gt; and answer the 5 toughest questions your dev leads and reviewers will throw at you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Objection 1: "It's brand new! Why should we adopt an untested library?"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Critic
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"We can't afford to run our production apps on a brand-new, untested state-management library. What if it gets abandoned or introduces critical bugs?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Response
&lt;/h3&gt;

&lt;p&gt;BlocSignal is a thin, architectural bridge linking two of the most mature, popular, and battle-tested paradigms in Dart and Flutter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The BLoC Pattern&lt;/strong&gt;: Originally designed by Google and popularized by Felix Angelov, the BLoC model (Events, States, Cubits, and standard unidirectional data flow) has been the gold standard for enterprise Flutter for years. Developers use the exact same mental models they already know.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preact-style Signals&lt;/strong&gt;: The reactivity engine is powered by Rody Davis's &lt;code&gt;signals&lt;/code&gt; library (version 7). Signals are the industry-standard state primitive of modern web frameworks like SolidJS, Qwik, Angular, Vue, and Preact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100% Test Coverage&lt;/strong&gt;: The &lt;code&gt;bloc_signals&lt;/code&gt; and &lt;code&gt;bloc_signals_flutter&lt;/code&gt; packages maintain &lt;strong&gt;100% line coverage&lt;/strong&gt; for all codebase paths. The API is locked down with strict automated unit and integration tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;: You aren't adopting a "from scratch" state engine; you are adopting a structured interface wrapper over highly optimized, production-ready primitives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Objection 2: "We tried BLoC and moved to Riverpod. Why go back?"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Critic
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"We migrated from BLoC to Riverpod because we hated streams, microtask queues, visual rendering lag, and boilerplate. Why would we go back to BLoC?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Response
&lt;/h3&gt;

&lt;p&gt;BlocSignal solves the exact pain points that drove teams to Riverpod in the first place, while avoiding Riverpod's own drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous Updates&lt;/strong&gt;: Unlike classic BLoC which propagates state changes asynchronously via Dart &lt;code&gt;Stream&lt;/code&gt; microtask queues, BlocSignal propagates updates &lt;strong&gt;synchronously&lt;/strong&gt; in the same frame. Just like Riverpod, calling &lt;code&gt;emit()&lt;/code&gt; instantly notifies downstream listeners.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Widget-Tree Scoped Lifecycle&lt;/strong&gt;: Riverpod relies on global provider scopes. Scoping state to specific routes, dialogs, or subtrees requires complex caching or parameter bindings. BlocSignal restores Flutter's native dependency injection model using &lt;code&gt;BlocSignalProvider&lt;/code&gt; (built on &lt;code&gt;InheritedWidget&lt;/code&gt;). When a widget unmounts, the bloc's resources are automatically closed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Stream Boilerplate&lt;/strong&gt;: Instead of using complex RxDart stream operations to derive state, you can declare lightweight, cached, and automatically updated derived values using &lt;code&gt;computed()&lt;/code&gt; signals inside your constructor:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;ReadOnlySignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;isCartEmpty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stateValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic De-duplication&lt;/strong&gt;: Classic BLoC triggers transition listeners for every emit. BlocSignal de-duplicates equal states (&lt;code&gt;==&lt;/code&gt;) out of the box, preventing redundant widget rebuilds.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Objection 3: "It's not enterprise battle-ready."
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Critic
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Enterprise apps need logging, metrics, error tracking, and strict guidelines. Lightweight state libraries are toys that don't scale."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Response
&lt;/h3&gt;

&lt;p&gt;BlocSignal was built from day one with enterprise constraints in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Native OpenTelemetry (OTel) Observability&lt;/strong&gt;: In enterprise environments, tracing requests across systems is vital. BlocSignal includes &lt;code&gt;OtelBlocSignalObserver&lt;/code&gt; which:

&lt;ul&gt;
&lt;li&gt;Automatically tracks event transitions, asynchronous executions, and errors.&lt;/li&gt;
&lt;li&gt;Maps them directly to OpenTelemetry Spans.&lt;/li&gt;
&lt;li&gt;Implements bounded span caching (capped at 1000 items, evicting oldest) to guarantee zero memory leaks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure Safety&lt;/strong&gt;: Event registry zones capture handler exceptions without disrupting the state container lifecycle, routing them dynamically to telemetry observers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Quality Rules&lt;/strong&gt;: The project enforces strict &lt;code&gt;very_good_analysis&lt;/code&gt; lints (the enterprise-standard ruleset in Flutter) and 100% doc comment coverage.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Objection 4: "Why not just use raw Signals? What is the value of wrapping them in BLoC?"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Critic
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"If we already have access to the &lt;code&gt;signals&lt;/code&gt; package, why introduce BLoC? Can't we just use raw signal objects inside plain controller classes?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Response
&lt;/h3&gt;

&lt;p&gt;Raw signals provide fine-grained reactivity but lack architectural discipline. In large team environments, raw mutable signals can lead to spaghetti state management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enforced Unidirectional Data Flow&lt;/strong&gt;: With raw signals, widgets can read and write to signals directly from anywhere (e.g. &lt;code&gt;counter.value++&lt;/code&gt; inside a button). BlocSignal enforces a clear event-driven boundary: widgets can only dispatch events via &lt;code&gt;.add()&lt;/code&gt;, and the container's state is exposed as a &lt;code&gt;ReadonlySignal&lt;/code&gt;, meaning &lt;strong&gt;only the bloc can call &lt;code&gt;emit()&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traceability&lt;/strong&gt;: Because mutations are routed through discrete events, we can observe, trace, and debug transitions globally (e.g., via OpenTelemetry), which is impossible with raw, ad-hoc signal changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uniformity&lt;/strong&gt;: It provides a unified pattern for teams. Instead of every developer building custom notifier/controller classes in their own style, everyone inherits from &lt;code&gt;BlocSignal&lt;/code&gt; or &lt;code&gt;Cubit&lt;/code&gt; patterns, ensuring consistency across feature modules.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Objection 5: "How do we coordinate overlapping asynchronous events? (Loss of &lt;code&gt;bloc_concurrency&lt;/code&gt;)"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Critic
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Standard BLoC uses stream concurrency transformers like &lt;code&gt;restartable()&lt;/code&gt; or &lt;code&gt;droppable()&lt;/code&gt; to cancel or queue overlapping events of the same type. How do we achieve these essential concurrent behaviors without streams?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Response
&lt;/h3&gt;

&lt;p&gt;While classic BLoC utilizes RxDart stream transformers, BlocSignal allows teams to orchestrate concurrency through native Dart async patterns, signal effects, or light-weight guard clauses.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent Execution by Default&lt;/strong&gt;: If multiple handlers are invoked, they execute concurrently using &lt;code&gt;Future.wait&lt;/code&gt; orchestration under the hood, ensuring no tasks are blocked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple Declarative Guards&lt;/strong&gt;: Implementing concurrency strategies like &lt;code&gt;droppable&lt;/code&gt; is trivial. For instance, we can check a loading state before executing the business logic:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;   &lt;span class="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stateValue&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;DataLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Droppable pattern&lt;/span&gt;
     &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataLoading&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
     &lt;span class="c1"&gt;// Fetch data...&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cancellation (Restartable Pattern)&lt;/strong&gt;: To replicate a &lt;code&gt;restartable()&lt;/code&gt; pattern (where a new event cancels the previous active task), keep a reference to a cancellation token or active future inside the bloc:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;   &lt;span class="n"&gt;CancelableOperation&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_activeOperation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SearchQueryChanged&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_activeOperation&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="n"&gt;_activeOperation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CancelableOperation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromFuture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

     &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_activeOperation&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Feature Comparison Matrix
&lt;/h2&gt;

&lt;p&gt;The table below summarizes the key trade-offs between the three main approaches:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Classic BLoC (Streams)&lt;/th&gt;
&lt;th&gt;Riverpod (v2/v3)&lt;/th&gt;
&lt;th&gt;BlocSignal (Signals v7)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Propagation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Asynchronous (Microtask)&lt;/td&gt;
&lt;td&gt;Synchronous&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Synchronous&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Widget Tree Scoped&lt;/td&gt;
&lt;td&gt;Global Scopes / &lt;code&gt;ref&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Widget Tree Scoped&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State De-duplication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual (&lt;code&gt;distinct()&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Automatic (&lt;code&gt;==&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Derived State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complex (RxDart / Streams)&lt;/td&gt;
&lt;td&gt;Declarative (&lt;code&gt;ref.watch&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Declarative (&lt;code&gt;computed&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifecycle Hook&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual / &lt;code&gt;BlocProvider&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Auto-Dispose (&lt;code&gt;ref.keepAlive&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Automatic (Unmount / &lt;code&gt;close&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;BlocObserver&lt;/code&gt; (Local)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ProviderObserver&lt;/code&gt; (Local)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;OTel Tracing (&lt;code&gt;OtelObserver&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (Stream queues)&lt;/td&gt;
&lt;td&gt;Medium (&lt;code&gt;ProviderContainer&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Low (Synchronous Asserts)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;BlocSignal is the evolution of two great state management philosophies. It offers the &lt;strong&gt;safety and scale&lt;/strong&gt; of BLoC combined with the &lt;strong&gt;developer experience and speed&lt;/strong&gt; of Signals. &lt;/p&gt;

&lt;p&gt;Have you tried bridging these patterns in your Flutter apps? Let me know in the comments below!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>statemanagement</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Introducing BlocSignal: Unidirectional Data Flow Meets Reactive Signals</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Sun, 19 Jul 2026 20:42:47 +0000</pubDate>
      <link>https://dev.to/randalschwartz/introducing-blocsignal-unidirectional-data-flow-meets-reactive-signals-48b2</link>
      <guid>https://dev.to/randalschwartz/introducing-blocsignal-unidirectional-data-flow-meets-reactive-signals-48b2</guid>
      <description>&lt;p&gt;State management in Flutter has always been a tale of two philosophies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Unidirectional, Event-Driven Architecture (BLoC/Cubit):&lt;/strong&gt; Exceptional for structuring business logic in large-scale applications. It provides clear separations, standard developer workflows, and centralized transition observers. But it runs on Dart &lt;code&gt;Stream&lt;/code&gt;s, introducing asynchronous microtask scheduling and event-propagation lag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Grained Reactive Primitives (Signals):&lt;/strong&gt; Blazing fast, synchronous, and boilerplate-free. However, without standard guardrails, pure signal codebases can sometimes feel like the "Wild West" when multiple developers try to coordinate complex side effects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What if you could have the architectural safety and predictability of BLoC combined with the performance and reactive simplicity of Signals?&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BlocSignal&lt;/code&gt; bridges these two paradigms, bringing synchronous, glitch-free propagation, automatic state de-duplication, and advanced signals reactivity to the classic BLoC pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Why BlocSignal?
&lt;/h2&gt;

&lt;p&gt;In classic BLoC, every emitted state is dispatched asynchronously on a stream. If you emit identical states consecutively, downstream widgets rebuild anyway unless you configure distinct filters.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BlocSignal&lt;/code&gt; rewrites the engine under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous Propagation:&lt;/strong&gt; State updates via &lt;code&gt;emit(newState)&lt;/code&gt; propagate instantly to downstream calculations and widget rebuilds in the exact same frame. No more waiting on microtasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic De-duplication:&lt;/strong&gt; States are automatically compared using &lt;code&gt;==&lt;/code&gt; equality. If a state doesn't change, downstream effects and builders are skipped entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative Reactivity:&lt;/strong&gt; The bloc exposes its state as a reactive &lt;code&gt;ReadonlySignal&amp;lt;State&amp;gt;&lt;/code&gt;. This allows you to compose derived state properties using &lt;code&gt;computed(...)&lt;/code&gt; and register side effects via &lt;code&gt;effect(...)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ The Event Handler Design: Two Flavors of Safety
&lt;/h2&gt;

&lt;p&gt;When managing events in standard BLoC, you typically register handlers in the constructor scope using &lt;code&gt;.on&amp;lt;Event&amp;gt;&lt;/code&gt;. In &lt;code&gt;BlocSignal&lt;/code&gt;, we support both this dynamic registration pattern and compile-time safe pattern matching.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧬 Flavor 1: Dynamic Registration with &lt;code&gt;.on&amp;lt;Event&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is the familiar, standard BLoC approach. It is dynamic and easy to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals/bloc_signals.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Increment&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CounterEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Decrement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CounterEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterBloc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CounterBloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;initialState:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Dynamic runtime registration&lt;/span&gt;
    &lt;span class="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stateValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Decrement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stateValue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;When to use:&lt;/em&gt; Perfect for simple event topologies or when mapping a small set of actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛡️ Flavor 2: Compile-Time Safe Exhaustiveness with Switch Expressions
&lt;/h3&gt;

&lt;p&gt;Because &lt;code&gt;.on&amp;lt;T&amp;gt;&lt;/code&gt; registers handlers dynamically at runtime, the Dart compiler cannot verify if you've handled every subclass of a &lt;code&gt;sealed&lt;/code&gt; class. &lt;/p&gt;

&lt;p&gt;To achieve &lt;strong&gt;100% compile-time safety&lt;/strong&gt;, you can override &lt;code&gt;onEvent(event)&lt;/code&gt; and use a Dart &lt;strong&gt;switch expression&lt;/strong&gt;. If you add a new event subclass and forget to handle it, your project will fail to compile, preventing runtime bugs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals/bloc_signals.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Increment&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CounterEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Decrement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CounterEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Reset&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CounterEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterBloc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CounterBloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;initialState:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;FutureOr&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CounterEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The compiler strictly enforces that all CounterEvent subclasses are handled&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;nextState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stateValue&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Decrement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stateValue&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Reset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nextState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;When to use:&lt;/em&gt; Production enterprise features with complex, evolving sealed event hierarchies.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Consuming State in Flutter
&lt;/h2&gt;

&lt;p&gt;Integrating &lt;code&gt;BlocSignal&lt;/code&gt; into your Flutter widgets is a breeze. It uses familiar provider/builder APIs, but optimizes them using signal-based rebuilding.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Providing the State
&lt;/h3&gt;

&lt;p&gt;Inject your state container into the widget tree using &lt;code&gt;BlocSignalProvider&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BlocSignalProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterBloc&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;create:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CounterBloc&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CounterScreen&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note that &lt;code&gt;BlocSignal&lt;/code&gt; for Flutter doesn't use the &lt;code&gt;Provider&lt;/code&gt; package—we implement our own lightweight &lt;code&gt;InheritedWidget&lt;/code&gt; subclass, eliminating one more external dependency.)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Building the UI
&lt;/h3&gt;

&lt;p&gt;Rebuild sections of the UI using &lt;code&gt;BlocSignalBuilder&lt;/code&gt;. Because it reads the underlying signal value, it subscribes and unsubscribes from state changes automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BlocSignalBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterBloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;$count&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headlineMedium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Advanced Composition: Computed Signals &amp;amp; Effects
&lt;/h2&gt;

&lt;p&gt;Since the state is powered by standard Dart &lt;code&gt;signals&lt;/code&gt;, you can compose derived state effortlessly:&lt;/p&gt;

&lt;h3&gt;
  
  
  Inside the Constructor (Scoped)
&lt;/h3&gt;

&lt;p&gt;You can declare reactive signals properties directly inside the class constructor. They will automatically be disposed of when the bloc is closed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskBloc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TaskEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;ReadonlySignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;completedCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;TaskBloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;initialState:&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Automatically updates whenever the state changes&lt;/span&gt;
    &lt;span class="n"&gt;completedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stateValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Auto-disposed side effects&lt;/span&gt;
    &lt;span class="n"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Outstanding tasks: &lt;/span&gt;&lt;span class="si"&gt;${stateValue.length - completedCount()}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Outside the Constructor (Consumer)
&lt;/h3&gt;

&lt;p&gt;Consumers can subscribe to a bloc's reactive &lt;code&gt;state&lt;/code&gt; signal directly to create external computed chains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;taskBloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TaskBloc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// External computed signal&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;hasOutstandingTasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;taskBloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;any&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📈 Enterprise Observability
&lt;/h2&gt;

&lt;p&gt;In production environments, tracking state transitions is critical. &lt;code&gt;BlocSignal&lt;/code&gt; zones track transitions triggered by specific events. &lt;/p&gt;

&lt;p&gt;By registering the &lt;code&gt;OtelBlocSignalObserver&lt;/code&gt; (from &lt;code&gt;otel_bloc_signals&lt;/code&gt;), transitions, actions, and errors map directly to OpenTelemetry traces, ensuring you have absolute visibility into user flows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BlocSignalObserver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OtelBlocSignalObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;tracerProvider:&lt;/span&gt; &lt;span class="n"&gt;myTracerProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏁 Summary &amp;amp; Resources
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;BlocSignal&lt;/code&gt; takes the best parts of two worlds. It gives you the structured boundaries and event hierarchies of BLoC/Cubit, backed by the raw performance, synchronous execution, and declarative power of Signals.&lt;/p&gt;

&lt;p&gt;Ready to check it out? Explore the packages on Pub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;strong&gt;&lt;a href="https://pub.dev/packages/bloc_signals" rel="noopener noreferrer"&gt;bloc_signals&lt;/a&gt;&lt;/strong&gt; – Core state management container.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;&lt;a href="https://pub.dev/packages/bloc_signals_flutter" rel="noopener noreferrer"&gt;bloc_signals_flutter&lt;/a&gt;&lt;/strong&gt; – High-performance widgets and custom providers.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;&lt;a href="https://pub.dev/packages/otel_bloc_signals" rel="noopener noreferrer"&gt;otel_bloc_signals&lt;/a&gt;&lt;/strong&gt; – OpenTelemetry tracing and observability observer.&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;&lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>statemanagement</category>
      <category>blocsignal</category>
    </item>
  </channel>
</rss>
