<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Daniel Asaboro - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Daniel Asaboro - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 16:29:53 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/DanielAsaboro/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ What is Primitive Obsession? ]]>
                </title>
                <description>
                    <![CDATA[ In a statically-typed language like Java, C#, Go, Typescript, or even Dart, how do you represent an email address? Okay, what about a password? A phone number? Zip code? Home address? Alright, what about a unique ID? If your answer to all or most of ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-primitive-obsession/</link>
                <guid isPermaLink="false">66ba189cb80b6ce06c3fdf1b</guid>
                
                    <category>
                        <![CDATA[ clean code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Asaboro ]]>
                </dc:creator>
                <pubDate>Tue, 23 Jul 2024 22:03:40 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/Screenshot-2024-07-23-at-08.25.18.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In a statically-typed language like Java, C#, Go, Typescript, or even Dart, how do you represent an email address? Okay, what about a password? A phone number? Zip code? Home address? Alright, what about a unique ID?</p>
<p>If your answer to all or most of these is a string, then you are suffering from what Software Development experts call "<strong>Primitive Obsession</strong>".</p>
<p>While primitives like <code>int</code>, <code>char</code>, <code>byte</code>, <code>short</code>, <code>long</code>, <code>float</code>, <code>double</code>, <code>boolean</code>, <code>strings</code>, and so on are the basic built-in building blocks of any programming language, Primitive Obsession is when your code relies too much on them to represent otherwise more complex concepts.</p>
<p>Experts say it's a code smell. This is because Primitive Obsession can lead to a range of problems including lack of validation, poor readability, code duplication, and difficulty in refactoring. This article will help you resolve this issue.</p>
<h2 id="heading-what-youll-learn-in-this-article">What You'll Learn in this Article</h2>
<p>This tutorial, while quite short, is designed to help you understand and address the issue of primitive obsession in your code. So when you are done reading, you should be able to:</p>
<ol>
<li><p>Identify what's so bad about Primitive Obsession and associated drawbacks.</p>
</li>
<li><p>Design better software and implement data structures that enhance code correctness, maintainability, and future-proof your software against potential issues.</p>
</li>
<li><p>Improve Data Safety, and Security, and reduce the likelihood of runtime errors and unexpected behavior by using more robust data representations.</p>
</li>
</ol>
<h2 id="heading-prerequisites-what-you-need-to-know">Prerequisites: What You Need to Know</h2>
<ol>
<li><p>Familiarity with OOP principles like classes, objects, encapsulation, inheritance, and polymorphism will be beneficial when designing custom data structures.</p>
</li>
<li><p>Proficiency in statically-typed languages like Java, C#, Go, TypeScript, and Dart where variable types are declared at compile time.</p>
</li>
</ol>
<p>It's fine if you're not familiar with these concepts – there are many resources available online and in textbooks to help you get started. But this tutorial assumes a basic grasp of these foundations and builds upon them to address the issue of primitive obsession.</p>
<h2 id="heading-whats-bad-about-primitive-obsession">What's Bad about Primitive Obsession?</h2>
<p>Primitive obsession is problematic for several reasons:</p>
<ol>
<li><p>Weak type checking</p>
</li>
<li><p>Limited built-in functionality for specific data types</p>
</li>
<li><p>Reduced maintainability and loss of domain knowledge</p>
</li>
</ol>
<p>Let's take a look at each issue in detail:</p>
<h3 id="heading-1-primitives-offer-weak-type-checking">1. Primitives offer weak type checking</h3>
<p>Beyond efficient memory allocation, or wasteful compute time in deciphering what type a value is, one of the benefits of statically type language is that it lets the compiler help you catch potential type mismatches at compile time. This can help prevent runtime errors caused by using the wrong data type.</p>
<p>The compiler would scream at you if you did this:</p>
<pre><code class="lang-dart"><span class="hljs-built_in">String</span> phoneNumber = <span class="hljs-string">"+1-555-229-1234"</span>;
<span class="hljs-built_in">int</span> zipCode = <span class="hljs-number">101257</span>;

zipCode = phoneNumber; <span class="hljs-comment">// throws an error</span>
</code></pre>
<p>What if you did this?</p>
<pre><code class="lang-dart"><span class="hljs-built_in">String</span> phoneNumber = <span class="hljs-string">"+1-555-229-1234"</span>;
<span class="hljs-built_in">String</span> zipCode = <span class="hljs-string">"1000 AP"</span>

zipCode = phoneNumber; <span class="hljs-comment">//works fine</span>
</code></pre>
<p>It will compile and do nothing!</p>
<p>Why?</p>
<p><strong>Because a string will always be a string.</strong></p>
<p>The compiler cannot differentiate whether the string value you pass represents a user password or an email address. Consequently, it cannot prevent you from mistakenly assigning a variable to the wrong field leading to runtime errors and unexpected behaviour.</p>
<h3 id="heading-2-limited-built-in-functionality-for-specific-data-types">2. Limited built-in functionality for specific data types.</h3>
<p>When you rely heavily on primitives, you often need to write additional code to perform tasks such as email address validation or phone number formatting. And that's not a problem really.</p>
<p>The real problem is the repetitive and scattered nature of these implementations. This doesn't just increase the risk of errors but it also makes the code harder to maintain and refactor.</p>
<h3 id="heading-3-reduced-maintainability-and-loss-of-domain-knowledge">3. Reduced Maintainability and Loss of Domain Knowledge</h3>
<p>Scattering business rules and logic across the codebase makes it harder to maintain and evolve the software. When primitives are used extensively, it can be challenging to understand the purpose and constraints of the data they represent.</p>
<p>Encapsulating this logic within dedicated types helps preserve domain knowledge and makes the code more intuitive for future developers.</p>
<p>For instance, you might need to block certain email addresses due to fraud or restrict specific zip codes, areas, or companies. When you use primitives, this important context is not evident, leading to potential errors and increased maintenance burden.</p>
<h2 id="heading-how-to-break-away-from-primitive-obsession">How to Break Away from Primitive Obsession</h2>
<h3 id="heading-1-watch-out-for-variables-with-special-validation-logic">1. Watch out for variables with special validation logic.</h3>
<p>Take Phone Numbers, for example. In Nigeria, where I'm from, all phone numbers are eleven digits long and start with a zero. The second digit is usually a "<em>7</em>" , "<em>8</em>", or "9" which helps identify the telecom operator. Any other number is invalid.</p>
<p>The same can be said for a Website URL. It could very well be represented by a string. But a URL has more information and specific properties compared to a string (for example, the scheme, query parameters, and protocol). And each part has a different validation method.</p>
<h3 id="heading-2-watch-out-for-variables-with-special-comparison-rules">2. Watch out for variables with special comparison rules</h3>
<p>Beyond simple equality checks (==), if you find yourself implementing custom comparison logic for a variable without encapsulating it in a class, it's a red flag.</p>
<p>For instance, comparing email addresses often involves more than a simple equality check. You might need to perform case-insensitive matching, strip out whitespace, or handle variations in formatting to accurately identify matching emails such as + sign in email addresses for people who want to game your ssytem.</p>
<p>daniel@example.com is the same email address as daniel+dev@example.com. Emails sent to the latter will be received on the former.</p>
<h3 id="heading-3-watch-out-for-variables-with-special-formatting-rules">3. Watch out for variables with special formatting rules</h3>
<p>Any variable that requires specific formatting or parsing to extract meaningful information is a candidate for a richer representation.</p>
<p><strong>Currency values</strong> are a good example of this. Different regions use different formatting rules – some use commas and some use decimal points. It's the same case with a date of birth or anything that requires some data.</p>
<p>Identifying and resolving primitive obsession is a continuous process.</p>
<p>You won't be able to catch all instances at once, especially those that are uncommon and unique to your business domain. That's perfectly fine – that's how it should be.</p>
<p>Instead of panicking or giving up, gradually refactor your code to incorporate these changes. This approach is key to making your code future-proof and maintainable.</p>
<h2 id="heading-you-dont-see-it-until-you-are-shown">You Don't See it Until You Are Shown</h2>
<p>Primitive obsession hides in plain sight. And that's a significant issue because, unlike static errors, linters won't flag them and neither will your program crash as a result.</p>
<p>It often takes a mentor, a code review, or an "aha" moment to realize that these repetitive checks and rules scattered throughout your codebase can be centralized into their own dedicated types. This recognition is the first step toward more robust, readable, and maintainable code.</p>
<p>So stay open-minded, engage with others, read code from different sources, and contribute to open-source projects. That's how you learn. Ultimately, it's all about one thing: encapsulation – grouping data and the methods that operate on it in one location for maintainability.</p>
<h2 id="heading-credits-and-resources">Credits and resources:</h2>
<ol>
<li><p><a target="_blank" href="https://medium.com/the-sixt-india-blog/primitive-obsession-code-smell-that-hurt-people-the-most-5cbdd70496e9">A Code Smell that Hurts People the Most</a> by Arpit Jain.</p>
</li>
<li><p><a target="_blank" href="https://refactoring.guru/smells/primitive-obsession">Signs and Symptoms of Primitive Obsession</a> by Refactoring Guru.</p>
</li>
<li><p><a target="_blank" href="https://hackernoon.com/what-is-primitive-obsession-and-how-can-we-fix-it-wh2f33ki">How to Fix Primitive Obsession</a> by Sam Walpole on Hackernoon.</p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Enhanced Enums in Dart – Explained With Code Examples ]]>
                </title>
                <description>
                    <![CDATA[ Enums are one of the most efficient ways to represent a fixed set of values. For example: days of the week, user online status, traffic light states, role hierarchy in an organization, and so on. What's interesting is that most typed languages such a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-enhanced-enums-in-dart/</link>
                <guid isPermaLink="false">66ba18991b08c4f4645ed9e3</guid>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Asaboro ]]>
                </dc:creator>
                <pubDate>Mon, 22 Jul 2024 11:52:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/CleanShot-2024-07-18-at-21.42.54@2x.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Enums are one of the most efficient ways to represent a fixed set of values. For example: days of the week, user online status, traffic light states, role hierarchy in an organization, and so on.</p>
<p>What's interesting is that most typed languages such as Typescript, Java, C#, and Dart give you additional features such as iterating over the enum's content, and calling you to attention to uncatered or misspelt cases.</p>
<p>However, if this is the only way you're using enums in Dart, you're missing out on a lot of functionality introduced in Dart 2.17 in 2022. So I'll show you how to unlock and leverage these advanced features in this article.</p>
<h2 id="heading-what-you-will-learn">What You Will Learn</h2>
<p>This article dives deep into the world of enhanced enums. We'll explore their capabilities, benefits, when to and when not to use them.</p>
<p>By the time you finish reading, I hope you'll gain valuable insights into:</p>
<ol>
<li>Writing clean and expressive code that's almost self-documenting, as the meaning and functionality of each option are readily apparent.</li>
<li>Improving code readability and maintainability by keeping related data and behavior together to simplify future modifications and reduce the risk of introducing errors.</li>
</ol>
<h2 id="heading-prerequisite-what-you-should-already-know">Prerequisite: What You Should Already Know</h2>
<ol>
<li><strong>Introductory knowledge of the Dart Language:</strong> Understanding the basics of Dart, including syntax, data types, and control structures.</li>
<li><strong>Basic Understanding of Enums:</strong> Familiarity with what enums are and how they are typically used to represent fixed sets of values.</li>
<li><strong>Object-Oriented Programming (OOP) Concepts:</strong> Knowledge of classes, objects, inheritance, and polymorphism in Dart or another programming language.</li>
</ol>
<p>That said, lets get into it.</p>
<h2 id="heading-table-of-content">Table of Content</h2>
<ol>
<li><a class="post-section-overview" href="#heading-enhanced-enums-the-game-changer">Enhanced Enums: The Game Changer</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-enhanced-enums">How to Use Enhanced Enums</a></li>
<li><a class="post-section-overview" href="#heading-1-enhanced-enums-with-a-custom-operator">Enhanced Enums with a Custom Operator</a></li>
<li><a class="post-section-overview" href="#heading-2-enhanced-enums-with-extensions">Enhanced Enums with Extensions</a></li>
<li><a class="post-section-overview" href="#heading-3-enhanced-enums-with-mixins">Enhanced Enums with Mixins</a></li>
<li><a class="post-section-overview" href="#heading-not-all-constants-have-to-be-enums">Not All Constants Have To Be Enums</a></li>
<li><a class="post-section-overview" href="#heading-bonus-tip-to-using-enums">Bonus Tip to Using Enums</a></li>
<li><a class="post-section-overview" href="#heading-closing-remark">Closing Remark</a></li>
<li><a class="post-section-overview" href="#heading-persisting-enums-best-practices">Persisting Enums: Best Practices</a></li>
<li><a class="post-section-overview" href="#heading-credits-and-resources">Credits and Resources:</a></li>
</ol>
<h2 id="heading-enhanced-enums-the-game-changer">Enhanced Enums: The Game Changer</h2>
<p>Say we're building a to-do list app. We might use a traditional enum to represent task priorities:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Priority { low, medium, high }
</code></pre>
<p>A quick one for those who don't know:  They are called enums, because they are short for enumerations. </p>
<p>According to <a target="_blank" href="https://en.wikipedia.org/wiki/Enumeration#:~:text=An%20enumeration%20is%20a%20complete,the%20elements%20of%20a%20set.">Wikipedia</a>: An enumeration is a complete, ordered listing of all the items in a collection. </p>
<p>Each constant within the enum (low, medium, high, and so on) is implicitly assigned an index starting from zero so they can be iterated through like a list/iterable.</p>
<p>This works well, but it only stores the basic names. What if we want to associate a color with each priority for visual cues, or a description? Or making a specific action to be triggered by each priority? Traditional enums can't handle that.</p>
<p>Let's say you actually do, you'd have to do some complex dance to make it "work".</p>
<h2 id="heading-how-to-use-enhanced-enums">How to Use Enhanced Enums</h2>
<p>They allow you to attach additional information, methods, and properties to each enum option. This means that each value in the enum can have its own unique behavior or data associated with it.</p>
<p>For example, let's say you want to add a shortened abbreviation for each day of the week. Instead of using Extension methods and all, here's how you'd do it with enhanced enums:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Day {
  monday(<span class="hljs-string">"Mon"</span>),
  tuesday(<span class="hljs-string">"Tue"</span>),
  wednesday(<span class="hljs-string">"Wed"</span>),
  thursday(<span class="hljs-string">"Thu"</span>),
  friday(<span class="hljs-string">"Fri"</span>),
  saturday(<span class="hljs-string">"Sat"</span>),
  sunday(<span class="hljs-string">"Sun"</span>);

  <span class="hljs-keyword">const</span> Day(<span class="hljs-keyword">this</span>.abbreviation);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> abbreviation;
}
</code></pre>
<p>Let me explain what's happening above.</p>
<p>Unlike normal enums, enhanced enums have custom constructors and you can pass any value to it as long as it's final. It has to be final because enums don't change.</p>
<p>Here's an example:</p>
<pre><code class="lang-dart"><span class="hljs-comment">// preceeding code removed for brevity</span>

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Example usage</span>
  Day today = Day.monday;
  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Today is <span class="hljs-subst">${today.name}</span> (<span class="hljs-subst">${today.abbreviation}</span>)'</span>); 
  <span class="hljs-comment">// Output: Today is monday (Mon)</span>
}
</code></pre>
<p>You can recreate the enum above this way: </p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Priority {
  low(color: Color.green),
  medium(color: Color.yellow),
  high(color: Color.red),
  ;

  <span class="hljs-keyword">final</span> Color color;

  <span class="hljs-keyword">const</span> Priority(<span class="hljs-keyword">this</span>.color);
}
</code></pre>
<pre><code class="lang-dart">Priority highPriority = Priority.high;
<span class="hljs-built_in">print</span>(highPriority.color); <span class="hljs-comment">// Prints Color.red</span>
</code></pre>
<p>This makes your code more powerful and expressive as data and behavior are bundled together to keep things organized and easy to understand.</p>
<p>Another example I saw in the wild is from the Flutter team. It was an explanatory video for using Flutter and Dart to build a Game for a Raspberry Pi. It's a simple enum that makes working with GPIO pins smooth, intuitive, and less prone to error.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-46.png" alt="Screenshot of a video from Flutter Youtube Channel on using Enhanced Enums" width="600" height="400" loading="lazy">
<em>Flutter, Dart, and RAspberry Pi video from the Flutter team</em></p>
<p>Here's the code for those interested:</p>
<pre><code class="lang-dart">
<span class="hljs-keyword">enum</span> GameHatGPIO {
  SELECT_BUTTON(<span class="hljs-number">7</span>, GameControlEvent.SELECT),
  TL_BUTTON(<span class="hljs-number">12</span>, GameControlEvent.LEFT_SHOULDER),
  TR_BUTTON(<span class="hljs-number">16</span>, GameControlEvent.RIGHT_SHOULDER),
  DPAD_UP_BUTTON(<span class="hljs-number">29</span>, GameControlEvent.UP),
  DPAD_DOWN_BUTTON(<span class="hljs-number">31</span>, GameControlEvent.DOWN),
  DPAD_LEFT_BUTTON(<span class="hljs-number">33</span>, GameControlEvent.LEFT),
  DPAD_RIGHT_BUTTON(<span class="hljs-number">35</span>, GameControlEvent.RIGHT),
  B_BUTTON(<span class="hljs-number">32</span>, GameControlEvent.B),
  X_BUTTON(<span class="hljs-number">36</span>, GameControlEvent.X);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> pin;
  <span class="hljs-keyword">final</span> GameControlEvent event;

  <span class="hljs-keyword">const</span> GameHatGPIO(<span class="hljs-keyword">this</span>.pin, <span class="hljs-keyword">this</span>.event);
}
</code></pre>
<p>Next, let's move on to some advance use cases.</p>
<p>The trick to understanding the section that follows is realizing that enums are literally classes, constant ones (all fields must be final and the constructor must be a constant) – albeit a special one. </p>
<p>What does this mean?</p>
<p>It means most things you can do with a class can be done with an enum. For example, it means you can:</p>
<ol>
<li>Override a custom operator.</li>
<li>Add addition methods and properties with an extension.</li>
<li>Use them with a mixin, and much more.</li>
</ol>
<p>Let's talk about some of them in detail.</p>
<h3 id="heading-1-enhanced-enums-with-a-custom-operator">1. Enhanced Enums with a Custom Operator</h3>
<p>Let's say you are designing the billing service for a mobile app, and somewhere in the app, you want to have a custom defined enum for months in the year like this:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Month {
  January(<span class="hljs-string">"Jan"</span>),
  February(<span class="hljs-string">"Feb"</span>),
  ...,
  December(<span class="hljs-string">"Dec"</span>);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> abbreviation;

  <span class="hljs-keyword">const</span> Month(<span class="hljs-keyword">this</span>.abbreviation);
}
</code></pre>
<p>With enhanced enums, you can overload an operator to change the normal logic of the language. For instance, you can overload the <code>+</code> operator to add months to a <code>Month</code> enum like this:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Month {
  January(<span class="hljs-string">"Jan"</span>),
  February(<span class="hljs-string">"Feb"</span>),
  March(<span class="hljs-string">"Mar"</span>),
  April(<span class="hljs-string">"Apr"</span>),
  May(<span class="hljs-string">"May"</span>),
  June(<span class="hljs-string">"Jun"</span>),
  July(<span class="hljs-string">"Jul"</span>),
  August(<span class="hljs-string">"Aug"</span>),
  September(<span class="hljs-string">"Sep"</span>),
  October(<span class="hljs-string">"Oct"</span>),
  November(<span class="hljs-string">"Nov"</span>),
  December(<span class="hljs-string">"Dec"</span>);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> abbreviation;

  <span class="hljs-keyword">const</span> Month(<span class="hljs-keyword">this</span>.abbreviation);

    Month <span class="hljs-keyword">operator</span> +(<span class="hljs-built_in">int</span> other) {
    <span class="hljs-comment">// Ensure the result stays within 0-11 range</span>
    <span class="hljs-built_in">int</span> result = (<span class="hljs-keyword">this</span>.index + other) % <span class="hljs-number">12</span>; 
    <span class="hljs-keyword">return</span> Month.values[result];
  }
}


<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Example usage</span>
  Month currentMonth = Month.January;
  Month nextMonth = currentMonth + <span class="hljs-number">1</span>;
  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Current month: <span class="hljs-subst">${currentMonth.name}</span>     (<span class="hljs-subst">${currentMonth.abbreviation}</span>)'</span>); 
  <span class="hljs-comment">// Output: Current month: January (Jan)</span>
  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Next month: <span class="hljs-subst">${nextMonth.name}</span> (<span class="hljs-subst">${nextMonth.abbreviation}</span>)'</span>);     
  <span class="hljs-comment">// Output: Next month: February (Feb)</span>

}
</code></pre>
<p>What if you need to compare priority degree so that some tasks are ranked over others in your to-do app? Here's how you'd go about:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Priority {
  low,
  medium,
  high,
}

<span class="hljs-keyword">extension</span> PriorityOperations <span class="hljs-keyword">on</span> Priority {
  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">operator</span> &lt;(Priority other) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.index &lt; other.index;
  }

  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">operator</span> &gt;(Priority other) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.index &gt; other.index;
  }
}

<span class="hljs-keyword">void</span> main() {
  Priority currentPriority = Priority.medium;
  <span class="hljs-keyword">if</span> (currentPriority &gt; Priority.low) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Priority is higher than low.'</span>);
  }
}
</code></pre>
<p>What about file permission access to a group of people?</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> AccessFlag { READ, WRITE, EXECUTE }

<span class="hljs-keyword">extension</span> AccessFlagExtension <span class="hljs-keyword">on</span> AccessFlag {
  AccessFlag <span class="hljs-keyword">operator</span> &amp;(AccessFlag other) {
    <span class="hljs-keyword">return</span> AccessFlag.values[<span class="hljs-keyword">this</span>.index | other.index];
  }
}
</code></pre>
<p>You get the point, right? Your imagination is really the limit.</p>
<h3 id="heading-2-enhanced-enums-with-extensions">2. Enhanced Enums with Extensions</h3>
<p>Sometimes, a library exposes an enum. You can add your own custom methods:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> LogLevel {
  DEBUG(<span class="hljs-string">"[DEBUG]"</span>),
  INFO(<span class="hljs-string">"[INFO]"</span>),
  WARN(<span class="hljs-string">"[WARN]"</span>),
  ERROR(<span class="hljs-string">"[ERROR]"</span>);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> label;

  <span class="hljs-keyword">const</span> LogLevel(<span class="hljs-keyword">this</span>.label);
}

<span class="hljs-keyword">extension</span> LogLevelExtension <span class="hljs-keyword">on</span> LogLevel {
  <span class="hljs-built_in">String</span> formattedString(<span class="hljs-built_in">String</span> error) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"<span class="hljs-subst">${<span class="hljs-keyword">this</span>.label}</span> <span class="hljs-subst">$error</span>"</span>;

  }
}

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-built_in">print</span>(LogLevel.WARN.formattedString(<span class="hljs-string">"you can't override toString method in an extension"</span>)); 
  <span class="hljs-comment">// Prints "[WARN] you can't override toString method in an extension"</span>
}
</code></pre>
<p>Another example for a game of cards</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> PlayingCardSuit { HEARTS, SPADES, DIAMONDS, CLUBS }

<span class="hljs-keyword">extension</span> PlayingCardSuitExtension <span class="hljs-keyword">on</span> PlayingCardSuit {
  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">operator</span> &gt;(PlayingCardSuit other) =&gt; 
      <span class="hljs-keyword">this</span>.index &gt; other.index;
  <span class="hljs-comment">// ... similar overloading for other comparison operators</span>
}

<span class="hljs-keyword">void</span> main() {
  PlayingCardSuit suit1 = PlayingCardSuit.SPADES;
  PlayingCardSuit suit2 = PlayingCardSuit.DIAMONDS;
  <span class="hljs-built_in">print</span>(suit1 &gt; suit2); <span class="hljs-comment">// Prints true (Spades rank higher than Diamonds)</span>
}
</code></pre>
<h3 id="heading-3-enhanced-enums-with-mixins">3. Enhanced Enums with Mixins</h3>
<p>If you want shared functionality across enums, you should consider using mixins. This can be particularly useful for common behaviors like serialization or validation.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">mixin</span> Loggable {
  <span class="hljs-built_in">String</span> getLogMessage();
}

<span class="hljs-keyword">enum</span> OrderStatus <span class="hljs-keyword">with</span> Loggable {
  CREATED(<span class="hljs-string">"Order Created"</span>),
  SHIPPED(<span class="hljs-string">"Order Shipped"</span>),
  DELIVERED(<span class="hljs-string">"Order Delivered"</span>);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> message;

  <span class="hljs-keyword">const</span> OrderStatus(<span class="hljs-keyword">this</span>.message);

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">String</span> getLogMessage() =&gt; <span class="hljs-string">"Order status changed to <span class="hljs-subst">$message</span>"</span>;
}

    <span class="hljs-comment">//another class can implement this</span>

main(){
    OrderStatus newStatus = OrderStatus.SHIPPED;
    <span class="hljs-built_in">String</span> logMessage = newStatus.getLogMessage();
    <span class="hljs-built_in">print</span>(logMessage); <span class="hljs-comment">// Output: Order status changed to Shipped</span>
}
</code></pre>
<p><strong>Note</strong>: Although they can both be used to add functionality to existing classes (including enums), mixins and extensions serve complementary purposes in Dart:</p>
<p>Mixins are used to share functionality among different classes or enums, while extensions are used to add new functionalities to specific existing types.   </p>
<p>Both provide ways to enhance code reusability, readability, and maintainability in Dart programming. Mixins can access private members of the class they are mixed into, while extensions cannot access private members of the extended type.</p>
<h3 id="heading-not-all-constants-have-to-be-enums">Not All Constants Have To Be Enums</h3>
<p>It's essential to understand that not all constants need to be represented as enums.</p>
<p>Misusing enums can lead to less readable and maintainable code. Enums should be reserved for related, fixed sets of values, ensuring clarity and logical grouping.</p>
<p>For example, this is not a great way to use enums:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Basic {
  font,
  weight,
  size,
}
</code></pre>
<p>In this case, the <code>Basic</code> enum groups together <code>font</code>, <code>weight</code>, and <code>size</code>. While these constants are related in a broad sense, they don't necessarily represent a fixed set of values that benefit from being grouped as an enum.</p>
<p>It's the same situation with the following example:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> Colors {
  red,
  blue,
  green,
  hexValue,
}
</code></pre>
<p>In this case, the <code>Colors</code> enum groups together <code>red</code>, <code>blue</code>, <code>green</code>, and <code>hexValue</code>.</p>
<p>While <code>red</code>, <code>blue</code>, and <code>green</code> are indeed colors, <code>hexValue</code> don't fit well into this set. Using enums inappropriately can lead to confusion and make the code harder to understand and maintain. The best way to use enums is when you have a closed set of related constants that are inherently tied together.</p>
<h3 id="heading-bonus-tip-to-using-enums">Bonus Tip to Using Enums:</h3>
<ol>
<li>Use PascalCase for enum names and camelCase for enum values.</li>
<li>Iterate over enum values using the built-in <code>values</code> property.</li>
<li>Instance variables must be immutable, including those added by mixins.</li>
<li>All generative constructors must be constant.</li>
<li>Factory constructors can only return one of the predefined, fixed enum instances.</li>
<li>No other class can be inherited from, as an enum is automatically inherited.</li>
<li>Overrides for index, hashCode, and the equality operator (==) are not allowed.</li>
<li>A member named value cannot be declared in an enum, as it would conflict with the automatically generated static values getter.</li>
<li>All instances of the enum must be declared at the start of the declaration, and at least one instance must be declared.</li>
<li>Instance methods in an enhanced enum can use <code>this</code> to reference the current enum value.</li>
</ol>
<h2 id="heading-closing-remark">Closing Remark</h2>
<p>As I've extensively shown, or hoped to, enhanced enums make your code cleaner and more expressive by allowing us to bundle related data and behavior together. Instead of scattering information about your enum options throughout our code, you can encapsulate it directly within the enum itself.</p>
<p>Typically, a blog post about enums or even Enhanced enums wouldn't be explain everything. It's just about understanding the basics and some extensions. </p>
<p>However, what I find truly interesting is that the concept doesn't fully resonate until you see concrete examples and experiment with them in your own work. And that's precisely what I've aimed to achieve here: to demonstrate the power and flexibility of enhanced enums through practical examples.</p>
<p>It's all limited by your imagination.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/MatanLurey/status/1797736320456102178"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-persisting-enums-best-practices">Persisting Enums: Best Practices</h3>
<p>A crucial thing I'd like to add is to never persist enums directly to storage. If you need to store enum values in a database or a file, always map and store them as strings. This approach ensures clarity and reduces the risk of mixing things up unintentionally. </p>
<p>When loading the data, you can map the strings back to the corresponding enum values. Avoid using integers for this purpose, as they can easily lead to confusion and errors. Using strings makes your code more robust and easier to maintain, as it directly represents the enum values.</p>
<p>Here's a simple way I deserialize strings to enums in my codebase:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">mixin</span> Common {
  <span class="hljs-built_in">bool</span> isActive();
}

<span class="hljs-keyword">enum</span> SubscriptionPlan <span class="hljs-keyword">with</span> Common {
  free(<span class="hljs-string">"Free Plan"</span>),
  basic(<span class="hljs-string">"Basic Plan"</span>),
  premium(<span class="hljs-string">"Premium Plan"</span>);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> description;

  <span class="hljs-keyword">const</span> SubscriptionPlan(<span class="hljs-keyword">this</span>.description);

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">bool</span> isActive() {
  <span class="hljs-comment">//custom method here</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
  }


  <span class="hljs-comment">// use a factory method so you don't have to rely</span>
  <span class="hljs-comment">// on creating an instance of SubscriptionPlan</span>
  <span class="hljs-keyword">factory</span> SubscriptionPlan.extractFrom(<span class="hljs-built_in">String</span> json) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">return</span> SubscriptionPlan.values.byName(json);
    } <span class="hljs-keyword">catch</span> (e, s) {
      <span class="hljs-comment">// Could even be a custom error</span>
      <span class="hljs-keyword">throw</span> Error.throwWithStackTrace(e, s);
    }
  }
}

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// [1] Test cases for extractFrom</span>

    <span class="hljs-comment">// Should print: SubscriptionPlan.free</span>
  <span class="hljs-built_in">print</span>(SubscriptionPlan.extractFrom(<span class="hljs-string">'free'</span>));

  <span class="hljs-comment">// Should print: SubscriptionPlan.basic</span>
  <span class="hljs-built_in">print</span>(SubscriptionPlan.extractFrom(<span class="hljs-string">'basic'</span>)); 

    <span class="hljs-comment">// Should print: SubscriptionPlan.premium</span>
  <span class="hljs-built_in">print</span>(SubscriptionPlan.extractFrom(<span class="hljs-string">'premium'</span>)); 

  <span class="hljs-comment">// [2] Test case for an invalid input</span>
  <span class="hljs-keyword">try</span> {

      <span class="hljs-comment">// Should throw an error</span>
    <span class="hljs-built_in">print</span>(SubscriptionPlan.extractFrom(<span class="hljs-string">'business'</span>)); 
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">print</span>(e); <span class="hljs-comment">// Prints the error</span>
  }
}
</code></pre>
<p>That's all from me for now. </p>
<p>I hope that reading this article help you improve code readability, and maintain scalable applications. It's all about streamlining your development process afterall.</p>
<h2 id="heading-credits-and-resources">Credits and Resources:</h2>
<ol>
<li><a target="_blank" href="https://www.reddit.com/r/FlutterDev/comments/1e4rnsd/comment/ldktlqn/?context=3">More Examples as Response to a question on how people use Enhanced Enums in the applications they build on flutter subbreddit</a>.</li>
<li><a target="_blank" href="https://blog.stackademic.com/dive-into-enums-in-dart-from-the-basics-to-advanced-techniques-40e13bc3569f">Dive into Enums in Dart: From the Basics to Advanced Techniques.</a></li>
<li><a target="_blank" href="https://www.planetgeek.ch/2009/07/01/enums-are-evil/">Use Enums with Caution</a>: A Blog post on when Enums are a code smell, quite philosophical, but a must read.</li>
<li>Finally, <a target="_blank" href="https://dart.dev/language/enums">Dart Documentation on Dart.dev</a></li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use and Create Streams from Scratch in Dart and Flutter – a Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ Programming can be a rollercoaster ride. It catapults you from feeling like a genius to feeling utterly clueless, and back again — all in the blink of an eye.  What's even more wild is that this cycle repeats itself countless times throughout the day... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-and-create-streams-in-dart-and-flutter/</link>
                <guid isPermaLink="false">66ba18964a85bb09c10a6989</guid>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Asaboro ]]>
                </dc:creator>
                <pubDate>Thu, 14 Mar 2024 13:37:45 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/stream-controller-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Programming can be a rollercoaster ride. It catapults you from feeling like a genius to feeling utterly clueless, and back again — all in the blink of an eye. </p>
<p>What's even more wild is that this cycle repeats itself countless times throughout the day, and for the entirety of your career as a software developer.</p>
<p>Apart from my personal experience, a prime illustration of this phenomenon that comes to mind is the case of this Reddit User who shared their struggle a few months ago:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1709365581794/7085351c-088b-4c10-91ac-9ba947ca3fbd.png" alt="A Reddit user shares their frustration with Flutter streams, streamcontrollers, and websockets and how they work." width="656" height="180" loading="lazy">
<em>A Reddit user shares their frustration with Flutter streams, streamcontrollers, and websockets and how they work.</em></p>
<p><strong>Streams are</strong> one of those concepts that can make you go from "<em>Wow, I'm so smart 🙈</em>" to "<em>I'm so freaking dumb 💀 I should probably be on the Farm</em>". A lot of developers find them difficult to grasp and understand, particularly new Dart and Flutter Developers.</p>
<p>While Streams might be complex, they aren't so complicated that they're impossible to learn. If you put in enough dedication and practice, you can grasp them, a skill that may become necessary sooner or later.</p>
<p>This is because Streams are fundamental, and so many Flutter-based Dart libraries and SDKs (like Firebase, Device Sensors, some State-Management techniques, and heck, even Dart Isolates) heavily rely on them. As a result, learning how to use streams effectively will no doubt elevate your development skills.</p>
<h2 id="heading-what-youll-learn">What You'll Learn</h2>
<p>When you are done reading, you should be able to:</p>
<ul>
<li>Understand what Streams are and what they aren't, recognize the optimal scenarios for using them in your Dart and Flutter applications, and identify situations where alternative approaches may be more suitable.</li>
<li>Create custom-specific streams in Dart and leverage advanced techniques in transforming them according to your app requirements and for improved performance.</li>
<li>Implement strategies to tackle common performance challenges associated with streams, ensure your applications run smoothly under diverse conditions, and many more.</li>
</ul>
<h2 id="heading-prerequisites-what-should-you-know">Prerequisites: What Should You Know?</h2>
<p>Before we get started and for easy comprehension, you should have a basic understanding of the following topics and concepts:</p>
<ol>
<li><strong>Asynchronous Code</strong>: Familiarize yourself with the principles of asynchronous programming, how they differ from synchronous ones. Understand how asynchronous techniques contribute to performance, the key concepts like futures, async/await, callbacks, and event loops for a Single-threaded language.</li>
<li><strong>Dart</strong>: Ensure you have a working knowledge of the Dart programming language, including syntax, data types, variables, functions, classes, and a basic understanding of exception handling.</li>
<li><strong>Flutter Framework</strong>: While not strictly necessary, having a basic understanding of Flutter and its key components can be beneficial. Familiarize yourself with Flutter widgets, state management techniques, navigation, and widget lifecycle to better integrate streams into your Flutter applications.</li>
</ol>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<p>While I encourage you to read each of the sections in the order they were written, still feel free to jump to any section that interests you if you grasp the section preceding it.</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-a-stream-in-dart">What is a Stream in Dart?</a></li>
<li><a class="post-section-overview" href="#heading-real-life-applications-of-streams">Real-Life Applications of Streams</a></li>
<li><a class="post-section-overview" href="#heading-how-do-streams-work">How do Streams work</a>?</li>
<li><a class="post-section-overview" href="#heading-how-to-work-with-streams-in-dart">How to Work with Streams in Dart</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-your-own-stream-in-dart">How to Create Your Own Stream in Dart</a></li>
<li><a class="post-section-overview" href="#heading-what-about-error-handling-in-streams">What about Error Handling with Streams</a>?</li>
<li><a class="post-section-overview" href="#heading-not-everything-that-changes-has-to-be-a-stream">Not Everything that Changes Should be a Stream</a></li>
<li><a class="post-section-overview" href="#heading-just-get-started">Conclusion</a></li>
<li><a class="post-section-overview" href="#heading-quick-challenge">Quick Challenge</a></li>
</ul>
<h2 id="heading-what-is-a-stream-in-dart">What is a Stream in Dart?</h2>
<p>If you are reading this article, chances are you understand Asynchronous Operations — you know how to use Futures with Async-Await. And while you may not have an idea how they work internally (<a target="_blank" href="https://dart.dev/language/concurrency">the event loop concurrency stuff</a>), you have probably used them to fetch a JSON result from a remote API.</p>
<p>Streams are similar to Futures in that they both work asynchronously.</p>
<p>One of the key differences is that once a Future is called and starts, it can either return a value or it errors out and then stops. A Stream, on the other hand, can deliver a series of values (data and errors) continuously (more on this later).</p>
<p>So it's technically correct to say Futures are single-value streams or one-time response stream operations. If a method or function needs to return more than one result at different time intervals, or requires continuous updates to be handled the same way, you should probably look into streams.</p>
<h2 id="heading-real-life-applications-of-streams">Real-Life Applications of Streams</h2>
<p>Beyond the apparent applications like retrieving data from Firestore or managing Firebase Messages in a chat app, consider scenarios such as scanning for available Bluetooth devices or searching for WiFi hotspots.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710138803116/9ae58be0-c0d8-47f8-ae76-668b7ac6c8e3.png" alt="A screenshot showing the process of searching for wifi to illustrate a real-life application of streams." width="335" height="744" loading="lazy"></p>
<p>In such cases, when data becomes available (a new available hotspot connection or device), an event is emitted through a stream. Then listeners subscribed to the stream receive and process these events in their way asynchronously.</p>
<p>It's similar to playing songs on Spotify and watching videos on platforms like YouTube and Netflix. YouTube or Spotify's music server cleverly breaks songs or videos into small, manageable chunks—a stream of bytes so you don't have to wait until the app finishes downloading. Hence, the name: Streaming.</p>
<p>Imagine waiting for a song to download before you can play it!?!</p>
<h3 id="heading-your-http-get-request-uses-streams-internally">Your HTTP Get request uses Streams internally</h3>
<p>Dart just waits patiently until the stream finishes and then returns all the data at once in the form of a completed future.</p>
<pre><code class="lang-dart"><span class="hljs-comment">//preceeding code removed for brevity</span>

  client.getUrl(uri)
    .then((req) =&gt; req.close())
    .then((response) =&gt; response.transform(utf8.decoder).join())
    .then((value) =&gt; jsonDecode(value) <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">dynamic</span>&gt;)
    .then((json) =&gt; json.map((map) =&gt; Todo.fromJson(map)).toList())
    .then((retrievedTodos) {
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> todo <span class="hljs-keyword">in</span> retrievedTodos) {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Todo: <span class="hljs-subst">${todo.title}</span>, Completed: <span class="hljs-subst">${todo.completed}</span>'</span>);
      }
    })
    .catchError((e) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error: <span class="hljs-subst">$e</span>'</span>);
    })
    .whenComplete(() {
      client.close();
    });

<span class="hljs-comment">// section only for illustration</span>
</code></pre>
<p>Here's an Async-Await version that's commented:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Todo</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> id;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> completed;

  Todo({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.completed,
  });

  <span class="hljs-keyword">factory</span> Todo.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">return</span> Todo(
      id: json[<span class="hljs-string">'id'</span>],
      title: json[<span class="hljs-string">'title'</span>],
      completed: json[<span class="hljs-string">'completed'</span>],
    );
  }
}

<span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> uri = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'https://jsonplaceholder.typicode.com/todos'</span>);
  <span class="hljs-keyword">final</span> client = HttpClient();

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">final</span> request = <span class="hljs-keyword">await</span> client.getUrl(uri);
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> request.close();

    <span class="hljs-keyword">final</span> jsonString = <span class="hljs-keyword">await</span> response.transform(utf8.decoder).join();
    <span class="hljs-keyword">final</span> json = jsonDecode(jsonString) <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">dynamic</span>&gt;;

    <span class="hljs-keyword">final</span> retrievedTodos = json.map((map) =&gt; Todo.fromJson(map)).toList();

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> todo <span class="hljs-keyword">in</span> retrievedTodos) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Todo: <span class="hljs-subst">${todo.title}</span>, Completed: <span class="hljs-subst">${todo.completed}</span>'</span>);
    }
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error: <span class="hljs-subst">$e</span>'</span>);
  } <span class="hljs-keyword">finally</span> {
    client.close();
  }
}
</code></pre>
<p>It should be no surprise that downloading a file leverages this technique too.</p>
<p>Generally, you will need streams when dealing with anything involving a form of "connection" according to Remi Rouselet, the Author of Provider and Riverpod Package.</p>
<p>If that's clear, let's get practical.</p>
<h2 id="heading-how-do-streams-work">How Do Streams Work?</h2>
<p>Streams operate similarly to conveyor belts commonly seen at airports.</p>
<p>They act as channels that smoothly transport various items from one end to the other. Generally, you add luggage or bags onto a conveyor belt and they're carried along in its path. You can add data or events to a stream in the same way.</p>
<p>Where you put in items is called the source.</p>
<p>As the items move along the conveyor belt, workers stationed along the belt observe and interact with them. These workers represent the listeners or subscribers to the stream.</p>
<p>They may examine, categorize, or manipulate the items based on specific criteria.</p>
<p>Some workers may only be interested in specific types of items and let others pass by, while others may modify or combine items as they pass through their station. This reflects the concept of filtering, transforming, or aggregating events in the stream which we will get into later in this piece.</p>
<h2 id="heading-how-to-work-with-streams-in-dart">How to Work with Streams in Dart</h2>
<p>You have two choices:</p>
<ul>
<li>use a stream that already exists, or</li>
<li>make one from scratch.</li>
</ul>
<p>It's usually easier to use a stream that's already there instead of making a new one just to use it elsewhere in your app. So let's start with the idea of using an already-created stream of data.</p>
<p>But first, you should know that there are two kinds of streams:</p>
<ol>
<li>A single-subscription stream</li>
<li>A broadcast stream</li>
</ol>
<h3 id="heading-a-single-subscription-stream-is-the-default-in-dart">A single subscription stream is the default in Dart</h3>
<p>A single subscription only allows a single listener/subscriber during its whole lifetime. It doesn't even matter if you cancel an old subscription – you can't subscribe again. Any attempt to resubscribe will yield the <code>Bad State</code> error:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a StreamController</span>
  StreamController&lt;<span class="hljs-built_in">int</span>&gt; streamController = StreamController&lt;<span class="hljs-built_in">int</span>&gt;();

  <span class="hljs-comment">// Listen to the stream</span>
  StreamSubscription&lt;<span class="hljs-built_in">int</span>&gt; subscription = streamController.stream.listen(
    (<span class="hljs-built_in">int</span> data) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
    },
  );

  <span class="hljs-comment">// Cancel the subscription</span>
  subscription.cancel();

  <span class="hljs-comment">// Try to listen to the stream again with the same subscription</span>
  <span class="hljs-keyword">try</span> {
    subscription = streamController.stream.listen(
      (<span class="hljs-built_in">int</span> data) {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data again: <span class="hljs-subst">$data</span>'</span>);
      },
    );
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error: <span class="hljs-subst">$e</span>'</span>); <span class="hljs-comment">// Handle the error</span>
  }

  <span class="hljs-comment">// Close the stream controller</span>
  streamController.close();
}}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710052410984/6a1556d2-d90c-4878-b180-c1bcf427d667.png" alt="The error message you get from the stream is: &quot;Error: Bad State: Stream has already been Listened to&quot;." width="694" height="55" loading="lazy"></p>
<p>This is useful when the order in which the information arrives is critical and any misalignment will make the data unreadable or impossible to interpret such as when an HTTP GET request, reading a file, or processing messages in a chat application.</p>
<p>Also, a single-subscription stream is the most efficient kind of stream because it doesn't start generating events until it has a listener, and it stops sending events when the listener unsubscribes, even if there are still more events to emit.</p>
<p>But what if you want more than a single listener?</p>
<p>What if you need to share the same data stream across multiple components or widgets in your application? What if a collaborative feature involves real-time updates, and various parts of your application need to react simultaneously – what do you do?</p>
<h3 id="heading-thats-where-the-broadcast-stream-comes-in">That's where the Broadcast Stream comes in</h3>
<p>Unlike a single-subscription stream, a broadcast stream allows any number of listeners. What's interesting is that it fires its events when they are ready without checking if there are listeners or not.</p>
<p>That's quite inefficient, isn't it? So it's essential to exercise caution with broadcast streams as they lead to memory leaks if not managed properly. After all, don't they say that with great power comes great responsibility?</p>
<p>Broadcast streams are well-suited for situations where each event can be handled without relying on previous events and can be processed by the user as soon as it's received – for example, breaking news, sports scores, or weather alerts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710138971106/3b426c19-ca45-45f8-9ddd-ead192f34e0f.png" alt="English Premier League Livescore used to illustrate a notification stream that doesnt' rely on previous events" width="507" height="502" loading="lazy"></p>
<p>It's worth noting that all subscribers are unsubscribed once a <code>done</code> event is fired. Then any new subscriber will simply get the done event and stop listening.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a StreamController</span>
  StreamController&lt;<span class="hljs-built_in">int</span>&gt; streamController = StreamController&lt;<span class="hljs-built_in">int</span>&gt;();

  <span class="hljs-comment">// Listen to the stream</span>
  streamController.stream.listen(
    (<span class="hljs-built_in">int</span> data) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
    },
    onDone: () {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Stream is done.'</span>);
    },
  );

  <span class="hljs-comment">// Add data to the stream</span>
  streamController.add(<span class="hljs-number">1</span>);
  streamController.add(<span class="hljs-number">2</span>);

  <span class="hljs-comment">// Close the stream controller</span>
  streamController.close();

  <span class="hljs-comment">// Try to subscribe again after the stream is closed</span>

  Future.delayed(<span class="hljs-built_in">Duration</span>.zero, () {
    <span class="hljs-keyword">try</span> {
      streamController.stream.listen(
        (<span class="hljs-built_in">int</span> data) {
          <span class="hljs-built_in">print</span>(<span class="hljs-string">'New subscriber received data: <span class="hljs-subst">$data</span>'</span>);
        },
        onDone: () {
          <span class="hljs-built_in">print</span>(<span class="hljs-string">'New subscriber received the done event.'</span>);
        },
      );
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-built_in">print</span>(e);
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'New subscriber is no longer listening.'</span>);
    }
  });
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710052967862/464e7a7e-ab6d-431c-8e70-09dc629a8dfd.png" alt="Result after running the code above. First 1 and 2 are printed, then the Stream is mark done. But if you try to listen to it again, it will say Stream has already been listening to" width="448" height="194" loading="lazy"></p>
<p>What about creating our own streams that others can subscribe to?</p>
<h2 id="heading-how-to-create-your-own-stream-in-dart">How to Create Your Own Stream in Dart</h2>
<p>Currently, there are three ways to create a new Stream in Dart:</p>
<ol>
<li>Transforming existing streams</li>
<li>Using an async generator</li>
<li>Using stream controllers</li>
</ol>
<h3 id="heading-how-to-create-streams-by-transforming-existing-streams">How to create streams by transforming existing streams</h3>
<p>I didn't really think of these as a standalone method for creating streams because it requires you to rely on another stream. But going through the Dart documentation made me realize we are actually creating a new stream entity whenever we transform another stream. It's quite meta, actually...</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a stream of integers</span>
  <span class="hljs-keyword">final</span> Stream&lt;<span class="hljs-built_in">int</span>&gt; originalStream = Stream&lt;<span class="hljs-built_in">int</span>&gt;.fromIterable([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]);

  <span class="hljs-comment">// Transform the original stream using map()</span>
  <span class="hljs-keyword">final</span> Stream&lt;<span class="hljs-built_in">int</span>&gt; transformedStream = originalStream.map((<span class="hljs-built_in">int</span> value) {
    <span class="hljs-keyword">return</span> value * <span class="hljs-number">2</span>; <span class="hljs-comment">// Double each integer</span>
  });

  <span class="hljs-comment">// Listen to the transformed stream</span>
  <span class="hljs-keyword">final</span> StreamSubscription&lt;<span class="hljs-built_in">int</span>&gt; subscription =
      transformedStream.listen((<span class="hljs-built_in">int</span> value) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Transformed value: <span class="hljs-subst">$value</span>'</span>);
  });

  <span class="hljs-comment">// Close the subscription and the streams after a delay</span>
  Future.delayed(<span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">1</span>), () {
    subscription.cancel();
  });
}
</code></pre>
<p>The result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1709919319018/3fb5a20c-7dfc-4d11-bfc5-fda51ff4fd93.png" alt="Result from the above code where each stream data is multipled by two giving: Transformed value: 2, 4, 6 etc" width="230" height="176" loading="lazy">
<em>Result from the above code where each stream data is multipled by two</em></p>
<p>This examples gets a Firebase Firestore data Stream and maps it to the UI:</p>
<pre><code class="lang-dart">

<span class="hljs-comment">//code removed for brevity</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FirestoreService</span> </span>{
  <span class="hljs-keyword">final</span> CollectionReference _collectionReference =
      FirebaseFirestore.instance.collection(<span class="hljs-string">'messages'</span>);

  Stream&lt;<span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;&gt;&gt; getMessages() {
    <span class="hljs-keyword">return</span> _collectionReference.snapshots().map((snapshot) =&gt;
        snapshot.docs.map((doc) =&gt; doc.data() <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>,             <span class="hljs-built_in">dynamic</span>&gt;).toList());
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; addMessage(<span class="hljs-built_in">String</span> message, <span class="hljs-built_in">String</span> sender) <span class="hljs-keyword">async</span> {
  <span class="hljs-comment">// your code...</span>
  }

  <span class="hljs-comment">// other methods removed for brevity</span>
}

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-keyword">final</span> firestoreService = FirestoreService();

  <span class="hljs-comment">// Listen to messages</span>
  <span class="hljs-keyword">final</span> Stream&lt;<span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;&gt;&gt; messageStream =
      firestoreService.getMessages();
  messageStream.listen((messages) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received messages: <span class="hljs-subst">$messages</span>'</span>);
  });

  <span class="hljs-comment">// Add a new message</span>
  firestoreService.addMessage(<span class="hljs-string">'Hello Firestore!'</span>, <span class="hljs-string">'Dart User'</span>);
}

<span class="hljs-comment">// other code for brevity</span>
</code></pre>
<p>Other common transformation methods are <code>take()</code>, <code>expand()</code>, and <code>where()</code>. If your application demands more advanced transformations (for example, converting the HTTP Get response using UTF-8 decoding) beyond these standard methods, explore the <a target="_blank" href="https://api.dart.dev/stable/3.3.0/dart-async/StreamTransformer-class.html">stream transformer class</a> for additional capabilities.</p>
<h3 id="heading-how-to-create-streams-using-generators">How to create streams using generators</h3>
<p>Here, you make use of what is called the async generator function.</p>
<p>It's a function marked with <code>async *</code> instead of <code>async</code> to differentiate it from futures. This function runs asynchronously and sends back a value whenever it sees a <code>yield</code> keyword, but it won't stop executing the function code body like a <code>return</code> would.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-comment">// Define an asynchronous generator function</span>
Stream&lt;<span class="hljs-built_in">int</span>&gt; countStream(<span class="hljs-built_in">int</span> max) <span class="hljs-keyword">async</span>* {
  <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">1</span>; i &lt;= max; i++) {
    <span class="hljs-comment">// Yield each value asynchronously</span>
    <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">1</span>));
    <span class="hljs-keyword">yield</span> i;
  }
}

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a stream using the asynchronous generator function</span>
  Stream&lt;<span class="hljs-built_in">int</span>&gt; stream = countStream(<span class="hljs-number">6</span>);

  <span class="hljs-comment">// Subscribe to the stream</span>
  stream.listen((value) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received: <span class="hljs-subst">$value</span>'</span>);
  }, onDone: () {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Stream is done'</span>);
  });
}
</code></pre>
<p><strong>Here's how it works:</strong></p>
<p>The stream is created when you call or invoke the function.</p>
<p>But it only starts running when you listen to the stream because streams are lazy-loaded. It can emit events on the stream by using <code>yield</code> or <code>yield*</code> statements until the function returns, then the stream closes.</p>
<h4 id="heading-how-is-a-yield-different-from-a-return-and-how-does-it-work">How is a yield different from a return and how does it work?</h4>
<p>In Dart, "return" is used to immediately exit a function and return a value to the caller. When a function encounters a return statement, it terminates its execution and passes control back to the caller, along with the specified return value.</p>
<p>Subsequent calls to the function will start execution from the beginning.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-comment">// Function that returns a Future&lt;int&gt; after a delay</span>
Future&lt;<span class="hljs-built_in">int</span>&gt; fetchUserData() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">2</span>)); <span class="hljs-comment">// Simulate a delay</span>
  <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>; <span class="hljs-comment">// Simulated user data</span>
}

<span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Fetching user data...'</span>);

  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// Initiating the asynchronous operation and waiting for the result</span>
    <span class="hljs-built_in">int</span> userData = <span class="hljs-keyword">await</span> fetchUserData();
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'User data received: <span class="hljs-subst">$userData</span>'</span>);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error fetching user data: <span class="hljs-subst">$error</span>'</span>);
  }

  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Continuing with other tasks...'</span>);
}
</code></pre>
<p>In contrast, when a function encounters a "yield" statement in an async generator, it suspends its execution, returns the value specified by "yield" to the caller, and preserves the state of the function. This allows the function to resume execution from where it left off when the next value is requested.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-comment">// Asynchronous generator function</span>
Stream&lt;<span class="hljs-built_in">int</span>&gt; countStream(<span class="hljs-built_in">int</span> max) <span class="hljs-keyword">async</span>* {
  <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">1</span>; i &lt;= max; i++) {
    <span class="hljs-comment">// Simulate asynchronous delay</span>
    <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">1</span>)); 
    <span class="hljs-keyword">yield</span> i; <span class="hljs-comment">// Yield each value in the sequence</span>
  }
}

<span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
  <span class="hljs-comment">// Create a stream using the asynchronous generator function</span>
  Stream&lt;<span class="hljs-built_in">int</span>&gt; stream = countStream(<span class="hljs-number">5</span>);

  <span class="hljs-comment">// Subscribe to the stream using await for loop</span>
  <span class="hljs-keyword">await</span> <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> value <span class="hljs-keyword">in</span> stream) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received: <span class="hljs-subst">$value</span>'</span>);

    <span class="hljs-comment">// Simulate additional processing</span>
    <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-built_in">Duration</span>(milliseconds: <span class="hljs-number">500</span>));
  }

  <span class="hljs-built_in">print</span>(<span class="hljs-string">'Stream is done'</span>);
}
</code></pre>
<p>In the example above, the function <code>countStream()</code> is a generator function that produces a sequence of integers from 0 to 4 lazily using the "yield" keyword.</p>
<p>Each time the function is called, it returns the next value in the sequence without generating the entire sequence upfront. This can be useful for conserving memory and processing large datasets efficiently.</p>
<p>Note that, the <code>Stream</code> created by a <code>async*</code> function is always a single-subscription stream. This is because a <code>async*</code> function is intended to execute normally until it's done similar to the normal control flow of a single (asynchronous) function.</p>
<p>What if you want a broadcast stream — what do you do?</p>
<p>Hint: I've already answered that in this article.</p>
<p>Finally, an interesting thing that the <a target="_blank" href="https://dart.dev/articles/libraries/creating-streams#:~:text=It's%20rare%20to%20have%20an,from%20other%20asynchronous%20event%20sources.">official documentation</a> pointed out, which I haven't really taken note of, is that:</p>
<blockquote>
<p>It's rare to have an <code>async*</code> function building a stream from nothing. It needs to get its data from somewhere, and most often that somewhere is another stream.</p>
</blockquote>
<p>That makes you still dependent on other streams. What if you need to go granular and start from scratch? That's where the <code>StreamController</code> class comes in.</p>
<h3 id="heading-how-to-create-streams-using-stream-controllers">How to create streams using stream controllers</h3>
<p>Stream controllers are well suited for situations where the events of your stream come from different parts of your program, and/or can't be gotten from another stream or future.</p>
<p>A few examples that come to mind are managing user input events, handling data from diverse sources, or creating custom events within your application like state updates, progress notifications, or system alerts.</p>
<p>Stream controllers are low-level, as I understand. They don't just give you a stream, they give you ways to add events to it at any point including the <a target="_blank" href="https://api.dart.dev/stable/dart-async/StreamController-class.html">logic necessary to handle listeners and pausing</a>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a stream controller to manage user input events</span>
  StreamController&lt;<span class="hljs-built_in">String</span>&gt; userInputController = StreamController&lt;<span class="hljs-built_in">String</span>&gt;();

  <span class="hljs-comment">// Listen to user input events</span>
  userInputController.stream.listen((<span class="hljs-built_in">String</span> userInput) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'User input: <span class="hljs-subst">$userInput</span>'</span>);
  });

  <span class="hljs-comment">// Simulate user input events</span>
  userInputController.add(<span class="hljs-string">'Hello'</span>);
  userInputController.add(<span class="hljs-string">'World'</span>);

  <span class="hljs-comment">// Create a custom stream controller for progress notifications</span>
  StreamController&lt;<span class="hljs-built_in">double</span>&gt; progressController = StreamController&lt;<span class="hljs-built_in">double</span>&gt;();

  <span class="hljs-comment">// Listen to progress notifications</span>
  progressController.stream.listen((<span class="hljs-built_in">double</span> progress) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Progress: <span class="hljs-subst">$progress</span>'</span>);
  });

  <span class="hljs-comment">// Simulate progress notifications</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-built_in">double</span> i = <span class="hljs-number">0</span>; i &lt;= <span class="hljs-number">1</span>; i += <span class="hljs-number">0.2</span>) {
    progressController.add(i);
  }

  <span class="hljs-comment">// Create a custom stream controller for system alerts</span>
  StreamController&lt;<span class="hljs-built_in">String</span>&gt; systemAlertController = StreamController&lt;<span class="hljs-built_in">String</span>&gt;();

  <span class="hljs-comment">// Listen to system alerts</span>
  systemAlertController.stream.listen((<span class="hljs-built_in">String</span> alert) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'System Alert: <span class="hljs-subst">$alert</span>'</span>);
  });

  <span class="hljs-comment">// Simulate system alerts</span>
  systemAlertController.add(<span class="hljs-string">'System overload detected!'</span>);
  systemAlertController.add(<span class="hljs-string">'Database connection lost!'</span>);

  <span class="hljs-comment">// Close all stream controllers when done</span>
  userInputController.close();
  progressController.close();
  systemAlertController.close();
}
</code></pre>
<p>It has four callback methods:</p>
<ol>
<li><code>onListen</code></li>
<li><code>onCancel</code></li>
<li><code>onResume</code></li>
<li><code>onPause</code></li>
</ol>
<p>If you want to know when the stream has been subscribed to, pass an onListenHandler to the <code>onListen</code> parameter when you create the <code>StreamController</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a StreamController with an onListen callback</span>
  StreamController&lt;<span class="hljs-built_in">int</span>&gt; streamController = StreamController&lt;<span class="hljs-built_in">int</span>&gt;(
    onListen: () {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Stream has been subscribed to.'</span>);
    },
  );

  <span class="hljs-comment">// Listen to the stream</span>
  streamController.stream.listen((<span class="hljs-built_in">int</span> data) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
  });

  <span class="hljs-comment">// Add data to the stream</span>
  streamController.add(<span class="hljs-number">1</span>);
  streamController.add(<span class="hljs-number">2</span>);
  streamController.add(<span class="hljs-number">3</span>);

  <span class="hljs-comment">// Close the stream controller when done</span>
  streamController.close();
}
</code></pre>
<p>The <code>onListen</code> callback is called when the stream gets its first subscriber. <code>onCancel</code>, on the other hand, is triggered when the controller loses its last subscriber.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a StreamController with onCancel callback</span>
  StreamController&lt;<span class="hljs-built_in">int</span>&gt; streamController = StreamController&lt;<span class="hljs-built_in">int</span>&gt;(
    onCancel: () {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Last subscriber canceled, stream controller is now inactive.'</span>);
    },
  );

  <span class="hljs-comment">// Listen to the stream</span>
  StreamSubscription&lt;<span class="hljs-built_in">int</span>&gt; subscription = streamController.stream.listen((<span class="hljs-built_in">int</span> data) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
  });

  <span class="hljs-comment">// Add data to the stream</span>
  streamController.add(<span class="hljs-number">1</span>);
  streamController.add(<span class="hljs-number">2</span>);

  <span class="hljs-comment">// Cancel the subscription</span>
  subscription.cancel();

  <span class="hljs-comment">// Add more data to the stream after canceling the subscription</span>
  streamController.add(<span class="hljs-number">3</span>);

  <span class="hljs-comment">// Close the stream controller</span>
  streamController.close();
}

<span class="hljs-comment">//##Note: </span>

<span class="hljs-comment">//When you use async* and yield*, </span>
<span class="hljs-comment">//you're creating a function that can asynchronously yield values,</span>
<span class="hljs-comment">//potentially generating a new stream of values each time it's called.</span>

<span class="hljs-comment">//When you return a stream,</span>
<span class="hljs-comment">//you're passing around a reference to an existing stream object without</span>
<span class="hljs-comment">//necessarily generating new values or modifying the stream itself.</span>
</code></pre>
<p>Remember when I said that StreamControllers are low-level?</p>
<p>What this generally means is that you have control over everything. However, this comes with the responsibility of implementing features that higher-level stream creation methods provide out of the box.</p>
<h3 id="heading-one-such-feature-is-known-as-honouring-the-pause">One such feature is known as "honouring the pause"</h3>
<p>When a stream subscription to an async* generator is paused, the generator function automatically pauses at a yield statement, ensuring that no new events are emitted until the subscription resumes.</p>
<p>But with StreamControllers, events continue to be generated and buffered during pauses. If the event-producing code fails to respect the pause, the buffer size can grow indefinitely, leading to potential memory issues.</p>
<p>Moreover, if the listener stops listening shortly after pausing, all the effort spent creating the buffer is wasted. Grossly inefficient, isn't it? Imagine a long-running operation.</p>
<p>Here's how to resolve it:</p>
<pre><code class="lang-dart">Stream&lt;<span class="hljs-built_in">int</span>&gt; integerCounter(<span class="hljs-built_in">Duration</span> interval, [<span class="hljs-built_in">int?</span> maxCount]) {
  <span class="hljs-keyword">late</span> StreamController&lt;<span class="hljs-built_in">int</span>&gt; controller;

  <span class="hljs-keyword">void</span> onListenHandler() {
    <span class="hljs-comment">//code removed for brevity;</span>
  }
  <span class="hljs-keyword">void</span> onPauseHandler() {
    <span class="hljs-comment">//code removed for brevity;</span>
  }
  <span class="hljs-keyword">void</span> onResumeHandler() {
    <span class="hljs-comment">//code removed for brevity;</span>
  }
  <span class="hljs-keyword">void</span> onCancelHandler() {
    <span class="hljs-comment">//code removed for brevity;</span>
  }

  controller = StreamController&lt;<span class="hljs-built_in">int</span>&gt;(
    onListen: onListenHandler,
    onPause: onPauseHandler,
    onResume: onResumeHandler,
    onCancel: onCancelHandler,
  );

  <span class="hljs-keyword">return</span> controller.stream;
}
</code></pre>
<h3 id="heading-another-one-is-something-i-call-pause-subscription-synchronization">Another one is something I call "Pause-Subscription Synchronization"</h3>
<p>This term refers to the synchronization between the subscription and pause states of a StreamController. If both the subscription and pause states change simultaneously, only the <code>onListen</code> or the <code>onCancel</code> callback is triggered.</p>
<p>This is why it's advisable to implement all available listeners—<code>onListen</code>, <code>onCancel</code>, <code>onPause</code>, and <code>onResume</code>—to mitigate potential issues and ensure proper functionality. This way, you can effectively monitor changes in the pause state and avoid hard-to-track bugs that may arise from unexpected behaviour.</p>
<p>Oh, and don't ever forget to dispose of your controller:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a StreamController</span>
  StreamController&lt;<span class="hljs-built_in">int</span>&gt; streamController = StreamController&lt;<span class="hljs-built_in">int</span>&gt;();

  <span class="hljs-comment">// Listen to the stream</span>
  StreamSubscription&lt;<span class="hljs-built_in">int</span>&gt; subscription = streamController.stream.listen((<span class="hljs-built_in">int</span> data) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
  });

  <span class="hljs-comment">// Add data to the stream</span>
  streamController.add(<span class="hljs-number">1</span>);
  streamController.add(<span class="hljs-number">2</span>);

  <span class="hljs-comment">// Dispose of the subscription and stream controller</span>
  subscription.cancel();
  streamController.close(); <span class="hljs-comment">// call the close method to dispose</span>
}
</code></pre>
<h2 id="heading-what-about-error-handling-in-streams">What about Error Handling in Streams?</h2>
<p>When errors arise within a stream, the stream manages them similarly to how it handles data events—by informing listeners through error events. Generally, streams demonstrate two clear behaviours in reaction to errors:</p>
<ol>
<li>The stream notifies the first error event and then halts further processing.</li>
<li>The stream notifies error event(s) but continues delivering subsequent events.</li>
</ol>
<p>Let's take each at a time.</p>
<h3 id="heading-stopping-after-the-first-error">Stopping after the first error</h3>
<p>In this scenario, the stream stops after encountering the first error, but it provides insight into the initial issue and discontinues any further event transmission. This is useful when the order of importance is critical and any missing piece is enough to make the whole file unusable.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a StreamController</span>
  StreamController&lt;<span class="hljs-built_in">int</span>&gt; streamController = StreamController&lt;<span class="hljs-built_in">int</span>&gt;();

  <span class="hljs-comment">// Listen to the stream</span>
  StreamSubscription&lt;<span class="hljs-built_in">int</span>&gt; subscription = streamController.stream.listen(
    (<span class="hljs-built_in">int</span> data) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
    },
    onError: (error) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error occurred: <span class="hljs-subst">$error</span>'</span>);
    },
    onDone: () {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Stream is done.'</span>);
    },
  );

  <span class="hljs-comment">// Add data to the stream</span>
  streamController.add(<span class="hljs-number">1</span>);
  streamController.add(<span class="hljs-number">2</span>);
  streamController.addError(<span class="hljs-string">'Error: Something went wrong'</span>); <span class="hljs-comment">// Simulate an error</span>
  streamController.add(<span class="hljs-number">3</span>);

  <span class="hljs-comment">// Close the stream controller</span>
  streamController.close();
}
</code></pre>
<p>From what you've learned, this is peculiar to async generator functions or single subscription streams. What if your case is different, say you want to continue after you encounter an error in a Stream, what do you do?</p>
<h3 id="heading-continuing-after-the-first-error">Continuing after the first error</h3>
<p>Unlike scenarios where streams stop after the first error, continuing after errors enables the stream to maintain its flow. This provides ongoing insights and updates to downstream consumers.</p>
<p>This approach is invaluable in scenarios where the stream's uninterrupted operation is paramount, such as real-time data processing or continuous monitoring systems. Streams that continue after errors offer resilience and adaptability, ensuring that critical information is not lost due to isolated incidents.</p>
<p>Let's look at an example:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
  <span class="hljs-comment">// Create a stream controller</span>
  StreamController&lt;<span class="hljs-built_in">int</span>&gt; streamController = StreamController&lt;<span class="hljs-built_in">int</span>&gt;();

  <span class="hljs-comment">// Generate numbers asynchronously with a delay of 1 second</span>
  <span class="hljs-built_in">int</span> count = <span class="hljs-number">0</span>;
  Timer.periodic(<span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">1</span>), (Timer timer) {
    <span class="hljs-comment">// Simulate errors for demonstration</span>
    <span class="hljs-keyword">if</span> (count % <span class="hljs-number">3</span> == <span class="hljs-number">0</span>) {
      streamController.addError(<span class="hljs-string">'Error: Failed to generate number <span class="hljs-subst">$count</span>'</span>);
    } <span class="hljs-keyword">else</span> {
      streamController.add(count);
    }
    count++;
  });

  <span class="hljs-comment">// Listen to the stream</span>
  streamController.stream.listen(
    (<span class="hljs-built_in">int</span> data) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Received data: <span class="hljs-subst">$data</span>'</span>);
    },
    onError: (error) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error occurred: <span class="hljs-subst">$error</span>'</span>);
    },
  );
}
</code></pre>
<h2 id="heading-not-everything-that-changes-has-to-be-a-stream">Not Everything that Changes Has to Be a Stream</h2>
<p>Streams offer great functionality by emitting events (data or error values) without concerning themselves with how they are consumed and this gives developers the flexibility to write code with low coupling and high extensibility. But they shouldn't be tied to everything that changes.</p>
<p>According to Randal Schwartz, <a target="_blank" href="https://www.reddit.com/r/FlutterDev/comments/1586sfg/whats_a_real_world_use_case_of_a_dart_stream/">State Management is a great example</a> of this.</p>
<p>I reached out to him to be sure I understand his stance, and here's what he says:</p>
<blockquote>
<p>"The key difference, as clarified to me by Remi, is that there is a place for streams when every event must be included, vs typical state management, where only the most recent state (and notification when it changes) is relevant. If something quickly goes from 1 to 2 to 3, but you then rebuild based on 3, that's enough."</p>
</blockquote>
<p>In other words, you don't care about the intermediate, only the latest ones.</p>
<p>State is something you have to manage through your app's entire lifecycle. If it's done poorly, your apps might suffer performance issues and lag due to excessive or large-scale rebuilds.</p>
<p>So minimize unnecessary updates, and rebuild only components that genuinely need rebuilding to optimise overall performance. Remember, Dart is single-threaded.</p>
<h2 id="heading-just-get-started">Just Get Started</h2>
<p>I don't expect you to grasp every detail presented here in one go even though I've dedicated countless hours spanning weeks to refine this tutorial. So don't feel pressured to.</p>
<p>Instead, feel free to bookmark for when you're ready to continue. If anything seems unclear, please refer to the credits and recommended resources or you can reach out to me on Twitter.</p>
<p>The undeniable truth is that you can consume endless tutorials and videos, but true confidence comes when you apply your knowledge to real-world problems and resolve them (I have three for you down below)</p>
<p>Approach this tutorial like you will use a stream to handle a large resource – break it into smaller, digestible chunks and process them at your convenience. It doesn't matter if it's irregular, just make sure to tackle it.</p>
<p>If any confusion arises, share it in the comments, tweet at me on Twitter (Now X), or reach out to me through the DM. I will be glad to help you resolve them and bring some clarity. Goodbye!</p>
<h2 id="heading-quick-challenge">Quick Challenge</h2>
<ol>
<li>How would you implement ChatGPT's typing style with streams?</li>
<li><p>Say you are assigned a new task. On button press, your app should:<br>– download a compressed file,<br>– extract the file into a<br>– find an executable binary file, and run it,<br>– return a list of directories that should be added to PATH.</p>
<p>     How would you resolve it with what you've learn so far in this tutorial?</p>
</li>
<li><p>How can you use streams to communicate when a user is typing or not?</p>
</li>
</ol>
<h3 id="heading-credits">Credits:</h3>
<ol>
<li><a target="_blank" href="https://medium.com/flutter-community/flutter-stream-basics-for-beginners-eda23e44e32f">Flutter Stream Basics for Beginners</a> by Dane Mackier.</li>
<li><a target="_blank" href="https://ptyagicodecamp.github.io/streams-asynchronous-programming-with-dart.html">Streams: Asynchronous Programming with Dart</a> by Priyanka Tyagi</li>
<li><a target="_blank" href="https://stackoverflow.com/questions/42611880/difference-between-await-for-and-listen-in-">Difference between <code>await for</code> and <code>listen</code></a> answered on StackOverflow.</li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=53jIxLiCv2E">Simple Beginners Guide to Streams | Flutter and Dart Stream Basics</a> by FilledStacks [Youtube Video]</li>
<li><a target="_blank" href="https://api.flutter.dev/flutter/dart-async/Stream-class.html">Streams: API documentation</a> on Flutter dot Dev</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
