<?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: Md. Al-Amin</title>
    <description>The latest articles on DEV Community by Md. Al-Amin (@alaminkarno).</description>
    <link>https://dev.to/alaminkarno</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F954504%2F4b0a140b-315c-47db-9c85-1d6d6d5e5923.jpeg</url>
      <title>DEV Community: Md. Al-Amin</title>
      <link>https://dev.to/alaminkarno</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alaminkarno"/>
    <language>en</language>
    <item>
      <title>DDD (Domain-Driven Design) in Flutter: Too Much or Just Right?</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Wed, 23 Jul 2025 11:30:41 +0000</pubDate>
      <link>https://dev.to/alaminkarno/ddd-domain-driven-design-in-flutter-too-much-or-just-right-d1g</link>
      <guid>https://dev.to/alaminkarno/ddd-domain-driven-design-in-flutter-too-much-or-just-right-d1g</guid>
      <description>&lt;p&gt;When it comes to architecting Flutter apps especially large-scale one's developers often find themselves stuck between clean code principles and shipping fast. One of the most debated topics in this space is &lt;strong&gt;Domain-Driven Design (DDD)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some say it’s overkill for mobile apps. Others swear by it. So, what’s the truth?&lt;/p&gt;

&lt;p&gt;Let’s break it down.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Domain-Driven Design (DDD)?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DDD&lt;/strong&gt; is a software development philosophy introduced by Eric Evans. The core idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your code should reflect your business domain and logic first not frameworks or UI layers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It encourages separation between different parts of your app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain layer:&lt;/strong&gt; Business logic, rules, entities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application layer:&lt;/strong&gt; Use cases, orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure layer:&lt;/strong&gt; Frameworks, APIs, databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation layer:&lt;/strong&gt; UI (Flutter Widgets in our case)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DDD is about aligning &lt;strong&gt;code structure with real-world business models&lt;/strong&gt;. In large, evolving applications, this separation becomes critical.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem DDD Solves in Flutter
&lt;/h2&gt;

&lt;p&gt;In many Flutter apps, business logic lives inside widgets. It starts small:&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="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;formIsValid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;submitForm&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;Then the logic grows. You add validation, API calls, error handling, retries… and suddenly your widget becomes a monster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is where DDD shines.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By separating concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI stays UI&lt;/li&gt;
&lt;li&gt;Business logic lives in the domain&lt;/li&gt;
&lt;li&gt;Reusability, testability, and maintainability skyrocket&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Is DDD Overkill for Flutter?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When DDD Makes Sense:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your app is large or growing fast&lt;/li&gt;
&lt;li&gt;You have complex business rules&lt;/li&gt;
&lt;li&gt;Teams work on different features/modules&lt;/li&gt;
&lt;li&gt;You plan to reuse logic across platforms (Web, Mobile, Backend)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When It Might Be Too Much:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You’re building a small MVP or prototype&lt;/li&gt;
&lt;li&gt;Your app has minimal business rules&lt;/li&gt;
&lt;li&gt;You’re a solo developer with tight deadlines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In those cases, simpler architectures like MVVM or feature-first clean code might be enough.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;However, &lt;strong&gt;starting clean is easier than refactoring dirty code later&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What DDD Looks Like in Flutter
&lt;/h2&gt;

&lt;p&gt;Here’s a simplified structure using DDD in a &lt;code&gt;Login&lt;/code&gt; feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lib/
└── features/
    └── auth/
        ├── domain/
        │   ├── entities/
        │   ├── repositories/
        │   └── usecases/
        ├── application/
        │   └── services/
        ├── infrastructure/
        │   └── datasource/
        └── presentation/
            └── pages/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example: Use Case&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoginUseCase&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;LoginUseCase&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="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Either&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Failure&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;call&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;email&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;password&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;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;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&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;In your BLoC or Cubit&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;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;login&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;email&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;password&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;LoginLoading&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;result&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;loginUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;result&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&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;LoginError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;failure&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="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="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;LoginSuccess&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your widget now just listens to state changes — and doesn’t know how login works internally.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;A logistics app I worked on had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple user roles (Driver, Dispatcher, Admin)&lt;/li&gt;
&lt;li&gt;Complex order states and workflows&lt;/li&gt;
&lt;li&gt;Offline data sync and conflict resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By applying DDD:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each domain (Orders, User, Vehicles) had its own clear logic&lt;/li&gt;
&lt;li&gt;Testing became isolated and fast&lt;/li&gt;
&lt;li&gt;Features could be worked on independently by different devs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It saved us from chaos when the app scaled from 2 devs to 10+ contributors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for Using DDD in Flutter
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start small, scale wisely :&lt;/strong&gt; Don’t try to implement DDD in everything from Day 1. Pick a critical feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use packages like &lt;code&gt;get_it&lt;/code&gt;, &lt;code&gt;injectable&lt;/code&gt;, or &lt;code&gt;riverpod&lt;/code&gt; :&lt;/strong&gt; This help manage dependencies across layers without tight coupling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid premature abstraction :&lt;/strong&gt; Abstract only when business logic demands it not to “look clean.”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write tests per layer :&lt;/strong&gt; Unit tests for use cases, integration tests for repositories, widget tests for UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communicate with your team :&lt;/strong&gt; DDD is only powerful when your team understands the domain model.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DDD in Flutter isn’t overkill it’s a mindset.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You don’t need to adopt it religiously in every app. But for scalable, maintainable, and testable Flutter apps, DDD gives you a solid foundation.&lt;/p&gt;

&lt;p&gt;Start where it hurts the most. Refactor those bloated widgets. Move business logic to the domain. You’ll feel the difference.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you tried DDD in Flutter before? What worked? What didn’t? Let’s talk in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
    </item>
    <item>
      <title>How to Structure a Plugin-Friendly Flutter App</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Tue, 22 Jul 2025 15:38:43 +0000</pubDate>
      <link>https://dev.to/alaminkarno/how-to-structure-a-plugin-friendly-flutter-app-20k</link>
      <guid>https://dev.to/alaminkarno/how-to-structure-a-plugin-friendly-flutter-app-20k</guid>
      <description>&lt;p&gt;You’ve probably built a cool feature that you wished you could easily reuse in another app maybe a custom calendar, a payment module, or a chat UI.&lt;/p&gt;

&lt;p&gt;But when you try to extract it… you realize it’s deeply coupled to your app.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;plugin-friendly architecture&lt;/strong&gt; comes in. If you structure your Flutter app the right way from the start, you’ll be able to extract features into packages or plugins effortlessly saving time, avoiding rewrites, and even enabling contributions from others if you’re working in a team or open-source setting.&lt;/p&gt;

&lt;p&gt;Let’s explore &lt;strong&gt;how to design your Flutter app to be plugin-friendly&lt;/strong&gt; from day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Care About Plugin-Friendly Structure?
&lt;/h2&gt;

&lt;p&gt;Here’s why these matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy code reuse across apps&lt;/li&gt;
&lt;li&gt;Convert any feature into a plugin/package&lt;/li&gt;
&lt;li&gt;Better testing and maintainability&lt;/li&gt;
&lt;li&gt;Clear modular separation&lt;/li&gt;
&lt;li&gt;Ideal for enterprise apps and open-source projects&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Principles of Plugin-Friendly Architecture
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Feature Isolation :&lt;/strong&gt;
Each feature should be self-contained UI, logic, services, and dependencies all live inside its own folder or module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Coupling to App Layer :&lt;/strong&gt;
Features shouldn’t rely directly on things like &lt;code&gt;BuildContext&lt;/code&gt;, global variables, or shared themes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Interfaces and Abstractions :&lt;/strong&gt;
Define contracts for services (like &lt;code&gt;AuthService&lt;/code&gt;, &lt;code&gt;PaymentGateway&lt;/code&gt;) so they can be swapped or mocked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate Core from Integration :&lt;/strong&gt;
Keep business logic and UI reusable and isolate app-specific wiring (like routes, theme, or platform logic) in the app layer only.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Recommended Folder Structure
&lt;/h2&gt;

&lt;p&gt;Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lib/
  models/
  views/
  controllers/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lib/
  app/              ← main app configuration
  features/
    chat/
      data/
      domain/
      presentation/
    auth/
      data/
      domain/
      presentation/
  core/
    widgets/
    utils/
    services/
  plugins/          ← internal reusable packages &lt;span class="o"&gt;(&lt;/span&gt;optional&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each &lt;code&gt;feature/&lt;/code&gt; folder could be turned into a standalone package or plugin later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools &amp;amp; Techniques to Use
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;package:injectable&lt;/code&gt; + &lt;code&gt;get_it&lt;/code&gt; for DI
&lt;/h3&gt;

&lt;p&gt;Helps decouple features from their concrete dependencies. Each feature can declare what it needs and get it injected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leverage Dart’s &lt;code&gt;export&lt;/code&gt; Keyword
&lt;/h3&gt;

&lt;p&gt;Simplify imports when creating reusable packages.&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/auth.dart&lt;/span&gt;
&lt;span class="kn"&gt;export&lt;/span&gt; &lt;span class="s"&gt;'src/auth_service.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;export&lt;/span&gt; &lt;span class="s"&gt;'src/auth_screen.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Platform Channels Smartly
&lt;/h3&gt;

&lt;p&gt;When creating features that interact with native code (e.g., camera, sensors), isolate native platform logic inside the feature package and use a clean API for access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Converting a Feature into a Plugin
&lt;/h2&gt;

&lt;p&gt;Let’s say you’ve built a &lt;code&gt;step_counter&lt;/code&gt; feature in your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Extract the Feature
&lt;/h3&gt;

&lt;p&gt;Move it to a new package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;packages/
  step_counter/
    lib/
      step_counter.dart
      src/
        step_counter_service.dart
        widgets/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Expose a Clean API
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;step_counter.dart&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;library&lt;/span&gt; &lt;span class="n"&gt;step_counter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;export&lt;/span&gt; &lt;span class="s"&gt;'src/step_counter_service.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;export&lt;/span&gt; &lt;span class="s"&gt;'src/widgets/step_display.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Add to App as Dependency
&lt;/h3&gt;

&lt;p&gt;In your main app:&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;step_counter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../packages/step_counter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your feature is modular, testable, and reusable across projects!&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Let’s take the case of a &lt;strong&gt;pedometer app&lt;/strong&gt; you’re building for multiple client brands.&lt;/p&gt;

&lt;p&gt;If you structure the core walking logic, distance calculation, and sensor access into a plugin, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reuse it across different branded apps&lt;/li&gt;
&lt;li&gt;Customize UI in each app while keeping core logic consistent&lt;/li&gt;
&lt;li&gt;Maintain and test the logic in one place&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Best Practices Recap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Feature-first folder structure: Enables modular extraction&lt;/li&gt;
&lt;li&gt;Define interfaces in domain layer: Decouples dependencies&lt;/li&gt;
&lt;li&gt;Use dependency injection: Improves testability &amp;amp; flexibility&lt;/li&gt;
&lt;li&gt;Avoid app-level dependencies in feature code: Keeps features portable&lt;/li&gt;
&lt;li&gt;Expose minimal API in packages: Makes reuse easy&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building plugin-friendly Flutter apps doesn’t just help you write cleaner code it prepares you for &lt;strong&gt;scale&lt;/strong&gt;, &lt;strong&gt;reuse&lt;/strong&gt;, &lt;strong&gt;testing&lt;/strong&gt;, and &lt;strong&gt;team collaboration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whether you’re working on enterprise apps, client projects, or building your own Flutter plugin library this structure will set you up for long-term success.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In an upcoming post, I’ll show how to publish a custom Flutter plugin to &lt;code&gt;pub.dev&lt;/code&gt; — from structuring to versioning and publishing best practices.&lt;/p&gt;




&lt;p&gt;Want help structuring your next app or plugin?&lt;br&gt;
Let’s connect on &lt;a href="https://www.linkedin.com/in/alaminkarno/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/alamin_karno" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; I love talking architecture!&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>programming</category>
    </item>
    <item>
      <title>Scaling Flutter Apps with Feature-First Folder Structures</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Mon, 21 Jul 2025 11:30:11 +0000</pubDate>
      <link>https://dev.to/alaminkarno/scaling-flutter-apps-with-feature-first-folder-structures-547f</link>
      <guid>https://dev.to/alaminkarno/scaling-flutter-apps-with-feature-first-folder-structures-547f</guid>
      <description>&lt;p&gt;As Flutter apps grow, so does the complexity of maintaining them. One of the most overlooked reasons behind messy, hard-to-scale codebases? Poor &lt;strong&gt;project structure&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Many developers start with the default &lt;code&gt;lib/&lt;/code&gt; folder and keep adding files until it becomes a graveyard of widgets, services, and helpers scattered without context. Sound familiar?&lt;/p&gt;

&lt;p&gt;Let’s fix that by exploring one of the most effective strategies for scaling Flutter apps: &lt;strong&gt;Feature-First Folder Structure&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with Layer-Based Structure
&lt;/h2&gt;

&lt;p&gt;A typical beginner project might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lib/
├── models/
├── screens/
├── widgets/
├── services/
├── utils/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works okay at first, but as features grow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You start jumping across folders to understand a single flow.&lt;/li&gt;
&lt;li&gt;Business logic leaks into UI layers.&lt;/li&gt;
&lt;li&gt;Teams step on each other’s toes when working on the same layers.&lt;/li&gt;
&lt;li&gt;Code reuse becomes a mess.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Feature-First Approach: Why It Works
&lt;/h2&gt;

&lt;p&gt;Instead of organizing by type (widgets/models/services), a &lt;strong&gt;feature-first structure&lt;/strong&gt; organizes by &lt;em&gt;features or modules&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lib/
├── features/
│   ├── home/
│   │   ├── data/
│   │   ├── domain/
│   │   ├── presentation/
│   │   └── home_page.dart
│   ├── auth/
│   │   ├── data/
│   │   ├── domain/
│   │   ├── presentation/
│   │   └── login_page.dart
├── core/
│   ├── theme/
│   ├── router/
│   └── common_widgets/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each feature is isolated it contains its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Presentation/UI (widgets, screens)&lt;/li&gt;
&lt;li&gt;Domain (use cases, entities)&lt;/li&gt;
&lt;li&gt;Data (models, repositories, services)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This aligns beautifully with Clean Architecture, making your code &lt;strong&gt;modular&lt;/strong&gt;, &lt;strong&gt;testable&lt;/strong&gt;, and &lt;strong&gt;scalable&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benefits at Scale
&lt;/h2&gt;

&lt;p&gt;Here’s why this approach shines in large projects:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Isolation of Features
&lt;/h3&gt;

&lt;p&gt;Each feature is self-contained, so developers can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Work independently&lt;/li&gt;
&lt;li&gt;Test modules in isolation&lt;/li&gt;
&lt;li&gt;Reduce merge conflicts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Improved Code Navigation
&lt;/h3&gt;

&lt;p&gt;Want to update the “checkout” feature?&lt;br&gt;
Just go to &lt;code&gt;features/checkout/&lt;/code&gt; — all relevant logic is there.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Clean Onboarding
&lt;/h3&gt;

&lt;p&gt;New teammates can focus on just one folder to understand how a feature works, rather than jumping across layers.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Fewer Regressions
&lt;/h3&gt;

&lt;p&gt;Because of isolated dependencies, changes in one feature are less likely to break another.&lt;/p&gt;


&lt;h2&gt;
  
  
  Best Practices for Feature-First Projects
&lt;/h2&gt;

&lt;p&gt;Here’s how to get the most out of this structure:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Use Clear Naming
&lt;/h3&gt;

&lt;p&gt;Use consistent names like &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;domain&lt;/code&gt;, &lt;code&gt;presentation&lt;/code&gt; under each feature. Avoid vague names like &lt;code&gt;helpers&lt;/code&gt; or &lt;code&gt;misc&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Leverage Dependency Injection
&lt;/h3&gt;

&lt;p&gt;Use tools like &lt;code&gt;get_it&lt;/code&gt; and &lt;code&gt;injectable&lt;/code&gt; to inject services per feature, avoiding tight coupling.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Modularize Your Features
&lt;/h3&gt;

&lt;p&gt;In very large apps, split each feature into Dart packages using &lt;strong&gt;Flutter’s package structure&lt;/strong&gt; or &lt;strong&gt;melos&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Extract Shared Logic to &lt;code&gt;core/&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Keep global theming, constants, and reusable widgets in &lt;code&gt;core/&lt;/code&gt; to avoid duplication.&lt;/p&gt;


&lt;h2&gt;
  
  
  Real-World Example: eCommerce App
&lt;/h2&gt;

&lt;p&gt;Let’s say you’re building a large eCommerce app. A feature-first structure could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lib/
├── features/
│   ├── products/
│   │   ├── presentation/
│   │   ├── domain/
│   │   ├── data/
│   ├── cart/
│   ├── checkout/
│   ├── auth/
├── core/
│   ├── theme/
│   ├── localization/
│   ├── common_widgets/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each team (e.g. cart team, auth team) can build and maintain their own feature independently. This scales beautifully when your app has multiple developers or contributors.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Not to Use Feature-First?
&lt;/h2&gt;

&lt;p&gt;Feature-first structures are great when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your app has 3+ features&lt;/li&gt;
&lt;li&gt;You’re working in a team&lt;/li&gt;
&lt;li&gt;You’re planning for long-term maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for tiny apps or MVPs, the added structure may feel like overhead. Start simple evolve as you grow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Your app’s folder structure is more than just an aesthetic choice it directly affects how fast you move, how easily you are onboard new devs, and how confidently you ship features.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;feature-first folder structure&lt;/strong&gt; brings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modularity&lt;/li&gt;
&lt;li&gt;Testability&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So next time you start a new Flutter project (or refactor an old one), consider organizing by features, not just by file type. Your future self (and your team) will thank you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want to Go Deeper?
&lt;/h2&gt;

&lt;p&gt;Check out these related topics to dive deeper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean Architecture in Flutter&lt;/li&gt;
&lt;li&gt;Dependency Injection with &lt;code&gt;get_it&lt;/code&gt; &amp;amp; &lt;code&gt;injectable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Managing state per feature with &lt;code&gt;Bloc&lt;/code&gt; or &lt;code&gt;Riverpod&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>dart</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Effective Layered Architecture in Large Flutter Apps</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Sun, 20 Jul 2025 12:43:07 +0000</pubDate>
      <link>https://dev.to/alaminkarno/effective-layered-architecture-in-large-flutter-apps-2n48</link>
      <guid>https://dev.to/alaminkarno/effective-layered-architecture-in-large-flutter-apps-2n48</guid>
      <description>&lt;p&gt;When your Flutter app grows beyond a few screens and features, your codebase starts to feel like a jungle. You find business logic inside widgets, API calls scattered across UI files, and trying to test anything becomes a nightmare. Sound familiar?&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Layered Architecture&lt;/strong&gt; comes in and not just for the sake of organization, but for maintainability, scalability, and testability.&lt;/p&gt;

&lt;p&gt;Let’s break this down from core principles to real-world implementation with no fluff.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Layered Architecture?
&lt;/h2&gt;

&lt;p&gt;At its core, layered architecture separates your app’s responsibilities into &lt;strong&gt;distinct layers&lt;/strong&gt;. Each layer focuses on a specific role and communicates only with its adjacent layers.&lt;/p&gt;

&lt;p&gt;Here’s the classic 3+&lt;strong&gt;1&lt;/strong&gt; layered structure in a Flutter context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Presentation Layer&lt;/li&gt;
&lt;li&gt;Application Layer (Use Cases)&lt;/li&gt;
&lt;li&gt;Domain Layer (Business Rules)&lt;/li&gt;
&lt;li&gt;Data Layer (Repositories, APIs, DB)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Layered Architecture Matters in Large Apps
&lt;/h2&gt;

&lt;p&gt;Here’s what layered architecture solves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bloated widgets&lt;/strong&gt;: Business logic is moved out into use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard-to-test code&lt;/strong&gt;: Each layer becomes independently testable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tightly coupled APIs&lt;/strong&gt;: Use repositories and abstractions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding new developers&lt;/strong&gt;: Clear folder boundaries make it easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability issues&lt;/strong&gt;: Business logic is reusable across screens and features.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Breaking Down the Layers (With Dart Examples)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Presentation Layer
&lt;/h3&gt;

&lt;p&gt;This is your &lt;strong&gt;UI&lt;/strong&gt; layer. It includes widgets, BLoC, Cubits, or Riverpod providers. This layer &lt;strong&gt;should never contain business logic&lt;/strong&gt; or direct data access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Responsibilities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show UI&lt;/li&gt;
&lt;li&gt;React to state changes&lt;/li&gt;
&lt;li&gt;Call use cases not services, not APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileScreen&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;final&lt;/span&gt; &lt;span class="n"&gt;GetUserProfileUseCase&lt;/span&gt; &lt;span class="n"&gt;useCase&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;ProfileScreen&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;useCase&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;(&lt;/span&gt;
      &lt;span class="nl"&gt;future:&lt;/span&gt; &lt;span class="n"&gt;useCase&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="c1"&gt;// show UI&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. Application Layer (Use Cases)
&lt;/h3&gt;

&lt;p&gt;This layer represents &lt;strong&gt;actions&lt;/strong&gt; in your app like &lt;code&gt;LoginUser&lt;/code&gt;, &lt;code&gt;GetPosts&lt;/code&gt;, &lt;code&gt;UpdateCart&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Responsibilities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encapsulate one app-level action&lt;/li&gt;
&lt;li&gt;Use domain models and repositories&lt;/li&gt;
&lt;li&gt;Contain no UI or infrastructure code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetUserProfileUseCase&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;repo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;GetUserProfileUseCase&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;repo&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;call&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;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserProfile&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;
  
  
  3. Domain Layer
&lt;/h3&gt;

&lt;p&gt;This is the &lt;strong&gt;core logic&lt;/strong&gt; of your app where your entities and rules live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Responsibilities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define pure business rules&lt;/li&gt;
&lt;li&gt;Be completely Flutter-independent&lt;/li&gt;
&lt;li&gt;No external libraries, no platform dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&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="n"&gt;User&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="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isValid&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;name&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Data Layer
&lt;/h3&gt;

&lt;p&gt;This layer communicates with the outside world APIs, databases, local storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Responsibilities&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement repository interfaces&lt;/li&gt;
&lt;li&gt;Handle API or DB calls&lt;/li&gt;
&lt;li&gt;Convert between DTOs and domain models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepositoryImpl&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;final&lt;/span&gt; &lt;span class="n"&gt;RemoteDataSource&lt;/span&gt; &lt;span class="n"&gt;remote&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getUserProfile&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;dto&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;remote&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="k"&gt;return&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="n"&gt;dto&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="nl"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;dto&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;h2&gt;
  
  
  Tools to Support Layered Architecture
&lt;/h2&gt;

&lt;p&gt;Here are some tools and libraries that make working with this structure easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State Management&lt;/strong&gt;: BLoC, Cubit, Riverpod&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Injection&lt;/strong&gt;: &lt;code&gt;get_it&lt;/code&gt;, &lt;code&gt;injectable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Generation&lt;/strong&gt;: &lt;code&gt;freezed&lt;/code&gt;, &lt;code&gt;json_serializable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: &lt;code&gt;mocktail&lt;/code&gt;, &lt;code&gt;bloc_test&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Folder Structure
&lt;/h2&gt;

&lt;p&gt;Organizing by &lt;strong&gt;feature first&lt;/strong&gt;, then &lt;strong&gt;by layer&lt;/strong&gt; within each feature is recommended.&lt;/p&gt;

&lt;p&gt;Example structure for a &lt;code&gt;profile&lt;/code&gt; feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/lib
  /features
    /profile
      /presentation
        profile_screen.dart
        profile_cubit.dart
      /application
        get_user_profile_usecase.dart
      /domain
        entities/user.dart
        repositories/user_repository.dart
      /data
        models/user_dto.dart
        repositories/user_repository_impl.dart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each feature is modular, self-contained, and easier to navigate or test.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use interfaces in the domain layer and implement them in the data layer.&lt;/li&gt;
&lt;li&gt;Keep presentation logic (UI) separate from business logic (use cases).&lt;/li&gt;
&lt;li&gt;Make your domain layer &lt;strong&gt;pure Dart&lt;/strong&gt; with no dependencies.&lt;/li&gt;
&lt;li&gt;Avoid injecting services directly into widgets.&lt;/li&gt;
&lt;li&gt;Prefer dependency injection over global service locators.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Use Case: E-Commerce App
&lt;/h2&gt;

&lt;p&gt;In a large shopping app, you might have features like cart, profile, search, checkout. Each of these would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have its own use cases like &lt;code&gt;AddToCartUseCase&lt;/code&gt;, &lt;code&gt;GetCartItemsUseCase&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use Cubits/Blocs only for managing state&lt;/li&gt;
&lt;li&gt;Keep data fetching in data layer via repositories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure scales well across multiple teams and codebases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mixing UI code with business logic (e.g., calling APIs inside widgets)&lt;/li&gt;
&lt;li&gt;Skipping domain layer to move faster (you’ll slow down later)&lt;/li&gt;
&lt;li&gt;Overusing abstractions when not needed&lt;/li&gt;
&lt;li&gt;Injecting concrete classes everywhere instead of interfaces&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Layered architecture isn’t about creating folders for the sake of it. It’s about &lt;strong&gt;controlling the direction of dependencies&lt;/strong&gt;, &lt;strong&gt;limiting complexity&lt;/strong&gt;, and &lt;strong&gt;empowering collaboration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It helps you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scale your app confidently&lt;/li&gt;
&lt;li&gt;Keep testing fast and easy&lt;/li&gt;
&lt;li&gt;Prevent spaghetti code&lt;/li&gt;
&lt;li&gt;Make onboarding developers smooth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re planning to build a large Flutter app that can grow without chaos this architecture is not optional. It’s essential.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stream vs Future in Dart: Stop Using the Wrong One</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Thu, 17 Jul 2025 10:13:58 +0000</pubDate>
      <link>https://dev.to/alaminkarno/stream-vs-future-in-dart-stop-using-the-wrong-one-11gp</link>
      <guid>https://dev.to/alaminkarno/stream-vs-future-in-dart-stop-using-the-wrong-one-11gp</guid>
      <description>&lt;p&gt;When building Flutter apps, you’ve probably asked yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Should I use a &lt;code&gt;Future&lt;/code&gt; here? Or do I need a &lt;code&gt;Stream&lt;/code&gt;?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many developers mix these up, which leads to performance issues, hard-to-maintain code, or UI bugs. In this blog, we’ll unpack &lt;strong&gt;when to use &lt;code&gt;Future&lt;/code&gt;, when to use &lt;code&gt;Stream&lt;/code&gt;, and how to avoid the most common mistakes&lt;/strong&gt; with examples and real-world use cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s the Difference?
&lt;/h2&gt;

&lt;p&gt;Let’s quickly define the two:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Future&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Represents &lt;strong&gt;a single asynchronous value&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Completes once.&lt;/li&gt;
&lt;li&gt;Best for short-lived operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Example: Fetching a user profile from a server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Stream&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Represents &lt;strong&gt;a sequence of asynchronous values&lt;/strong&gt; over time.&lt;/li&gt;
&lt;li&gt;Can emit multiple values.&lt;/li&gt;
&lt;li&gt;May never complete.&lt;/li&gt;
&lt;li&gt;Ideal for long-lived or real-time data.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Example: Receiving real-time messages from a chat app.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Future Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&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;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchUser&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;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&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;seconds:&lt;/span&gt; &lt;span class="mi"&gt;2&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;'Alamin Karno'&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;Using it in a &lt;code&gt;FutureBuilder&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;FutureBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;future:&lt;/span&gt; &lt;span class="n"&gt;fetchUser&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="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;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;'Hello, &lt;/span&gt;&lt;span class="si"&gt;${snapshot.data}&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;p&gt;Good use cases for &lt;code&gt;Future&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetching one-time data (e.g. user profile, config)&lt;/li&gt;
&lt;li&gt;Writing to a local file&lt;/li&gt;
&lt;li&gt;Showing a splash delay&lt;/li&gt;
&lt;li&gt;Getting device permissions or version info&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Stream Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Stream&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;counterStream&lt;/span&gt;&lt;span class="p"&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;{&lt;/span&gt;
  &lt;span class="k"&gt;for&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;i&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="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&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="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;delayed&lt;/span&gt;&lt;span class="p"&gt;(&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;seconds:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;i&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;Using it in a &lt;code&gt;StreamBuilder&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;StreamBuilder&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="nl"&gt;stream:&lt;/span&gt; &lt;span class="n"&gt;counterStream&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="o"&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;hasData&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;CircularProgressIndicator&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;'Counter: &lt;/span&gt;&lt;span class="si"&gt;${snapshot.data}&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;p&gt;Good use cases for &lt;code&gt;Stream&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listening to Firebase/FireStore changes&lt;/li&gt;
&lt;li&gt;Real-time chat messages&lt;/li&gt;
&lt;li&gt;GPS or sensor updates&lt;/li&gt;
&lt;li&gt;Bluetooth data streams&lt;/li&gt;
&lt;li&gt;Internet connectivity updates&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;FutureBuilder&lt;/code&gt; for continuous data&lt;/strong&gt;&lt;br&gt;
Using &lt;code&gt;FutureBuilder&lt;/code&gt; with something that emits data repeatedly (like Firebase or a location stream) is a &lt;strong&gt;bad idea&lt;/strong&gt;. &lt;code&gt;FutureBuilder&lt;/code&gt; runs once and won’t update when new data comes.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;StreamBuilder&lt;/code&gt; for such cases.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Forgetting to cancel a stream subscription&lt;/strong&gt;&lt;br&gt;
If you manually use &lt;code&gt;listen()&lt;/code&gt; on a stream, don’t forget to cancel it in &lt;code&gt;dispose()&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="kd"&gt;late&lt;/span&gt; &lt;span class="n"&gt;StreamSubscription&lt;/span&gt; &lt;span class="n"&gt;subscription&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="nf"&gt;initState&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;someStream&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="c1"&gt;// handle event&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="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="nf"&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;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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;Stream.fromFuture()&lt;/code&gt; without reason&lt;/strong&gt;&lt;br&gt;
This is unnecessary unless you’re working with an API that only takes a stream. Don’t turn a &lt;code&gt;Future&lt;/code&gt; into a &lt;code&gt;Stream&lt;/code&gt; unless the use case requires it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Real-World Use Cases (In Words)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User login&lt;/strong&gt;: Use &lt;code&gt;Future&lt;/code&gt; — it’s a one-time action that returns a result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Firestore updates&lt;/strong&gt;: Use &lt;code&gt;Stream&lt;/code&gt; — documents can change any time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Location tracking&lt;/strong&gt;: Use &lt;code&gt;Stream&lt;/code&gt; — location changes over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get device info or permissions&lt;/strong&gt;: Use &lt;code&gt;Future&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live sensor data from a fitness app&lt;/strong&gt;: Use &lt;code&gt;Stream&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Advanced Tips
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Creating your own Stream&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;Stream&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;generateStream&lt;/span&gt;&lt;span class="p"&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;{&lt;/span&gt;
  &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&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;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&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;seconds:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;yield&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Combine multiple streams using RxDart&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;Rx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;combineLatest2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream2&lt;/span&gt;&lt;span class="p"&gt;,&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="n"&gt;b&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="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;$a&lt;/span&gt;&lt;span class="s"&gt; &amp;amp; &lt;/span&gt;&lt;span class="si"&gt;$b&lt;/span&gt;&lt;span class="s"&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;Convert stream to future if you only need the first value&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;result&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;myStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;FutureBuilder&lt;/code&gt; for short, one-time data needs.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;StreamBuilder&lt;/code&gt; for long running or changing data.&lt;/li&gt;
&lt;li&gt;Always dispose of stream subscriptions if you manually listen to them.&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;Stream.periodic()&lt;/code&gt; unless absolutely needed.&lt;/li&gt;
&lt;li&gt;For complex use cases, use &lt;code&gt;rxdart&lt;/code&gt; for reactive patterns like debouncing, combining streams, etc.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Choosing between &lt;code&gt;Future&lt;/code&gt; and &lt;code&gt;Stream&lt;/code&gt; is not just about personal preference it’s about picking the right tool for the job.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Future&lt;/code&gt; for short, one-time tasks.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Stream&lt;/code&gt; when data changes over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mastering this will help you write &lt;strong&gt;faster, cleaner, and more reactive Flutter apps&lt;/strong&gt; and avoid many hidden bugs in your UI or business logic.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Trusted Dart’s Null Safety… and It Still Crashed My App</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Wed, 16 Jul 2025 08:42:56 +0000</pubDate>
      <link>https://dev.to/alaminkarno/i-trusted-darts-null-safety-and-it-still-crashed-my-app-365a</link>
      <guid>https://dev.to/alaminkarno/i-trusted-darts-null-safety-and-it-still-crashed-my-app-365a</guid>
      <description>&lt;p&gt;It was a chill Thursday night. I was working on a profile update feature for one of my Flutter apps. I had just refactored a bunch of code and felt pretty confident.&lt;/p&gt;

&lt;p&gt;After all, Dart has null safety now what could go wrong?&lt;/p&gt;

&lt;p&gt;The app launched, everything looked good. But then… &lt;strong&gt;Crash&lt;/strong&gt;.&lt;br&gt;
Not a red screen. Not a console warning. A full-on, “Unhandled exception: Null check operator used on a null value.”&lt;/p&gt;

&lt;p&gt;I was confused.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Wait, I’m using null safety. Isn’t this stuff supposed to be impossible now?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had sprinkled a few &lt;code&gt;!&lt;/code&gt; operators here and there (you know, just to keep the compiler happy). But that one exclamation mark brought the whole thing down.&lt;/p&gt;

&lt;p&gt;That was my wake-up call.&lt;/p&gt;

&lt;p&gt;And that night, I went deep into understanding Dart’s null safety. In this post, I’ll share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What I did wrong&lt;/li&gt;
&lt;li&gt;How Dart null safety really works&lt;/li&gt;
&lt;li&gt;What tools Dart gives you (&lt;code&gt;?&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;, &lt;code&gt;late&lt;/code&gt;, &lt;code&gt;required&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Best practices to keep your app safe, clean, and crash-free&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  What Is Null Safety, Really?
&lt;/h2&gt;

&lt;p&gt;Null safety in Dart means &lt;strong&gt;you have to explicitly declare when a variable can be null&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before null safety, any variable could potentially be null, leading to &lt;em&gt;tons&lt;/em&gt; of runtime crashes.&lt;/p&gt;

&lt;p&gt;Now, you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'Md. Al-Amin'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// non-nullable&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;nickname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// nullable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dart forces you to think about nulls. It doesn’t let you do:&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nickname&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ❌ compile error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to check if it’s null:&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nickname&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="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nickname&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use &lt;code&gt;!&lt;/code&gt; to say “I promise this isn’t null” (but use it carefully… more on that soon).&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Did Wrong: The Dangerous &lt;code&gt;!&lt;/code&gt; Operator
&lt;/h2&gt;

&lt;p&gt;Back to my bug…&lt;/p&gt;

&lt;p&gt;I had this 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="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;userToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// later...&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;setToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userToken&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my head, I was sure &lt;code&gt;userToken&lt;/code&gt; was initialized before calling &lt;code&gt;setToken&lt;/code&gt;. But in reality, it wasn’t.&lt;/p&gt;

&lt;p&gt;That &lt;code&gt;!&lt;/code&gt; is called the &lt;strong&gt;null assertion operator&lt;/strong&gt;, and it basically says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Trust me, this is not null don’t check, just run.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But Dart doesn’t &lt;strong&gt;protect&lt;/strong&gt; you here. If &lt;code&gt;userToken&lt;/code&gt; is null, you crash.&lt;br&gt;
And that’s exactly what happened.&lt;/p&gt;


&lt;h2&gt;
  
  
  Let’s Break Down the Null Safety Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;?&lt;/code&gt; → Nullable Variable&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="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means &lt;code&gt;email&lt;/code&gt; can be either a &lt;code&gt;String&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;?&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re receiving data from external sources (APIs, storage, etc.)&lt;/li&gt;
&lt;li&gt;A variable truly might not be set (optional fields)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;&lt;code&gt;!&lt;/code&gt; → Null Assertion&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="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;email&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="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// You better be 100% sure it's not null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this &lt;strong&gt;only when you’re absolutely certain&lt;/strong&gt; a value can never be null at that point.&lt;/p&gt;

&lt;p&gt;When it’s okay:&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&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="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// you're safe here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it’s dangerous:&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// if email is null = crash!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tip: Avoid using &lt;code&gt;!&lt;/code&gt; unless you’ve done a prior null check.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;code&gt;late&lt;/code&gt; → Delayed Initialization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What if a variable can’t be set immediately, but you’re sure it’ll be set before use?&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;late&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;token&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;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetchToken&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;makeRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Dart assumes it's set before use&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 to assign it before access &lt;strong&gt;Dart throws an error&lt;/strong&gt; at runtime.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;late&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want to avoid &lt;code&gt;null&lt;/code&gt;, but initialize later (e.g., in &lt;code&gt;initState&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You’re working with dependency injection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t use it as a lazy shortcut for avoiding &lt;code&gt;?&lt;/code&gt; or &lt;code&gt;!&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;code&gt;required&lt;/code&gt; → Non-nullable named parameters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before null safety:&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;createUser&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="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After null safety:&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;createUser&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;name&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;email&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the caller doesn’t pass &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;email&lt;/code&gt;, Dart throws a compile-time error.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;required&lt;/code&gt; for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named parameters that are essential&lt;/li&gt;
&lt;li&gt;Enforcing contracts in your API or widget constructors&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-World Use Case: User Profile Model
&lt;/h2&gt;

&lt;p&gt;Let’s look at a simple &lt;code&gt;User&lt;/code&gt; model that could crash if nulls aren’t handled well:&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;User&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="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;email&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;phone&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="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="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="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;phone&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, when rendering UI:&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;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;email&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;'No email available'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s safe. No &lt;code&gt;!&lt;/code&gt; needed. No risk of crash.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for Null Safety in Dart
&lt;/h2&gt;

&lt;p&gt;Here’s what I learned and now live by:&lt;/p&gt;

&lt;p&gt;Do This&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;?&lt;/code&gt; when data may be null&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;required&lt;/code&gt; for essential fields&lt;/li&gt;
&lt;li&gt;Check nulls before using values&lt;/li&gt;
&lt;li&gt;Use defaults like &lt;code&gt;??&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write unit tests for null cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid This&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making everything &lt;code&gt;late&lt;/code&gt; to avoid &lt;code&gt;?&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Forcing values with &lt;code&gt;!&lt;/code&gt; everywhere&lt;/li&gt;
&lt;li&gt;Trusting external data blindly&lt;/li&gt;
&lt;li&gt;Assuming things are initialized&lt;/li&gt;
&lt;li&gt;Ignoring edge cases in models&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Extra Tip: Null-Aware Operators in Dart
&lt;/h2&gt;

&lt;p&gt;Dart gives you some powerful operators:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;??&lt;/code&gt; → Default value if null&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;print&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;email&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;'No email'&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;&lt;code&gt;??=&lt;/code&gt; → Assign default if null&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;email&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="s"&gt;'unknown@example.com'&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;&lt;code&gt;?.&lt;/code&gt; → Safe access&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;print&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;profile&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// If profile is null, won’t crash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Dart’s null safety is amazing, but it only works if we understand how to use it.&lt;/p&gt;

&lt;p&gt;Null safety doesn’t mean “no more crashes.”&lt;br&gt;
It means: “You now have the power to &lt;strong&gt;prevent&lt;/strong&gt; them, but you have to &lt;strong&gt;think&lt;/strong&gt;.”&lt;/p&gt;




&lt;h2&gt;
  
  
  My Advice to You
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Avoid the &lt;code&gt;!&lt;/code&gt; unless you’re absolutely sure&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;?&lt;/code&gt; and null-aware operators liberally they’re your friends&lt;/li&gt;
&lt;li&gt;Don’t go overboard with &lt;code&gt;late&lt;/code&gt; — it’s powerful, but sharp&lt;/li&gt;
&lt;li&gt;And always, always test your edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Dart asks you to handle nulls, it’s not being annoying its &lt;strong&gt;saving future you from a nightmare&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  I’d love to hear from you:
&lt;/h2&gt;

&lt;p&gt;Have you ever been betrayed by a &lt;code&gt;!&lt;/code&gt;? Or misused &lt;code&gt;late&lt;/code&gt; and got runtime surprises?&lt;br&gt;
Share your experience and let’s help each other write safer Dart code.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stop the Confusion: Flutter State Management Explained</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Tue, 15 Jul 2025 16:44:29 +0000</pubDate>
      <link>https://dev.to/alaminkarno/stop-the-confusion-flutter-state-management-explained-4ce9</link>
      <guid>https://dev.to/alaminkarno/stop-the-confusion-flutter-state-management-explained-4ce9</guid>
      <description>&lt;p&gt;Let’s be honest for a sec…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Should I use &lt;code&gt;setState&lt;/code&gt;, or Provider, or Riverpod?&lt;br&gt;
I heard Bloc is better for large apps, but isn’t it too much for small ones?&lt;br&gt;
Oh wait, now everyone’s talking about Signals...”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If that sounds familiar, you’re not alone.&lt;br&gt;
State management in Flutter is &lt;em&gt;one of the most debated and misunderstood&lt;/em&gt; topics in the community.&lt;/p&gt;

&lt;p&gt;But here’s the truth:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You don’t need to learn &lt;strong&gt;all&lt;/strong&gt; the state management libraries.&lt;br&gt;
You just need to understand &lt;strong&gt;when&lt;/strong&gt; and &lt;strong&gt;why&lt;/strong&gt; to use each based on &lt;strong&gt;your app’s complexity&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this post, we’ll walk through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What state management actually means in Flutter&lt;/li&gt;
&lt;li&gt;Real-world examples (small to large app scenarios)&lt;/li&gt;
&lt;li&gt;A friendly comparison of &lt;code&gt;setState&lt;/code&gt;, Provider, Bloc, Riverpod&lt;/li&gt;
&lt;li&gt;The cost of choosing the wrong tool&lt;/li&gt;
&lt;li&gt;My personal journey and what I recommend today&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s make state management make sense.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Is State Management, Really?
&lt;/h2&gt;

&lt;p&gt;Before we dive into tools, let’s clear this up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“State” = any piece of data that affects your UI.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A counter value (&lt;code&gt;int count&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;User login status (&lt;code&gt;bool isLoggedIn&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A fetched list of posts (&lt;code&gt;List&amp;lt;Post&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A form input (&lt;code&gt;String email&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever that data changes, the UI needs to update to reflect the new state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;State management = keeping track of changes + rebuilding the right parts of the UI&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds simple… until your app has 15 screens, 3 types of state, and 2 developers fighting over where the logic should live.&lt;/p&gt;


&lt;h2&gt;
  
  
  Real-World Analogy: Managing State = Managing Conversations
&lt;/h2&gt;

&lt;p&gt;Imagine you’re at a party with a few friends. One person says something, and a few others react.&lt;/p&gt;

&lt;p&gt;If only 2 people are talking, it’s easy to manage.&lt;/p&gt;

&lt;p&gt;But if &lt;strong&gt;everyone’s shouting across the room&lt;/strong&gt; things get chaotic.&lt;br&gt;
That’s what happens when your app grows without proper state separation.&lt;/p&gt;

&lt;p&gt;State management helps you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decide &lt;strong&gt;who owns the state&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Control &lt;strong&gt;who should rebuild&lt;/strong&gt; when it changes&lt;/li&gt;
&lt;li&gt;Keep everything o*&lt;em&gt;rganized and predictable&lt;/em&gt;*&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Scenario 1: The Simple App (Counter, Toggle, Form)
&lt;/h2&gt;

&lt;p&gt;Let’s say you’re building a simple counter or toggle switch. One widget, one screen.&lt;/p&gt;

&lt;p&gt;Here’s what works beautifully:&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;MyCounter&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="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;MyCounter&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;_MyCounterState&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;_MyCounterState&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;MyCounter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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="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;Column&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;'Count: &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="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;setState&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;count&lt;/span&gt;&lt;span class="o"&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Increment'&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;p&gt;&lt;strong&gt;Use &lt;code&gt;setState&lt;/code&gt; when&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your state is &lt;strong&gt;local&lt;/strong&gt; to one widget&lt;/li&gt;
&lt;li&gt;Your app is &lt;strong&gt;simple&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No business logic, no shared state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;setState&lt;/code&gt; is not evil it's fast and perfect for UI-local state.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scenario 2: Medium App (Login Flow, Theme Toggle, Auth State)
&lt;/h2&gt;

&lt;p&gt;Now your app has 3–5 screens. You need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep track of whether the user is logged in&lt;/li&gt;
&lt;li&gt;Show a different home screen if not&lt;/li&gt;
&lt;li&gt;Share data across screens (e.g., user info, theme mode)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where &lt;code&gt;setState&lt;/code&gt; breaks down it doesn’t persist across screens, and passing state through constructors becomes messy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Provider when&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to &lt;strong&gt;share state&lt;/strong&gt; across multiple widgets or screens&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;separation of concerns&lt;/strong&gt; (UI ≠ business logic)&lt;/li&gt;
&lt;li&gt;You still want things to feel Flutter-native&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthProvider&lt;/span&gt; &lt;span class="k"&gt;with&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;bool&lt;/span&gt; &lt;span class="n"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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="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;Then in UI:&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;auth&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;AuthProvider&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="n"&gt;auth&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Works well with small to medium apps&lt;/li&gt;
&lt;li&gt;Easy learning curve&lt;/li&gt;
&lt;li&gt;Plays nicely with &lt;code&gt;Consumer&lt;/code&gt;, &lt;code&gt;Selector&lt;/code&gt;, and performance tools&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Scenario 3: Large App (Chat App, eCommerce, Real-Time Data)
&lt;/h2&gt;

&lt;p&gt;Let’s say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have 10+ screens&lt;/li&gt;
&lt;li&gt;Multiple async calls&lt;/li&gt;
&lt;li&gt;Business logic needs to be testable&lt;/li&gt;
&lt;li&gt;You need to separate &lt;code&gt;UI&lt;/code&gt;, &lt;code&gt;UseCase&lt;/code&gt;, &lt;code&gt;Repository&lt;/code&gt;, &lt;code&gt;Data Source&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s where &lt;strong&gt;Bloc&lt;/strong&gt; or &lt;strong&gt;Cubit&lt;/strong&gt; shines.&lt;/p&gt;

&lt;p&gt;With Bloc, you &lt;strong&gt;structure your app like a real system&lt;/strong&gt;, with clear layers.&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;// Event&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoadUserEvent&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="c1"&gt;// State&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserLoaded&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;UserState&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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;UserLoaded&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;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Bloc&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserBloc&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Bloc&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserEvent&lt;/span&gt;&lt;span class="p"&gt;,&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;UserRepo&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;UserBloc&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;repo&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;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;on&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoadUserEvent&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="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;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUser&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;UserLoaded&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="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;Use Bloc or Cubit when&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;strong&gt;strict structure + testability&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You’re working in a &lt;strong&gt;team&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You need predictable, event-driven logic&lt;/li&gt;
&lt;li&gt;You’re building apps with &lt;strong&gt;real business logic&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, it’s more boilerplate but in large apps, that structure is gold.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scenario 4: Clean &amp;amp; Modern State (with Less Boilerplate)
&lt;/h2&gt;

&lt;p&gt;Now say you love &lt;code&gt;Provider&lt;/code&gt;’s simplicity but wish it had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better modularity&lt;/li&gt;
&lt;li&gt;More control&lt;/li&gt;
&lt;li&gt;Support for &lt;strong&gt;scoped overrides&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Built-in testing features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter: &lt;strong&gt;Riverpod&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Riverpod is like a modern evolution of &lt;code&gt;Provider&lt;/code&gt;. It’s &lt;strong&gt;fully DI-friendly&lt;/strong&gt;, &lt;strong&gt;testable&lt;/strong&gt;, and &lt;strong&gt;stateless&lt;/strong&gt; at its core.&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;counterProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StateProvider&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;ref&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="n"&gt;ref&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="n"&gt;counterProvider&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;state&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works with &lt;code&gt;hooks&lt;/code&gt;, async providers, &lt;code&gt;FutureProvider&lt;/code&gt;, and integrates beautifully with Clean Architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Riverpod when&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;strong&gt;fine-grained control&lt;/strong&gt; without the Bloc boilerplate&lt;/li&gt;
&lt;li&gt;You prefer &lt;strong&gt;composition over inheritance&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You like working declaratively and test-first&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparison Table: Flutter State Management at a Glance
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdmygr6c9bzn8872mq12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdmygr6c9bzn8872mq12.png" alt="Flutter State Management Comparison" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens If You Pick the Wrong One?
&lt;/h2&gt;

&lt;p&gt;Let’s be real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Picking Bloc for a 2-screen app = frustration&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;setState&lt;/code&gt; in a real-time shopping app = chaos&lt;/li&gt;
&lt;li&gt;Using Provider for deeply nested reactive logic = rebuild madness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why choosing &lt;strong&gt;the right tool for the job&lt;/strong&gt; is key. Don’t just follow trends &lt;strong&gt;understand your app’s needs&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Journey (And What I Recommend)
&lt;/h2&gt;

&lt;p&gt;When I started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I used &lt;code&gt;setState&lt;/code&gt; for everything.&lt;/li&gt;
&lt;li&gt;Then I discovered &lt;code&gt;Provider&lt;/code&gt; — and it changed my life.&lt;/li&gt;
&lt;li&gt;On a large team project, we used Bloc, and it made the architecture scale.&lt;/li&gt;
&lt;li&gt;Now, for personal and clean projects, I use &lt;strong&gt;Riverpod&lt;/strong&gt; it’s declarative, scalable, and clean.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, here’s my general recommendation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very Small (1–2 screens) - &lt;code&gt;setState&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Small to Medium - &lt;code&gt;Provider&lt;/code&gt; or &lt;code&gt;Riverpod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Large / Team Projects - &lt;code&gt;Bloc&lt;/code&gt; or &lt;code&gt;Riverpod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Apps with Clean Architecture - &lt;code&gt;Riverpod&lt;/code&gt; + DI or Bloc + &lt;code&gt;get_it&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;State management in Flutter &lt;strong&gt;isn’t about picking the “best” tool&lt;/strong&gt; it’s about picking the &lt;strong&gt;right tool for your app’s complexity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Start simple. Learn why things break. Then scale your tools as your app grows.&lt;/p&gt;

&lt;p&gt;And remember:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clean architecture isn’t about more code it’s about code that makes sense later.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  I’d Love to Hear From You
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What’s your go-to state management solution?&lt;/li&gt;
&lt;li&gt;Have you had pain points switching from one to another?&lt;/li&gt;
&lt;li&gt;Want me to break down Riverpod or Bloc in the next post?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s keep learning and keep Flutter clean.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>code</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Clean Flutter Apps Use Dependency Injection and Yours Should Too</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Mon, 14 Jul 2025 09:58:58 +0000</pubDate>
      <link>https://dev.to/alaminkarno/why-clean-flutter-apps-use-dependency-injection-and-yours-should-too-3668</link>
      <guid>https://dev.to/alaminkarno/why-clean-flutter-apps-use-dependency-injection-and-yours-should-too-3668</guid>
      <description>&lt;p&gt;Have you ever written a widget that somehow ended up knowing about your database, API client, local storage, and maybe even Firebase auth?&lt;/p&gt;

&lt;p&gt;Be honest we’ve all been there.&lt;/p&gt;

&lt;p&gt;At first, everything works fine. You call a service from your UI, get the result, display it. But a few weeks later…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your widget’s &lt;code&gt;build()&lt;/code&gt; method is 100+ lines&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;HomeScreen&lt;/code&gt; knows about every feature in the app&lt;/li&gt;
&lt;li&gt;Writing a test requires bootstrapping your entire app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Welcome to &lt;strong&gt;tight coupling hell&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The good news? There’s a simple fix it’s called &lt;strong&gt;Dependency Injection (DI)&lt;/strong&gt;. And in this post, I’m going to show you &lt;strong&gt;why it matters, how it works in Flutter&lt;/strong&gt;, and &lt;strong&gt;how to start using it in your real apps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s go.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, What the Heck Is Dependency Injection?
&lt;/h2&gt;

&lt;p&gt;Think of it like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instead of your class creating everything it needs, you just give it what it needs from the outside.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, if a class needs an &lt;code&gt;ApiClient&lt;/code&gt;, you don’t create one inside it. You pass one in like a gift.&lt;/p&gt;

&lt;p&gt;This makes the class:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to test&lt;/li&gt;
&lt;li&gt;Easier to replace&lt;/li&gt;
&lt;li&gt;Easier to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s compare:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without DI&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&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;_api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ApiClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// tightly coupled&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;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&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;_api&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now UserService is stuck with ApiClient. You can’t test it with mock data, and swapping out the API layer will break your app.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;With DI&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&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;api&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;UserService&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;api&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getUser&lt;/span&gt;&lt;span class="p"&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;api&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can inject any version of &lt;code&gt;ApiClient&lt;/code&gt; a mock, a test version, or a real one and &lt;code&gt;UserService&lt;/code&gt; won’t care. That’s the beauty of DI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Should You Care?
&lt;/h2&gt;

&lt;p&gt;Let me show you 3 reasons you’ll thank yourself later:&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Code Becomes Modular
&lt;/h2&gt;

&lt;p&gt;When each class receives its dependencies, you can change one thing without touching everything.&lt;/p&gt;

&lt;p&gt;Imagine replacing Firebase Auth with Supabase. If you injected your AuthService, the change is painless. If not… you’re in for a lot of refactoring.&lt;/p&gt;




&lt;h2&gt;
  
  
  You Can Actually Write Unit Tests
&lt;/h2&gt;

&lt;p&gt;With DI, testing becomes clean and easy:&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;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserService&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to mock the entire app or spin up a widget. You test what matters and only that.&lt;/p&gt;




&lt;h2&gt;
  
  
  It Makes Clean Architecture Possible
&lt;/h2&gt;

&lt;p&gt;If you want to follow clean architecture in Flutter (UI → Use Cases → Repositories → Data Sources), &lt;strong&gt;you need DI&lt;/strong&gt; to keep layers separate.&lt;/p&gt;

&lt;p&gt;Otherwise, your app becomes one big spaghetti plate where everything touches everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Do Dependency Injection in Flutter
&lt;/h2&gt;

&lt;p&gt;Let’s talk tools.&lt;/p&gt;

&lt;p&gt;You can do DI manually (by passing things into constructors), or use tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_it&lt;/code&gt; – Service locator (basic but powerful)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;injectable&lt;/code&gt; – Code generator built on top of &lt;code&gt;get_it&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Riverpod&lt;/code&gt; – Great for DI + state management in one&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;provider&lt;/code&gt; – Can be used for simple DI too&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this blog, I’ll show you &lt;code&gt;get_it&lt;/code&gt; + &lt;code&gt;injectable&lt;/code&gt; the combo I personally use in most of my apps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Example: User Profile Feature
&lt;/h2&gt;

&lt;p&gt;Let’s say we’re building a user profile screen. Here’s the flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ProfileScreen → ProfileCubit → GetUserProfileUseCase → UserRepository → ApiClient
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our goal is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The screen only knows about the Cubit&lt;/li&gt;
&lt;li&gt;The Cubit only knows about the use case&lt;/li&gt;
&lt;li&gt;Each layer is injected, not hardcoded&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Add Dependencies
&lt;/h2&gt;

&lt;p&gt;In your &lt;code&gt;pubspec.yaml&lt;/code&gt;:&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;get_it&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^7.6.0&lt;/span&gt;
  &lt;span class="na"&gt;injectable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^2.3.0&lt;/span&gt;

&lt;span class="na"&gt;dev_dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build_runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^2.4.0&lt;/span&gt;
  &lt;span class="na"&gt;injectable_generator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^2.4.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Set Up the DI Container
&lt;/h2&gt;

&lt;p&gt;Create a file: &lt;code&gt;lib/di/injection.dart&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:get_it/get_it.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:injectable/injectable.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;'injection.config.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// this will be generated&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;getIt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetIt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@InjectableInit&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;configureDependencies&lt;/span&gt;&lt;span class="p"&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;getIt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub run build_runner build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Annotate Classes
&lt;/h2&gt;

&lt;p&gt;Let’s annotate our repository and services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Client&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="nd"&gt;@lazySingleton&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiClient&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getUser&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="c1"&gt;// Fetch user from API&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;User Repository&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="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;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&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="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@LazySingleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="o"&gt;:&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;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepositoryImpl&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;final&lt;/span&gt; &lt;span class="n"&gt;ApiClient&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;UserRepositoryImpl&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;api&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="n"&gt;User&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUser&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;Use Case&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="nd"&gt;@injectable&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetUserProfileUseCase&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;repo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;GetUserProfileUseCase&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;repo&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;call&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;repo&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cubit&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="nd"&gt;@injectable&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;Cubit&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;GetUserProfileUseCase&lt;/span&gt; &lt;span class="n"&gt;getUser&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="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUser&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;ProfileInitial&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;loadProfile&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;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;getUser&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;user&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 your app, you can get your Cubit like this:&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;cubit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getIt&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Testable. Scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for DI in Flutter
&lt;/h2&gt;

&lt;p&gt;Here are a few do’s and don’ts I’ve learned the hard way:&lt;/p&gt;

&lt;p&gt;Do This&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;@LazySingleton&lt;/code&gt; for services&lt;/li&gt;
&lt;li&gt;Use abstract interfaces&lt;/li&gt;
&lt;li&gt;Mock dependencies in tests&lt;/li&gt;
&lt;li&gt;Group dependencies in one place (like &lt;code&gt;injection.dart&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Keep your layers decoupled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t Do This&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create services directly in widgets&lt;/li&gt;
&lt;li&gt;Hardcode concrete classes everywhere&lt;/li&gt;
&lt;li&gt;Depend on real APIs during unit tests&lt;/li&gt;
&lt;li&gt;Scatter initialization logic across files&lt;/li&gt;
&lt;li&gt;Let UI talk directly to repositories or APIs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Bonus: Mocking in Tests
&lt;/h2&gt;

&lt;p&gt;Let’s say you want to test &lt;code&gt;GetUserProfileUseCase&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeRepo&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="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="n"&gt;User&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="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;User&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;'Fake 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;Then test it:&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;useCase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetUserProfileUseCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FakeRepo&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;'returns fake user'&lt;/span&gt;&lt;span class="p"&gt;,&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;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;useCase&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;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="s"&gt;'Fake User'&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 app. No UI. Just pure logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-Up: What You Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dependency Injection decouples your code and keeps things clean&lt;/li&gt;
&lt;li&gt;It’s the foundation of scalable, testable Flutter apps&lt;/li&gt;
&lt;li&gt;Tools like &lt;code&gt;get_it&lt;/code&gt; + &lt;code&gt;injectable&lt;/code&gt; make DI simple and maintainable&lt;/li&gt;
&lt;li&gt;You can follow clean architecture patterns without the pain&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;If your app is still wiring up dependencies manually in widgets it’s time to level up.&lt;/p&gt;

&lt;p&gt;Start small. Use DI for one feature.&lt;br&gt;
Once you see how clean and testable it becomes you won’t go back.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why Your Flutter App Rebuilds Too Much — And How to Fix It</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Sun, 13 Jul 2025 18:27:40 +0000</pubDate>
      <link>https://dev.to/alaminkarno/why-your-flutter-app-rebuilds-too-much-and-how-to-fix-it-bpi</link>
      <guid>https://dev.to/alaminkarno/why-your-flutter-app-rebuilds-too-much-and-how-to-fix-it-bpi</guid>
      <description>&lt;p&gt;You press a button.&lt;br&gt;
You change one value.&lt;br&gt;
Suddenly your &lt;strong&gt;whole screen&lt;/strong&gt; rebuilds even widgets that had nothing to do with the change.&lt;/p&gt;

&lt;p&gt;You might be thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“But I only called &lt;code&gt;setState()&lt;/code&gt; once… what’s the problem?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Flutter is fast but &lt;strong&gt;unnecessary widget rebuilds can ruin performance&lt;/strong&gt;, cause visual flicker, battery drain, and even crash low-end devices.&lt;/p&gt;

&lt;p&gt;In this post, we’re going deep into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What actually causes rebuilds&lt;/li&gt;
&lt;li&gt;Real-world examples of excessive rebuilds&lt;/li&gt;
&lt;li&gt;Tools to detect them&lt;/li&gt;
&lt;li&gt;And how to fix them the right way&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want your Flutter app to feel smooth, battery-friendly, and production-grade this is for you.&lt;/p&gt;


&lt;h2&gt;
  
  
  How Flutter Really Rebuilds Widgets
&lt;/h2&gt;

&lt;p&gt;Flutter’s UI is declarative.&lt;br&gt;
That means you don’t manually change UI elements you &lt;strong&gt;describe&lt;/strong&gt; what the UI should look like at any given state.&lt;/p&gt;

&lt;p&gt;When state changes, &lt;strong&gt;Flutter rebuilds affected widgets&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But here’s the catch:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Flutter rebuilds &lt;strong&gt;from the top of the widget tree down&lt;/strong&gt; unless you break it into smaller, optimized parts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And sometimes, it rebuilds &lt;strong&gt;more than you expected&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Common Rebuild Triggers
&lt;/h2&gt;

&lt;p&gt;Here are the most common ways widgets get rebuilt (sometimes unnecessarily):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;setState()&lt;/code&gt; — Rebuilds the entire widget that called it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;InheritedWidget&lt;/code&gt; (e.g. &lt;code&gt;Theme&lt;/code&gt;, &lt;code&gt;MediaQuery&lt;/code&gt;) — Rebuilds all widgets using it when it changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Provider&lt;/code&gt;, &lt;code&gt;Riverpod&lt;/code&gt;, &lt;code&gt;Bloc&lt;/code&gt; — Depends on how you consume the state — poor use = unnecessary rebuilds&lt;/li&gt;
&lt;li&gt;Parent widget rebuilds — All child widgets rebuild unless marked &lt;code&gt;const&lt;/code&gt; or separated&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Real-World Bug: App Feels Laggy on Every Tap
&lt;/h2&gt;

&lt;p&gt;Let’s say you built a shopping cart screen:&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;CartScreen&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="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;_CartScreenState&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;_CartScreenState&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;_CartScreenState&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;CartScreen&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;quantity&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="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;Column&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;'Cart'&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;setState&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;quantity&lt;/span&gt;&lt;span class="o"&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Add'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ProductImage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// This shouldn't change&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;'Quantity: &lt;/span&gt;&lt;span class="si"&gt;$quantity&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;:&lt;br&gt;
When you press “Add”, &lt;code&gt;setState&lt;/code&gt; triggers a rebuild of the &lt;strong&gt;entire &lt;code&gt;CartScreen&lt;/code&gt;&lt;/strong&gt;, including &lt;code&gt;ProductImage()&lt;/code&gt; which is expensive to rebuild.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;br&gt;
Extract &lt;code&gt;ProductImage&lt;/code&gt; into a &lt;code&gt;StatelessWidget&lt;/code&gt;, &lt;strong&gt;outside the rebuild scope&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="nd"&gt;@override&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;Column&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;'Cart'&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;setState&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;quantity&lt;/span&gt;&lt;span class="o"&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Add'&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;ProductImage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// const prevents rebuild!&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;'Quantity: &lt;/span&gt;&lt;span class="si"&gt;$quantity&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;h2&gt;
  
  
  How to Detect Rebuilds
&lt;/h2&gt;

&lt;p&gt;Flutter gives you a few powerful ways to catch excessive rebuilding:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use &lt;code&gt;debugPrintRebuildDirtyWidgets = true;&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Add this in &lt;code&gt;main()&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="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;debugPrintRebuildDirtyWidgets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyApp&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 every widget rebuild will be printed in the debug console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Wrap widgets in &lt;code&gt;RepaintBoundary&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;RepaintBoundary&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;SomeExpensiveWidget&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 separates the render pipeline Flutter won’t repaint this widget unless its &lt;strong&gt;own state&lt;/strong&gt; changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use Flutter DevTools&lt;/strong&gt;&lt;br&gt;
Go to &lt;strong&gt;Flutter DevTools &amp;gt; Performance &amp;gt; Rebuild Stats&lt;/strong&gt;&lt;br&gt;
You’ll see exactly &lt;strong&gt;which widgets rebuilt&lt;/strong&gt; and how often.&lt;/p&gt;

&lt;p&gt;This is gold when debugging complex UI behavior.&lt;/p&gt;


&lt;h2&gt;
  
  
  Case Study: Flutter App Lagging on List Scroll
&lt;/h2&gt;

&lt;p&gt;A developer built a chat app. Each message item had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Profile picture&lt;/li&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Last message&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;li&gt;Online status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever &lt;strong&gt;any new message&lt;/strong&gt; arrived &lt;strong&gt;all messages were rebuilding&lt;/strong&gt;. Why?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;:&lt;br&gt;
They used a &lt;code&gt;ListView.builder&lt;/code&gt;, but placed the entire message list inside a widget that updated when &lt;em&gt;any message&lt;/em&gt; changed.&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;BlocBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatCubit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ChatState&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;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;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;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;messages&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;ChatTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;message:&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;messages&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="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;Every time the &lt;code&gt;ChatCubit&lt;/code&gt; emitted a new message, the &lt;strong&gt;entire &lt;code&gt;ListView&lt;/code&gt; rebuilt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;:&lt;br&gt;
Use &lt;code&gt;ListView.separated&lt;/code&gt; or optimize rebuilds by checking which items actually changed, or use indexed rebuild logic (like &lt;code&gt;flutter_bloc&lt;/code&gt;’s &lt;code&gt;buildWhen&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices to Prevent Excessive Rebuilds
&lt;/h2&gt;

&lt;p&gt;Do&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract widgets into small, reusable components&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;const&lt;/code&gt; constructors when possible&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Selector&lt;/code&gt; or &lt;code&gt;Consumer&lt;/code&gt; wisely in Provider&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;buildWhen&lt;/code&gt; / &lt;code&gt;select&lt;/code&gt; to control rebuild triggers&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;RepaintBoundary&lt;/code&gt; for images, charts, maps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put all UI inside one giant &lt;code&gt;build()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Forget to mark stateless widgets as &lt;code&gt;const&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Wrap entire widget trees in &lt;code&gt;BlocBuilder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Allow every state change to rebuild the whole tree&lt;/li&gt;
&lt;li&gt;Let heavy widgets redraw every frame&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;const&lt;/code&gt; liberally especially for static widgets&lt;/li&gt;
&lt;li&gt;Memorize data or cache widgets if they don’t change often&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ValueNotifier&lt;/code&gt; and &lt;code&gt;ValueListenableBuilder&lt;/code&gt; for simple UI state (faster than full state management for small tasks)&lt;/li&gt;
&lt;li&gt;Profile with DevTools every time you add new UI logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary: What You Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Flutter rebuilds widgets declaratively, but &lt;strong&gt;you control the scope&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Rebuilding too much = lag, Jank, bad UX&lt;/li&gt;
&lt;li&gt;Use tools like &lt;code&gt;debugPrintRebuildDirtyWidgets&lt;/code&gt;, &lt;code&gt;RepaintBoundary&lt;/code&gt;, and DevTools&lt;/li&gt;
&lt;li&gt;Optimize using &lt;code&gt;const&lt;/code&gt;, smaller widgets, and smart state consumption&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Your Flutter app may “work,” but that doesn’t mean it’s &lt;strong&gt;efficient&lt;/strong&gt;.&lt;br&gt;
Understanding how and when widgets rebuild helps you write smoother, cleaner, production-ready code.&lt;/p&gt;

&lt;p&gt;So next time you face a lag or Jank that doesn’t make sense check who’s rebuilding. It might just be that innocent widget you forgot to &lt;code&gt;const&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Reads
&lt;/h2&gt;

&lt;p&gt;[&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding Dart’s Event Loop: Why Your Async Code Acts Weird](&lt;a href="https://alaminkarno.medium.com/understanding-darts-event-loop-why-your-async-code-acts-weird-146748da0769" rel="noopener noreferrer"&gt;https://alaminkarno.medium.com/understanding-darts-event-loop-why-your-async-code-acts-weird-146748da0769&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@alaminkarno/stop-using-initstate-like-that-async-await-flutters-lifecycle-explained-42ff54e09101" rel="noopener noreferrer"&gt;Stop Using &lt;code&gt;initState()&lt;/code&gt; Like That: Async, Await &amp;amp; Flutter’s Lifecycle Explained&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stop Using initState() Like That: Async, Await &amp; Flutter’s Lifecycle Explained</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Sat, 12 Jul 2025 15:00:43 +0000</pubDate>
      <link>https://dev.to/alaminkarno/stop-using-initstate-like-that-async-await-flutters-lifecycle-explained-2fod</link>
      <guid>https://dev.to/alaminkarno/stop-using-initstate-like-that-async-await-flutters-lifecycle-explained-2fod</guid>
      <description>&lt;p&gt;Have you ever written an &lt;code&gt;async&lt;/code&gt; function inside &lt;code&gt;initState()&lt;/code&gt; to fetch data or show a loading indicator only to find your UI doesn’t update or crashes with a cryptic error?&lt;/p&gt;

&lt;p&gt;You’re not alone. Almost every Flutter dev has been there.&lt;/p&gt;

&lt;p&gt;The real issue?&lt;br&gt;
You’re using &lt;code&gt;initState()&lt;/code&gt; wrong and Dart’s event loop + widget lifecycle aren’t forgiving about it.&lt;/p&gt;

&lt;p&gt;This blog will help you &lt;strong&gt;understand what’s really happening&lt;/strong&gt;, teach you &lt;strong&gt;the right way to handle async logic in &lt;code&gt;initState()&lt;/code&gt;&lt;/strong&gt;, and walk through &lt;strong&gt;real-world examples and fixes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s fix that weird bug for good.&lt;/p&gt;


&lt;h2&gt;
  
  
  First, What Is &lt;code&gt;initState()&lt;/code&gt; Really?
&lt;/h2&gt;

&lt;p&gt;In Flutter, &lt;code&gt;initState()&lt;/code&gt; is a &lt;strong&gt;lifecycle method&lt;/strong&gt; that’s called once when your &lt;code&gt;StatefulWidget&lt;/code&gt; is inserted into the widget tree.&lt;/p&gt;

&lt;p&gt;It’s the perfect place to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialize variables&lt;/li&gt;
&lt;li&gt;Start animations&lt;/li&gt;
&lt;li&gt;Setup controllers&lt;/li&gt;
&lt;li&gt;Trigger data fetching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the catch:&lt;br&gt;
It &lt;strong&gt;must remain synchronous&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You &lt;strong&gt;can’t&lt;/strong&gt; mark &lt;code&gt;initState()&lt;/code&gt; as &lt;code&gt;async&lt;/code&gt; and if you try to &lt;code&gt;await&lt;/code&gt; something inside it, you might get strange bugs like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setState called after dispose&lt;/li&gt;
&lt;li&gt;UI not rebuilding&lt;/li&gt;
&lt;li&gt;App freezes or doesn’t respond&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  What Not to Do
&lt;/h2&gt;

&lt;p&gt;Let’s say you want to fetch data when the screen opens:&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;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&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;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// async function&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;fetchData&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&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="n"&gt;_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sounds reasonable, right? But here’s what could go wrong:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your UI might rebuild before data is ready.&lt;/li&gt;
&lt;li&gt;If the widget is disposed while the async call is still running, &lt;code&gt;setState()&lt;/code&gt; throws an error.&lt;/li&gt;
&lt;li&gt;A loading spinner might not even show until the data is fetched.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Right Way: Use &lt;code&gt;addPostFrameCallback()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To make sure your async code runs &lt;strong&gt;after the first build&lt;/strong&gt;, use:&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;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&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="n"&gt;fetchData&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 your &lt;code&gt;initState()&lt;/code&gt; stays sync, and your async logic starts after the widget is on-screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&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="nd"&gt;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&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;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&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="n"&gt;_loadInitialData&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;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;_loadInitialData&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;setState&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;_isLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;result&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;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getData&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;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;// avoid calling setState after dispose&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="n"&gt;_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_isLoading&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Real-World Case: Spinner Doesn’t Show
&lt;/h2&gt;

&lt;p&gt;You write this:&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;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&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;_loadData&lt;/span&gt;&lt;span class="p"&gt;();&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;_loadData&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;setState&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;_loading&lt;/span&gt; &lt;span class="o"&gt;=&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;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&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;seconds:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// simulate API call&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="n"&gt;_loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'B'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'C'&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;But on running it, the spinner doesn’t show!&lt;br&gt;
Why? Because the UI hasn’t had time to build before the async work blocks it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix: Let the UI build first&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="nd"&gt;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&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;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&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="n"&gt;_loadData&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 your loading indicator appears immediately, and the UI updates smoothly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for &lt;code&gt;initState()&lt;/code&gt; and Async
&lt;/h2&gt;

&lt;p&gt;Do&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep &lt;code&gt;initState()&lt;/code&gt; synchronous&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;addPostFrameCallback&lt;/code&gt; to trigger async task&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;mounted&lt;/code&gt; before calling &lt;code&gt;setState()&lt;/code&gt; after async&lt;/li&gt;
&lt;li&gt;Show loading indicators clearly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mark &lt;code&gt;initState()&lt;/code&gt; as async&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;await&lt;/code&gt; directly in &lt;code&gt;initState()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Assume your widget is always alive&lt;/li&gt;
&lt;li&gt;Run long tasks before first build&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Bonus Tip: Use &lt;code&gt;FutureBuilder&lt;/code&gt; for Stateless Data Loading
&lt;/h2&gt;

&lt;p&gt;If you’re just fetching something once and don’t need full-blown state management, &lt;code&gt;FutureBuilder&lt;/code&gt; might be simpler:&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;FutureBuilder&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="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;future:&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&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="o"&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;hasData&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;CircularProgressIndicator&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="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;children:&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="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;new&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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But be cautious &lt;code&gt;FutureBuilder&lt;/code&gt; runs the future &lt;strong&gt;every time the widget rebuilds&lt;/strong&gt; unless cached.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary: What You Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initState()&lt;/code&gt; runs before your widget is on screen don’t block it&lt;/li&gt;
&lt;li&gt;Async code should be scheduled using &lt;code&gt;addPostFrameCallback&lt;/code&gt; or similar techniques&lt;/li&gt;
&lt;li&gt;Always check &lt;code&gt;mounted&lt;/code&gt; before calling &lt;code&gt;setState()&lt;/code&gt; after &lt;code&gt;await&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Flutter lifecycle + Dart’s event loop = weird bugs unless handled correctly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Understanding &lt;code&gt;initState()&lt;/code&gt; and how async fits into the Flutter lifecycle is &lt;strong&gt;a game-changer&lt;/strong&gt;. It prevents flaky UI, unexpected errors, and improves performance.&lt;/p&gt;

&lt;p&gt;Next time your spinner doesn’t spin, or your UI feels stuck now you know where to look.&lt;/p&gt;




&lt;h2&gt;
  
  
  More Blogs Like This
&lt;/h2&gt;

&lt;p&gt;If you liked this one, check out my last post: &lt;a href="https://alaminkarno.medium.com/understanding-darts-event-loop-why-your-async-code-acts-weird-146748da0769" rel="noopener noreferrer"&gt;Understanding Dart’s Event Loop: Why Your Async Code Acts Weird&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Understanding Dart’s Event Loop: Why Your Async Code Acts Weird</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Fri, 11 Jul 2025 11:19:54 +0000</pubDate>
      <link>https://dev.to/alaminkarno/understanding-darts-event-loop-why-your-async-code-acts-weird-4hpb</link>
      <guid>https://dev.to/alaminkarno/understanding-darts-event-loop-why-your-async-code-acts-weird-4hpb</guid>
      <description>&lt;p&gt;Have you ever written asynchronous code in Flutter or Dart, and something just &lt;em&gt;didn’t behave the way you expected?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Maybe you scheduled a &lt;code&gt;Future&lt;/code&gt;, but it didn’t run immediately.&lt;br&gt;
Maybe your &lt;code&gt;setState()&lt;/code&gt; inside a &lt;code&gt;Future.delayed()&lt;/code&gt; broke the UI.&lt;br&gt;
Maybe you used &lt;code&gt;await&lt;/code&gt;, but things still executed out of order.&lt;/p&gt;

&lt;p&gt;If that sounds familiar welcome to the world of &lt;strong&gt;Dart’s Event Loop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we’ll dive deep into how Dart’s asynchronous engine works not just to fix bugs, but to reason like the Dart runtime itself.&lt;/p&gt;


&lt;h2&gt;
  
  
  What is an Event Loop?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Event Loop&lt;/strong&gt; is the core of Dart’s asynchronous execution model. It powers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Future&lt;/code&gt;s&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;async&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;UI updates&lt;/li&gt;
&lt;li&gt;Timers and animations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you write Dart code, it’s executed on a &lt;strong&gt;single thread&lt;/strong&gt;, which means asynchronous code must be carefully scheduled and executed without blocking.&lt;/p&gt;

&lt;p&gt;So how does Dart handle this?&lt;br&gt;
Using &lt;strong&gt;two queues&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dart has TWO types of queues:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Microtask Queue&lt;/strong&gt;: High priority, runs before any event tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Queue (aka Event Loop)&lt;/strong&gt;: Regular events like I/O, timer callbacks.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Execution Order: Microtasks vs Event Tasks
&lt;/h2&gt;

&lt;p&gt;Here’s the general order of execution in Dart:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Run the current synchronous code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run all microtasks&lt;/strong&gt; (until the microtask queue is empty)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run one event task&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Repeat steps 2–3&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s make it visual 👇&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;"dart:async"&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;scheduleMicrotask&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'2'&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;(()&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'3'&lt;/span&gt;&lt;span class="p"&gt;));&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;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;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;'4'&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;(()&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'5'&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;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&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="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'6'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="n"&gt;scheduleMicrotask&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'7'&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;'8'&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;What gets printed?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1
8
2
7
3
4
5
6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;print('1')&lt;/code&gt;: sync -&amp;gt; Immediately&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scheduleMicrotask(...)&lt;/code&gt;: Microtask -&amp;gt; After sync&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Future(...)&lt;/code&gt;: Event queue -&amp;gt; Later&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Future.delayed&lt;/code&gt;: Event queue -&amp;gt; Later&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;await Future.delayed(...)&lt;/code&gt;: Event queue → Suspends, resumes as microtask&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;print('8')&lt;/code&gt;: Sync -&amp;gt; Immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, &lt;strong&gt;microtasks always run before event queue tasks&lt;/strong&gt; and that causes most unexpected behavior in Flutter apps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Flutter Problem
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem: UI Not Updating After &lt;code&gt;await&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&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="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="n"&gt;_loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&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;seconds:&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;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_loading&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You click a button. The spinner is supposed to show, wait 2 seconds, then disappear.&lt;/p&gt;

&lt;p&gt;But… the UI doesn’t show the spinner during the delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix: Yield control using &lt;code&gt;Future.delayed(Duration.zero)&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&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="n"&gt;setState&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;_loading&lt;/span&gt; &lt;span class="o"&gt;=&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;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&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="c1"&gt;// give time for rebuild&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;delayed&lt;/span&gt;&lt;span class="p"&gt;(&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;seconds:&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;setState&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;_loading&lt;/span&gt; &lt;span class="o"&gt;=&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why does this work?&lt;br&gt;
Because &lt;code&gt;Future.delayed(Duration.zero)&lt;/code&gt; moves the execution into the &lt;strong&gt;event queue&lt;/strong&gt;, giving Flutter time to rebuild the UI before the delay.&lt;/p&gt;


&lt;h2&gt;
  
  
  How Dart Awaits Work (Under the Hood)
&lt;/h2&gt;

&lt;p&gt;When you &lt;code&gt;await&lt;/code&gt; a &lt;code&gt;Future&lt;/code&gt;, Dart:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Registers a callback&lt;/li&gt;
&lt;li&gt;Returns control to the event loop&lt;/li&gt;
&lt;li&gt;When the &lt;code&gt;Future&lt;/code&gt; completes, schedules that callback as a &lt;strong&gt;microtask&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, in an &lt;code&gt;async&lt;/code&gt; function, &lt;strong&gt;everything after &lt;code&gt;await&lt;/code&gt; is treated like a microtask&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Use Case: Background Sync in a Flutter App
&lt;/h2&gt;

&lt;p&gt;Imagine you’re building a task manager app. After opening the home screen, it fetches user tasks from local storage, then syncs with the server:&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;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;_loadTasks&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;_loadTasks&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;local&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;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTasks&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&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;updated&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;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;syncTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updated&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 seems fine… but users report UI Jank during startup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimized Approach&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&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="c1"&gt;// Let UI load first&lt;/span&gt;
  &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&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;_loadTasks&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;_loadTasks&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;local&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;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTasks&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Let microtasks/UI catch up&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;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;updated&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;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;syncTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updated&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;ul&gt;
&lt;li&gt;
&lt;code&gt;addPostFrameCallback&lt;/code&gt;: Defers work until after first frame&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Future.delayed(Duration.zero)&lt;/code&gt;: Gives the UI time to breathe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This respects Dart’s event loop while improving perceived performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for Dart’s Event Loop
&lt;/h2&gt;

&lt;p&gt;Do&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Future.delayed(Duration.zero)&lt;/code&gt; to yield control&lt;/li&gt;
&lt;li&gt;Schedule background work after first frame using &lt;code&gt;addPostFrameCallback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;scheduleMicrotask&lt;/code&gt; for priority work (rare)&lt;/li&gt;
&lt;li&gt;Profile UI Jank with DevTools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heavy sync logic in UI methods&lt;/li&gt;
&lt;li&gt;Calling async code in &lt;code&gt;initState()&lt;/code&gt; without care&lt;/li&gt;
&lt;li&gt;Misusing &lt;code&gt;await&lt;/code&gt; assuming it instantly pauses execution&lt;/li&gt;
&lt;li&gt;Ignoring long frame times during async ops&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Dart’s &lt;strong&gt;event loop&lt;/strong&gt; isn’t just an engine detail it shapes how your code behaves. Understanding it helps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write smoother UI updates&lt;/li&gt;
&lt;li&gt;Debug async bugs&lt;/li&gt;
&lt;li&gt;Build better architecture for real-world apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next time you &lt;code&gt;await&lt;/code&gt; doesn’t feel like it’s waiting, or your &lt;code&gt;setState()&lt;/code&gt; doesn’t fire as expected check the queues. It’s probably a microtask thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus Tip: Want to Explore Live?
&lt;/h2&gt;

&lt;p&gt;Paste this code into &lt;a href="https://dartpad.dev/" rel="noopener noreferrer"&gt;DartPad&lt;/a&gt; and tweak the order of &lt;code&gt;Future&lt;/code&gt;, &lt;code&gt;scheduleMicrotask&lt;/code&gt;, and &lt;code&gt;print&lt;/code&gt; statements to see the event loop in action.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Thought This Was Private… Again! But Freezed Had a Different Story</title>
      <dc:creator>Md. Al-Amin</dc:creator>
      <pubDate>Wed, 02 Jul 2025 12:52:51 +0000</pubDate>
      <link>https://dev.to/alaminkarno/i-thought-this-was-private-again-but-freezed-had-a-different-story-2be</link>
      <guid>https://dev.to/alaminkarno/i-thought-this-was-private-again-but-freezed-had-a-different-story-2be</guid>
      <description>&lt;p&gt;It was &lt;strong&gt;10:30 PM&lt;/strong&gt; on a typical night. Instead of winding down, I was tuned into a bi-weekly Flutter session hosted by the &lt;a href="https://www.facebook.com/groups/flutter.bangladesh" rel="noopener noreferrer"&gt;Flutter Bangladesh Group&lt;/a&gt;, led by none other than &lt;a href="https://www.linkedin.com/in/momshaddinury/" rel="noopener noreferrer"&gt;Momshad Dinury&lt;/a&gt; bhai, the group’s admin and a Senior Software Engineer at Brain Station 23.&lt;/p&gt;

&lt;p&gt;That night’s topic?&lt;br&gt;
&lt;strong&gt;Freezed package’s latest major update&lt;/strong&gt; and the breaking changes it introduced.&lt;/p&gt;

&lt;p&gt;Honestly, I was there out of curiosity. I’ve used &lt;code&gt;freezed&lt;/code&gt; in several projects, and I love how it handles immutability, union types, and &lt;code&gt;copyWith&lt;/code&gt;. But that night, I stumbled onto something I didn’t expect… again.&lt;/p&gt;


&lt;h2&gt;
  
  
  Freezed’s Major Update: What Changed?
&lt;/h2&gt;

&lt;p&gt;Momshad bhai shared that &lt;code&gt;freezed&lt;/code&gt; now &lt;strong&gt;requires a keyword&lt;/strong&gt; like &lt;code&gt;sealed&lt;/code&gt; or &lt;code&gt;abstract&lt;/code&gt; when defining classes using the &lt;code&gt;factory&lt;/code&gt; constructor.&lt;/p&gt;

&lt;p&gt;Here’s the change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;@freezed
&lt;span class="gd"&gt;-class Person with _$Person {
&lt;/span&gt;&lt;span class="gi"&gt;+abstract class Person with _$Person {
&lt;/span&gt;  const factory Person({
    required String firstName,
    required String lastName,
    required int age,
  }) = _Person;
&lt;span class="err"&gt;
&lt;/span&gt;  factory Person.fromJson(Map&amp;lt;String, Object?&amp;gt; json)
      =&amp;gt; _$PersonFromJson(json);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or for union types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;@freezed
&lt;span class="gd"&gt;-class Model with _$Model {
&lt;/span&gt;&lt;span class="gi"&gt;+sealed class Model with _$Model {
&lt;/span&gt;  factory Model.first(String a) = First;
  factory Model.second(int b, bool c) = Second;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This migration was required because of changes in Dart itself. But as I followed the example, something caught my attention:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;@freezed
&lt;span class="p"&gt;abstract class Person with _$Person {
&lt;/span&gt;  const factory Person({
    required String firstName,
    required String lastName,
    required int age,
  }) = _Person;
&lt;span class="err"&gt;
&lt;/span&gt;  factory Person.fromJson(Map&amp;lt;String, Object?&amp;gt; json) =&amp;gt; _$PersonFromJson(json);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait… &lt;code&gt;_Person&lt;/code&gt; and &lt;code&gt;_$PersonFromJson&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;They start with an underscore aren’t those &lt;strong&gt;private&lt;/strong&gt;?&lt;br&gt;
Yet, they’re accessible right here in the same file.&lt;/p&gt;

&lt;p&gt;My mind instantly went back to something I had written not long ago: &lt;a href="https://alaminkarno.medium.com/i-thought-this-was-private-until-dart-surprised-me-958ab59bc24f" rel="noopener noreferrer"&gt;I Thought This Was Private… Until Dart Surprised Me&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Dart Privacy Refresher: Underscore Isn’t File-Private
&lt;/h2&gt;

&lt;p&gt;If you’ve used Dart for a while, you know that prefixing a class or variable with &lt;code&gt;_&lt;/code&gt; makes it &lt;strong&gt;private&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But what kind of private?&lt;/p&gt;

&lt;p&gt;Here’s the twist: &lt;strong&gt;Dart’s privacy is scoped to the &lt;em&gt;library&lt;/em&gt;, not the file&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means as long as your files are part of the same library, they can share each other’s private members.&lt;/p&gt;


&lt;h2&gt;
  
  
  Enter &lt;code&gt;part&lt;/code&gt; and &lt;code&gt;part of&lt;/code&gt;: The Glue Behind Code Generation
&lt;/h2&gt;

&lt;p&gt;Here’s how this works behind the scenes:&lt;/p&gt;

&lt;p&gt;In your Dart file (&lt;code&gt;person.dart&lt;/code&gt;), you’ll see:&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;part&lt;/span&gt; &lt;span class="s"&gt;'person.freezed.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;'person.g.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside those generated files (like &lt;code&gt;person.freezed.dart&lt;/code&gt;), you’ll find:&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;part of&lt;/span&gt; &lt;span class="s"&gt;'person.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Dart: “Hey, all these files are part of the same library.”&lt;br&gt;
So suddenly, &lt;code&gt;_Person&lt;/code&gt; and &lt;code&gt;_$PersonFromJson&lt;/code&gt;, even though private by naming convention, are &lt;strong&gt;accessible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Mind = Blown&lt;/p&gt;


&lt;h2&gt;
  
  
  What Are &lt;code&gt;_$Person&lt;/code&gt; and &lt;code&gt;_Person&lt;/code&gt; Anyway?
&lt;/h2&gt;

&lt;p&gt;Here’s the breakdown of what &lt;code&gt;freezed&lt;/code&gt; generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;_$Person&lt;/code&gt;&lt;/strong&gt;: A &lt;strong&gt;mixin or helper&lt;/strong&gt; generated by &lt;code&gt;freezed&lt;/code&gt;, giving you the &lt;code&gt;.copyWith()&lt;/code&gt;, &lt;code&gt;.toString()&lt;/code&gt;, &lt;code&gt;.hashCode&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;_Person&lt;/code&gt;&lt;/strong&gt;: The actual &lt;strong&gt;implementation class&lt;/strong&gt; behind your factory constructor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;_$PersonFromJson()&lt;/code&gt;&lt;/strong&gt;: A function created by &lt;code&gt;json_serializable&lt;/code&gt; to handle deserialization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this becomes possible due to part and part of.&lt;/p&gt;


&lt;h2&gt;
  
  
  New Dart Feature: Pattern Matching
&lt;/h2&gt;

&lt;p&gt;One more thing I learned from that session: &lt;code&gt;freezed&lt;/code&gt; no longer generates &lt;code&gt;map()&lt;/code&gt; or &lt;code&gt;when()&lt;/code&gt; methods for pattern matching.&lt;/p&gt;

&lt;p&gt;Now, you’re expected to use Dart’s &lt;strong&gt;native pattern matching&lt;/strong&gt; syntax. For example:&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;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'42'&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;result&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;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;First&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;a&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="s"&gt;'first &lt;/span&gt;&lt;span class="si"&gt;$a&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;Second&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;b&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;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="s"&gt;'second &lt;/span&gt;&lt;span class="si"&gt;$b&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;$c&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;No more &lt;code&gt;model.map()&lt;/code&gt; magic. This aligns better with modern Dart, but it also means we must adjust our habits.&lt;/p&gt;




&lt;h2&gt;
  
  
  But why &lt;code&gt;abstract&lt;/code&gt; or &lt;code&gt;sealed&lt;/code&gt; Now?
&lt;/h2&gt;

&lt;p&gt;This was a change enforced by Dart itself.&lt;/p&gt;

&lt;p&gt;When using &lt;code&gt;factory&lt;/code&gt; constructors, Dart now expects you to mark the class as either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;abstract&lt;/code&gt;: Meaning it cannot be instantiated directly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sealed&lt;/code&gt;: Meaning it can only be subclassed within the same file great for union types and pattern matching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, this change is less about &lt;code&gt;freezed&lt;/code&gt; itself and more about keeping up with Dart’s stricter and cleaner type system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Here’s what I walked away with from a 10:30 PM session that was supposed to be “just another meetup”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dart’s privacy is &lt;strong&gt;library-scoped&lt;/strong&gt;, not file-scoped.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;part&lt;/code&gt; and &lt;code&gt;part of&lt;/code&gt; allow generated code to &lt;strong&gt;access and expose private classes&lt;/strong&gt; across files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;freezed&lt;/code&gt;’s latest update aligns with &lt;strong&gt;modern Dart features&lt;/strong&gt; like &lt;code&gt;sealed&lt;/code&gt;, &lt;code&gt;abstract&lt;/code&gt;, and native pattern matching.&lt;/li&gt;
&lt;li&gt;Code generation feels magical but understanding the &lt;strong&gt;“why”&lt;/strong&gt; behind the magic makes you a better dev.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;It’s wild how small moments like a late-night tech session can lead to big learning.&lt;/p&gt;

&lt;p&gt;What looked like an accessibility bug (&lt;code&gt;_Person&lt;/code&gt;, &lt;code&gt;_$Person&lt;/code&gt;) was actually a deep Dart feature.&lt;br&gt;
And what felt like a confusing migration (&lt;code&gt;sealed&lt;/code&gt;, &lt;code&gt;abstract&lt;/code&gt;) was Dart evolving into a cleaner, more expressive language.&lt;/p&gt;

&lt;p&gt;Big thanks to &lt;strong&gt;Momshad Dinury&lt;/strong&gt; bhai and the &lt;strong&gt;Flutter Bangladesh Group&lt;/strong&gt; for organizing these sessions.&lt;/p&gt;

&lt;p&gt;They don’t just teach you tools they lead you into deeper understanding.&lt;/p&gt;

&lt;p&gt;If you liked this, you might also enjoy my earlier post: &lt;a href="https://alaminkarno.medium.com/i-thought-this-was-private-until-dart-surprised-me-958ab59bc24f" rel="noopener noreferrer"&gt;I Thought This Was Private… Until Dart Surprised Me&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was this helpful? Drop a comment or share your own “Dart surprise” moment I’d love to hear it.&lt;/strong&gt;&lt;br&gt;
Happy Fluttering!&lt;/p&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>encapsulation</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
