<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Elisey Ozerov</title>
    <description>The latest articles on DEV Community by Elisey Ozerov (@eliseyozerov).</description>
    <link>https://dev.to/eliseyozerov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F42988%2Fc96434cc-4c1c-4ea9-be63-10d12472dac2.jpeg</url>
      <title>DEV Community: Elisey Ozerov</title>
      <link>https://dev.to/eliseyozerov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eliseyozerov"/>
    <language>en</language>
    <item>
      <title>Asynchronous Programming in Dart and Flutter</title>
      <dc:creator>Elisey Ozerov</dc:creator>
      <pubDate>Thu, 06 Mar 2025 15:40:13 +0000</pubDate>
      <link>https://dev.to/eliseyozerov/asynchronous-programming-in-dart-and-flutter-34dd</link>
      <guid>https://dev.to/eliseyozerov/asynchronous-programming-in-dart-and-flutter-34dd</guid>
      <description>&lt;p&gt;Asynchronous programming is a paradigm where code &lt;strong&gt;continues executing without waiting&lt;/strong&gt; for long-running operations to complete, handling their results when they become available.&lt;/p&gt;

&lt;p&gt;In this article, I will outline in my opinion the most relevant APIs for working with asynchronous code, as well as explain a little of the inner workings of the Dart VM in terms of concurrency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Futures
&lt;/h3&gt;

&lt;p&gt;By far the most common async concept you will encounter are Futures. When interacting with servers or the native platform, every API you'll use will return a &lt;a href="https://api.dart.dev/stable/latest/dart-async/Future-class.html" rel="noopener noreferrer"&gt;Future&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Async/Await
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;myAsyncFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"filename"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readAsString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three main keywords here - &lt;strong&gt;Future&lt;/strong&gt;, &lt;strong&gt;async&lt;/strong&gt; and &lt;strong&gt;await&lt;/strong&gt;. The most important one in my opinion, is &lt;code&gt;await&lt;/code&gt;. It allows you to wait for the method to finish and only then continue with the execution. And if you want to use the &lt;code&gt;await&lt;/code&gt; keyword, you must denote the function as &lt;code&gt;async&lt;/code&gt; and if you want to do that, it must have a return type of &lt;code&gt;Future&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The part in the parentheses behind the &lt;code&gt;Future&lt;/code&gt; keyword is called a generic type parameter. This means that the compiler will expect the returned value to be of the type you specify here. In our case, when you call the &lt;code&gt;myAsyncFunction&lt;/code&gt;, the returned value will be a String. And if you avoid specifying the generic type, the resulting Future will contain a &lt;code&gt;dynamic&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;You may ask - "But we were just talking about &lt;em&gt;not&lt;/em&gt; having to wait for long-running operations to complete, so why are we doing this?" That point still stands for the definition of asynchronicity. But here we want to wait for the result before continuing the execution, as further logic depends on the contents of the file.&lt;/p&gt;

&lt;h4&gt;
  
  
  Callbacks
&lt;/h4&gt;

&lt;p&gt;But if we do indeed want asynchronicity, there is a second way of using Futures, which is via callbacks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;mySyncFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;myAsyncFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;myAsyncFunction&lt;/code&gt; is called and when it returns the result, it will be printed out. For those of you who've programmed in JavaScript, this will be instantly recognizable.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Future&lt;/code&gt; class provides several &lt;a href="https://api.dart.dev/stable/latest/dart-async/Future-class.html#instance-methods" rel="noopener noreferrer"&gt;methods&lt;/a&gt; you can use. The one I use the most after &lt;a href="https://api.dart.dev/stable/latest/dart-async/Future/then.html" rel="noopener noreferrer"&gt;.then&lt;/a&gt;, is the &lt;a href="https://api.dart.dev/stable/latest/dart-async/Future/timeout.html" rel="noopener noreferrer"&gt;.timeout&lt;/a&gt; method, as sometimes you must stop waiting for the future to complete. If you provide the &lt;code&gt;onTimeout&lt;/code&gt; callback, the value you return from it will be returned from the timed out future.&lt;/p&gt;

&lt;p&gt;There are also some constructors and static methods I use quite frequently. &lt;a href="https://api.dart.dev/stable/latest/dart-async/Future/Future.delayed.html" rel="noopener noreferrer"&gt;Future.delayed&lt;/a&gt; is a constructor that takes in a &lt;a href="https://api.dart.dev/stable/latest/dart-core/Duration-class.html" rel="noopener noreferrer"&gt;Duration&lt;/a&gt; and a callback, and you use that to delay the execution of that callback for the given duration. &lt;a href="https://api.dart.dev/stable/latest/dart-async/Future/Future.value.html" rel="noopener noreferrer"&gt;Future.value&lt;/a&gt; is another constructor if you want to immediately return the given value for some reason, like perhaps in some type requirement situations.&lt;/p&gt;

&lt;p&gt;Of the static methods I mostly use the &lt;a href="https://api.dart.dev/stable/latest/dart-async/Future/wait.html" rel="noopener noreferrer"&gt;.wait&lt;/a&gt; method, as it allows you to run multiple futures in parallel. It completes once every future has completed, or when one of them throws an error. If you set &lt;code&gt;eagerError&lt;/code&gt; parameter to true, the returned future will complete with the error from the first failing future immediately, while if it's left on false, all other futures will run as well, but will silently drop their errors.&lt;/p&gt;

&lt;h4&gt;
  
  
  Errors
&lt;/h4&gt;

&lt;p&gt;The futures, as non-futures alike, can cause errors. Among the methods I've linked to above, there are several options to handle errors, like the &lt;code&gt;catchError&lt;/code&gt; method, &lt;code&gt;onError&lt;/code&gt; method, and the &lt;code&gt;onError&lt;/code&gt; parameter inside &lt;code&gt;then&lt;/code&gt;. I really don't like using any of them, to be honest. We have the &lt;strong&gt;async/await&lt;/strong&gt; keywords for a reason, which is to avoid &lt;a href="http://callbackhell.com/" rel="noopener noreferrer"&gt;callback hell&lt;/a&gt; by linearizing the execution and thus making the code more readable.&lt;/p&gt;

&lt;p&gt;In order to handle errors like an adult, I suggest you use the &lt;code&gt;try/catch&lt;/code&gt; blocks. One important note - you &lt;strong&gt;MUST&lt;/strong&gt; use the await keyword when doing so. If you don't, the &lt;code&gt;catch&lt;/code&gt; block will not be executed and the exception will not be handled.&lt;/p&gt;

&lt;p&gt;After errors, we're left with only a few methods, including &lt;code&gt;asStream&lt;/code&gt;, &lt;code&gt;ignore&lt;/code&gt; and &lt;code&gt;whenComplete&lt;/code&gt;, none of which I ever use, so I'll skip them for now.&lt;/p&gt;

&lt;h4&gt;
  
  
  Completers
&lt;/h4&gt;

&lt;p&gt;These bad boys have been very useful to me, mostly because sometimes there are futures you want to await, but in a different scope. Consider this example. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You've got a login screen, where you call &lt;code&gt;loginUser&lt;/code&gt; when the user taps the login button.&lt;/li&gt;
&lt;li&gt;This launches the login flow and whenever the login function finishes successfully, you know the user is logged in.&lt;/li&gt;
&lt;li&gt;But say your user has a profile you want to fetch before continuing to the next screen and you're using Firebase Auth's &lt;code&gt;authStateChanges&lt;/code&gt; Stream to centralize the handling of user's authentication state.&lt;/li&gt;
&lt;li&gt;Now you need to wait for the stream to emit a &lt;em&gt;signed in&lt;/em&gt; event so you can call the getProfile function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order for you to wait for that getProfile function, a future which you don't have access to in the current scope, you can use a &lt;code&gt;Completer&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;signedInCompleter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Completer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;listenToAuthChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FirebaseAuth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authStateChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;handleUserSignedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handleUserSignedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FirebaseFirestore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users/&lt;/span&gt;&lt;span class="si"&gt;${user.uid}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;signedInCompleter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isCompleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;signedInCompleter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;on&lt;/span&gt; &lt;span class="n"&gt;FirebaseException&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;someOtherFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;signedInCompleter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you should also be careful here, because if you call &lt;code&gt;.complete&lt;/code&gt; on the completer once it has already completed, it will throw an error. Make sure to call the &lt;code&gt;.isCompleted&lt;/code&gt; getter on the completer first.&lt;/p&gt;

&lt;p&gt;Once you've got your completer you can then wait for the future to complete by awaiting the &lt;code&gt;.future&lt;/code&gt; property of the completer.&lt;/p&gt;

&lt;h4&gt;
  
  
  Timer
&lt;/h4&gt;

&lt;p&gt;An honorable, and necessary mention is the &lt;a href="https://api.dart.dev/stable/latest/dart-async/Timer-class.html" rel="noopener noreferrer"&gt;Timer&lt;/a&gt; object. This is a great utility for running some code with a delay, just like Future.delayed, but more importantly for running some code &lt;strong&gt;periodically&lt;/strong&gt;. You can also get the current &lt;a href="https://api.dart.dev/stable/latest/dart-async/Timer/tick.html" rel="noopener noreferrer"&gt;tick&lt;/a&gt; of the timer and check if it &lt;a href="https://api.dart.dev/stable/latest/dart-async/Timer/isActive.html" rel="noopener noreferrer"&gt;isActive&lt;/a&gt; . And since we're already talking about time, I might as well mention the &lt;a href="https://api.dart.dev/stable/latest/dart-core/Stopwatch-class.html" rel="noopener noreferrer"&gt;Stopwatch&lt;/a&gt; class for measuring elapsed time, like when measuring the performance of specific async calls.&lt;/p&gt;

&lt;h4&gt;
  
  
  FutureBuilder
&lt;/h4&gt;

&lt;p&gt;Everything we've talked about so far was purely Dart, but now we're in Flutter territory. &lt;a href="https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html" rel="noopener noreferrer"&gt;FutureBuilder&lt;/a&gt; is the widget that gives us some quite useful APIs to handle different states of our Future.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// note the absence of the await keyword&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@override&lt;/span&gt;
&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FutureBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;future:&lt;/span&gt; &lt;span class="n"&gt;_fetchData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connectionState&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ConnectionState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;waiting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Show loading spinner&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Error: &lt;/span&gt;&lt;span class="si"&gt;${snapshot.error}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Show error message&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Data: &lt;/span&gt;&lt;span class="si"&gt;${snapshot.data}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Show fetched data&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's really simple - you give it a Future, and a builder callback which includes the &lt;a href="https://api.flutter.dev/flutter/widgets/AsyncSnapshot-class.html" rel="noopener noreferrer"&gt;AsyncSnapshot&lt;/a&gt; object. This object has a few properties we can use to render different UI based on the state of the Future - &lt;code&gt;.hasData&lt;/code&gt; and &lt;code&gt;.hasError&lt;/code&gt; or we can just check that &lt;code&gt;.data&lt;/code&gt; or &lt;code&gt;.error&lt;/code&gt; are null or not.&lt;/p&gt;

&lt;p&gt;When the future is still loading, we can show a loading indicator, when we've got data we render our data state and if there's an error we render the error state. Of course we can also go deeper into each branch to render different UI based on different data and errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note that I've created the future outside of the FutureBuilder widget.&lt;/strong&gt; If you create the future inside the FutureBuilder, as so: &lt;code&gt;future: fetchDataFuture()&lt;/code&gt;, then the future will be called on every rebuild of the FutureBuilder's parent widget. And because theoretically that can happen at most once every frame, we certainly don't want to call it 60 times per second. The UI will be unusable and we risk sending a ton of requests to our resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  Streams
&lt;/h3&gt;

&lt;p&gt;Another piece of the asynchronicity puzzle are &lt;a href="https://api.dart.dev/stable/latest/dart-async/Stream-class.html" rel="noopener noreferrer"&gt;Streams&lt;/a&gt;. These objects allow to continuously send data back and forth within your app and between your app and the outside world, via WebSockets or EventChannels for example. I have actually already mentioned streams above, when talking about the &lt;code&gt;authStateChanges&lt;/code&gt; stream from the FirebaseAuth package.&lt;/p&gt;

&lt;p&gt;There are two main types of streams - &lt;strong&gt;single-subscription&lt;/strong&gt; and &lt;strong&gt;broadcast&lt;/strong&gt; streams. &lt;/p&gt;

&lt;h4&gt;
  
  
  Single-subscription streams
&lt;/h4&gt;

&lt;p&gt;Single-&lt;em&gt;subscription&lt;/em&gt; streams are the default and are called that way because when you call the &lt;a href="https://api.dart.dev/stable/latest/dart-async/Stream/listen.html" rel="noopener noreferrer"&gt;.listen&lt;/a&gt; method on a Stream, it returns a &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamSubscription-class.html" rel="noopener noreferrer"&gt;StreamSubscription&lt;/a&gt; object. This subscription object lets you control the stream using the &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamSubscription/pause.html" rel="noopener noreferrer"&gt;pause&lt;/a&gt;, &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamSubscription/resume.html" rel="noopener noreferrer"&gt;resume&lt;/a&gt; and &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamSubscription/cancel.html" rel="noopener noreferrer"&gt;cancel&lt;/a&gt; methods.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;single&lt;/em&gt; part in the name means you can only subscribe to the stream once. Single-subscription streams &lt;strong&gt;start emitting events only once they are being listened to&lt;/strong&gt;, which means you can be sure to get &lt;em&gt;all&lt;/em&gt; of the emitted events when you listen to this stream. If you try to listen to the same stream again, you will get an exception.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
I haven't had the need to create single-subscription streams yet, but I do see how they can be useful if you need to process some large amount of data in chunks once and be done with it, like in file I/O and network requests. But usually that kind of thing is abstracted away for you by the SDK and other libraries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first way of creating single-subscription streams is by using the &lt;code&gt;async*&lt;/code&gt; and &lt;code&gt;yield&lt;/code&gt; keywords.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buildStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;buildStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seconds:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second way is using the &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamController-class.html" rel="noopener noreferrer"&gt;StreamController&lt;/a&gt;. You initialize the controller, call the &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamController/add.html" rel="noopener noreferrer"&gt;.add&lt;/a&gt; method to add events to it and listen to the emitted events using the &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamController/stream.html" rel="noopener noreferrer"&gt;.stream&lt;/a&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that by default, the controller creates a single-subscription stream, so we will not miss any events here just because we've started listening to it after adding the events.&lt;/p&gt;

&lt;p&gt;In this example I've used the &lt;code&gt;.listen&lt;/code&gt; method to listen to the events, but you can also use the &lt;code&gt;await for&lt;/code&gt; syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;await for&lt;/code&gt; syntax works the same way for Streams as the &lt;code&gt;await&lt;/code&gt; keyword for does for Futures, so when an error is thrown inside the stream, a &lt;code&gt;try/catch&lt;/code&gt; statement outside will catch it and the stream will stop producing events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Whoops!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// prints out&lt;/span&gt;
&lt;span class="c1"&gt;// 2025-03-06 14:35:15.635&lt;/span&gt;
&lt;span class="c1"&gt;// 2025-03-06 14:35:15.837&lt;/span&gt;
&lt;span class="c1"&gt;// Whoops!&lt;/span&gt;

&lt;span class="c1"&gt;// .. no more events&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't want to exit the stream on error, you can use the &lt;a href="https://api.dart.dev/stable/latest/dart-async/Stream/handleError.html" rel="noopener noreferrer"&gt;handleError&lt;/a&gt; wrapper method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;handleError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're using the asynchronous &lt;code&gt;.listen&lt;/code&gt; approach, set the &lt;code&gt;cancelOnError&lt;/code&gt; parameter to false.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="nl"&gt;cancelOnError:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Broadcast streams
&lt;/h4&gt;

&lt;p&gt;These are in my opinion the more useful kind of stream, because you can listen to it from multiple places. &lt;/p&gt;

&lt;p&gt;You can create broadcast streams in &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamController/StreamController.broadcast.html" rel="noopener noreferrer"&gt;two&lt;/a&gt; &lt;a href="https://api.dart.dev/stable/latest/dart-async/Stream/asBroadcastStream.html" rel="noopener noreferrer"&gt;ways&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// way number 1&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// way number 2&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buildStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asBroadcastStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first way is by using the &lt;code&gt;.broadcast&lt;/code&gt; StreamController constructor and the second is by using the &lt;code&gt;.asBroadcastStream&lt;/code&gt; method on the stream itself.&lt;/p&gt;

&lt;h4&gt;
  
  
  Transformations
&lt;/h4&gt;

&lt;p&gt;You can transform streams in multiple ways. The most common one will probably be via the &lt;a href="https://api.dart.dev/stable/latest/dart-async/Stream-class.html#instance-methods" rel="noopener noreferrer"&gt;methods&lt;/a&gt; available on the Stream object, many of which are the same as those on the &lt;a href="https://api.dart.dev/stable/latest/dart-core/Iterable-class.html" rel="noopener noreferrer"&gt;Iterable&lt;/a&gt; class, like &lt;code&gt;.map&lt;/code&gt;, &lt;code&gt;.cast&lt;/code&gt;, &lt;code&gt;.where&lt;/code&gt;, &lt;code&gt;.expand&lt;/code&gt;, and more.&lt;/p&gt;

&lt;p&gt;You can also encapsulate transformations by extending a &lt;a href="https://api.dart.dev/stable/latest/dart-async/StreamTransformer-class.html" rel="noopener noreferrer"&gt;StreamTransformer&lt;/a&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingTransformer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StreamTransformer&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@override&lt;/span&gt;
    &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Processing: &lt;/span&gt;&lt;span class="si"&gt;$data&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoggingTransformer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  StreamBuilder
&lt;/h4&gt;

&lt;p&gt;Similarly to FutureBuilders, &lt;a href="https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html" rel="noopener noreferrer"&gt;StreamBuilder&lt;/a&gt; is a specific Flutter widget that supports building different UI based on a given stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_stream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// note the absence of the await keyword&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@override&lt;/span&gt;
&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;StreamBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;stream:&lt;/span&gt; &lt;span class="n"&gt;_stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connectionState&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ConnectionState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;waiting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Show loading spinner&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Error: &lt;/span&gt;&lt;span class="si"&gt;${snapshot.error}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Show error message&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Data: &lt;/span&gt;&lt;span class="si"&gt;${snapshot.data}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Show fetched data&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Async programming is something you will constantly use when developing Flutter apps, so make sure to study these concepts and try them out on your own so you know what to use and when!&lt;/p&gt;

&lt;p&gt;There are a lot of things I haven't covered here due to time constraints and the volume of information, so make sure to check out the sources if you want to get a deeper picture on async programming for Dart &amp;amp; Flutter.&lt;/p&gt;

&lt;p&gt;This article is also more focused on the Flutter side of this concept, so I suggest you to invest more time into Streams, Zones and Isolates if you are developing server-side apps with Dart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dart.dev/libraries/dart-async" rel="noopener noreferrer"&gt;General dart:async docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dart.dev/libraries/async/async-await" rel="noopener noreferrer"&gt;Async/Await&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://api.dart.dev/stable/latest/dart-async/index.html" rel="noopener noreferrer"&gt;dart:async API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dart.dev/libraries/async/using-streams" rel="noopener noreferrer"&gt;Using streams&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
    </item>
    <item>
      <title>A Simple Guide to The Application Development Process</title>
      <dc:creator>Elisey Ozerov</dc:creator>
      <pubDate>Fri, 14 Jan 2022 10:06:33 +0000</pubDate>
      <link>https://dev.to/eliseyozerov/a-simple-guide-to-the-application-development-process-290a</link>
      <guid>https://dev.to/eliseyozerov/a-simple-guide-to-the-application-development-process-290a</guid>
      <description>&lt;p&gt;&lt;em&gt;This is an opinion piece formed by my 2+ years of working at a software development agency and observing the management of its projects from start to finish. Some of my ideas might be obvious, some questionable or wrong. Please don't hesitate to present any constructive criticism you might find pertinent.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;The basic business model for agencies is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find&lt;/strong&gt; a client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make&lt;/strong&gt; a product they need&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transfer&lt;/strong&gt; said product for money&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Finding clients is beyond the scope of this article, so I won't talk about it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To make a product, we need to know what it is.&lt;br&gt;
To successfully transfer it, the client needs to be satisfied.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Enter expectations vs reality
&lt;/h2&gt;

&lt;p&gt;If reality is worse than expectations, satisfaction is negative.&lt;br&gt;
If reality is better than expectations, satisfaction is positive.&lt;/p&gt;

&lt;p&gt;Reality - Expectations = Satisfaction&lt;/p&gt;

&lt;h4&gt;
  
  
  How to quantify expectations?
&lt;/h4&gt;

&lt;p&gt;By compiling a list of the acceptance criteria.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to do that?
&lt;/h4&gt;

&lt;p&gt;By describing what the app needs to display, how will the users interact with it and what happens when they do.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying the contents
&lt;/h2&gt;

&lt;p&gt;The basic premise of applications is that the user has a problem and the application proposes a solution to this problem. An interface, which the user interacts with, resulting in either a successful or an unsuccessful outcome.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Define the problem (users need groceries every week)&lt;/li&gt;
&lt;li&gt;Propose a solution (an app that manages the delivery of groceries)&lt;/li&gt;
&lt;li&gt;Define the features (choosing the groceries and delivery schedule)&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  User Interface
&lt;/h4&gt;

&lt;p&gt;Define the UI for every part of those features (choosing the test, entering the necessary information, accepting terms &amp;amp; conditions and confirming the purchase, etc.). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The UI is the general design, its components, their styles and organization within the application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each UI component must be extracted from the general design of the application and presented in &lt;strong&gt;every&lt;/strong&gt; state, including the animation stops. The styles such as &lt;u&gt;text styles, colors, gradients, spacings, border radiuses and shadows&lt;/u&gt; must be listed separately from the general design to be easily accessible and reusable.&lt;/p&gt;

&lt;p&gt;Each component and style must be meaningfully named to ease communication and documentation. By extracting and naming each style and component, we are forced to create a cohesive application that looks uniform and is simple to develop. Also, every asset like icons and images have to be appropriately sized and exported.&lt;/p&gt;

&lt;h4&gt;
  
  
  Functionality
&lt;/h4&gt;

&lt;p&gt;All logic in an application is triggered to execute. Those triggers can be gestures &lt;em&gt;(tap, double tap, long press, swipe, scroll, pinch, pan, etc.)&lt;/em&gt;, timers, network connections, app lifecycle events and other OS callbacks. We need to define all those triggers across the application. Then we define the desired outcomes for the logic triggered by those triggers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Application flow
&lt;/h4&gt;

&lt;p&gt;We finally create a graph of all the UI states before, during and after every action on a component, as well as pending states during the execution of the functionality triggered by that action. This graph contains the complete user flow throughout the application lifecycle. Might seem hard to do, but it is really just connecting all the dots we’ve defined in the previous steps.&lt;/p&gt;

&lt;p&gt;By now, we should have &lt;strong&gt;three&lt;/strong&gt; documents.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The design system document (the UI components, their states/variants and styles).&lt;/li&gt;
&lt;li&gt;The functionality document with the defined triggers and logic triggered by them (in written form).&lt;/li&gt;
&lt;li&gt;The application flow document with all the states connected together (in visual or interactive form).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since we are in the software development business, these documents should of course be linked for the information to be easily accessible between them.&lt;/p&gt;

&lt;p&gt;With these three documents, it is now set in stone how the application shall look and respond to interactions and so the expectations have been set. Of course client requirements change all the time, so this composable system also makes it possible to swap out and modify UI elements and functionalities. The amendments however, do need to be documented, so there is no misunderstandings about the acceptance criteria down the line. Going fast and breaking things might be great for prototyping, but not for building final products.&lt;/p&gt;

&lt;p&gt;Now to the easy part.&lt;br&gt;
 &lt;/p&gt;

&lt;h2&gt;
  
  
  The development
&lt;/h2&gt;

&lt;p&gt;Developing the application after receiving the acceptance criteria is its own story. The codebase needs to reflect the composability of the design and be easily maintainable and extendable.&lt;/p&gt;

&lt;p&gt;The codebase is a product in itself, regardless of the functionalities it’s supporting. No matter the product, any developer should be able to understand how the application was designed just by looking at its codebase.&lt;/p&gt;

&lt;h4&gt;
  
  
  Planning
&lt;/h4&gt;

&lt;p&gt;In the planning phase, we decide on the technologies and tools, state management and data persistence solutions, the architecture of the app and the folder structure, the local (OS) and remote APIs that will be needed. Also, it is logical to work with the tools one knows best, but it might be beneficial to try different technologies on the side, in case some of them prove better than the existing ones.&lt;/p&gt;

&lt;p&gt;All the planning decisions need to be documented in a technical specification, to eliminate uncertainty in the future in case there is a temptation to switch tools.&lt;/p&gt;

&lt;p&gt;In case the client is responsible for backend development, once the business logic was defined in the acceptance criteria, the client needs to provide accurate documentation for the endpoints needed to interact with their servers. Of course some clients are busy and already have a developed backend (e.g. in case the mobile application will support an existing web app), in which case they should either compile a list of endpoints that can be used for the needed user actions, or they must be notified that figuring out their API will take time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Development
&lt;/h4&gt;

&lt;p&gt;There needs to be a team lead setting the rules for the project and all the code must be approved by them before being merged. This is someone that knows everything about the technological side of development and can clearly communicate any issues to the project manager. The project manager has to prepare the backlog of issues in conjunction with the team lead, so there’s no questioning the tasks that need to be done and no developer stays without work at any time during the project. I won’t go into project management methodologies, but that’s the gist of it.&lt;/p&gt;

&lt;p&gt;I find GitFlow a logical git workflow, so I recommend it be used for most projects. Commits should be concise and contain as few changes as possible while still making sense. Developers mustn’t commit multiple unrelated changes within a file at the same time. Commit messages should be short and to the point, keeping the infinitive verb form (add, fix, merge, replace, etc.).&lt;/p&gt;

&lt;p&gt;Components should be developed as a part of the feature they belong to, unless they are used in several features that will be developed simultaneously. That makes them common, and as such, they should be developed before their dependent features, to limit merge conflicts. If a dependent feature is already in development, the other feature should have a stubbed component to be replaced by the developing one. In other words, there shouldn’t be multiple developers writing the same code.&lt;/p&gt;

&lt;p&gt;Different developers use different IDEs, configurations and coding styles, so linting rules must be specified that standardise the code format, allowing for easier collaboration, debugging, as well as monitoring changes. Auto formatting a document where only one line was actually changed makes it nearly impossible to find the change if the whole page looks different. SOLID &amp;amp; Clean Code principles should be followed as much as possible.&lt;/p&gt;

&lt;p&gt;Since we’ve clearly separated the UI from the business logic in the “Identifying the contents” section of this document, they can be developed separately.&lt;/p&gt;

&lt;p&gt;All the existing styles need to be defined before doing any UI work, to minimize any merge conflicts if different developers edit the styles at the same time. Of course it is impossible to eliminate conflicts altogether, but we should aim for as few as possible. Flutter provides a framework for defining themes, which is worth looking into to limit files with constants and increase dynamic styling in case it is needed.&lt;/p&gt;

&lt;p&gt;Once the technologies and APIs have been defined, there need to be wrappers written to abstract out the implementations of the APIs so they can be easily replaced later.&lt;/p&gt;

&lt;p&gt;These are all the miscellaneous rules I think need to be specified for development. Now the only thing left is to clear out the backlog of issues.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing
&lt;/h4&gt;

&lt;p&gt;Testing is of course a big part of building anything, unless it’s my castle in Minecraft. Don’t touch that. An agency should have at least one QA engineer that is experienced in testing applications both manually and automatically. As far as I know, they are the ones writing integration tests and making sure the application works as intended, not the project managers. They are also the ones rejecting the code in pull requests. Also they know how to investigate issues and write bug reports so the developers don’t waste time trying to figure out if there is actually a bug or someone just fat-fingered a button. Of course, developers don’t get out scot free. I believe it is the developers’ job to write unit &amp;amp; UI tests and making sure their own code works.&lt;/p&gt;

&lt;h4&gt;
  
  
  Release
&lt;/h4&gt;

&lt;p&gt;In the agile world, something like a bi-weekly release is a reasonable thing to do in my opinion, since it might take a couple days to incorporate the code built by different developers, thus leaving a whole week for development. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;p&gt;This document was long, but I’ve tried to keep it as concise as possible, barring a couple jokes in the last few sentences. Software development is a complicated business and agencies don’t need to complicate it even more by working without structure.&lt;/p&gt;

&lt;p&gt;If this whole process is done correctly, the reality will likely be even better than the expectations defined in the acceptance criteria, and the company will gain (or keep) one more satisfied customer.&lt;/p&gt;

&lt;p&gt;I am aware, that for some companies, this approach will introduce some short-term costs. It takes time to adopt a new process, to learn and become efficient with seemingly unimportant, but time consuming tasks. However, I believe once such process becomes second nature, it will greatly increase the productivity and profitability of making and transferring products to clients in exchange for money.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>agency</category>
      <category>management</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
