<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://iiro.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://iiro.dev/" rel="alternate" type="text/html" /><updated>2025-10-30T18:52:04+00:00</updated><id>https://iiro.dev/feed.xml</id><title type="html">iiro.dev</title><subtitle>Dart and Flutter tutorials, articles, tips and tricks. Sometimes other stuff too.
</subtitle><author><name>Iiro Krankka</name></author><entry><title type="html">Quickie: AdaptiveImageProvider</title><link href="https://iiro.dev/adaptive-images/" rel="alternate" type="text/html" title="Quickie: AdaptiveImageProvider" /><published>2021-10-05T00:00:00+00:00</published><updated>2021-10-05T00:00:00+00:00</updated><id>https://iiro.dev/adaptive-images</id><content type="html" xml:base="https://iiro.dev/adaptive-images/"><![CDATA[<p>Here’s a little snippet that I made a couple of years ago that seems to be following me from a Flutter project to another.</p>

<p>It’s a little class that I call <code class="language-plaintext highlighter-rouge">AdaptiveImageProvider</code>:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code>
<span class="dir"><span class="b">import</span> <span class="ls">'package:flutter&#47;painting.dart'</span><span class="semicolon">;</span></span>

<span class="k">class</span> <span class="cl">AdaptiveImageProvider</span> <span class="k">extends</span> <span class="cl">ImageProvider</span> {
  <span class="cl">AdaptiveImageProvider</span>(<span class="cl">String</span> <span class="pd">url</span>) : <span class="ifr">_delegate</span> = <span class="smr">_resolve</span>(<span class="pr">url</span>)<span class="semicolon">;</span>
  <span class="k">final</span> <span class="cl">ImageProvider</span> <span class="ifd">_delegate</span><span class="semicolon">;</span>

  <span class="b">static</span> <span class="cl">ImageProvider</span> <span class="smd">_resolve</span>(<span class="cl">String</span> <span class="pd">url</span>) {
    <span class="k">final</span> <span class="lvd">uri</span> = <span class="cl">Uri</span>.<span class="smr">parse</span>(<span class="pr">url</span>)<span class="semicolon">;</span>
    <span class="k">switch</span> (<span class="lvr">uri</span>.<span class="igr">scheme</span>) {
      <span class="k">case</span> <span class="ls">'asset'</span>:
        <span class="k">final</span> <span class="lvd">path</span> = <span class="lvr">uri</span>.<span class="imr">toString</span>().<span class="imr">replaceFirst</span>(<span class="ls">'asset:&#47;&#47;'</span><span class="comma">,</span> <span class="ls">''</span>)<span class="semicolon">;</span>
        <span class="k">return</span> <span class="ctr">AssetImage</span>(<span class="lvr">path</span>)<span class="semicolon">;</span>
      <span class="k">case</span> <span class="ls">'file'</span>:
        <span class="k">final</span> <span class="lvd">file</span> = <span class="ctr">File</span>.<span class="ctr">fromUri</span>(<span class="lvr">uri</span>)<span class="semicolon">;</span>
        <span class="k">return</span> <span class="ctr">FileImage</span>(<span class="lvr">file</span>)<span class="semicolon">;</span>
      <span class="k">case</span> <span class="ls">'http'</span>:
      <span class="k">case</span> <span class="ls">'https'</span>:
        <span class="k">return</span> <span class="ctr">NetworkImage</span>(<span class="pr">url</span>)<span class="semicolon">;</span>
      <span class="k">default</span>:
        <span class="k">throw</span> <span class="ctr">ArgumentError</span>(<span class="ls">'Unsupported scheme: <span class="lvr">${<span class="lvr">uri</span>.<span class="igr">scheme</span>}</span>'</span>)<span class="semicolon">;</span>
    }
  }

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">ImageStreamCompleter</span> <span class="imd">load</span>(<span class="cl">Object</span> <span class="pd">key</span><span class="comma">,</span> <span class="fnta">DecoderCallback</span> <span class="pd">decode</span>) =&gt;
      <span class="igr">_delegate</span>.<span class="imr">load</span>(<span class="pr">key</span><span class="comma">,</span> <span class="pr">decode</span>)<span class="semicolon">;</span>

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Future</span>&lt;<span class="cl">Object</span>&gt; <span class="imd">obtainKey</span>(<span class="cl">ImageConfiguration</span> <span class="pd">configuration</span>) =&gt;
      <span class="igr">_delegate</span>.<span class="imr">obtainKey</span>(<span class="pr">configuration</span>)<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>You can give this class pretty much any URL and it will poop out an <code class="language-plaintext highlighter-rouge">ImageProvider</code> that knows how to display an image using that URL.</p>

<blockquote>
  <p>“That’s cool, I guess. But why?”</p>
</blockquote>

<p>Let’s imagine we have a widget called <code class="language-plaintext highlighter-rouge">Avatar</code>.
We give it a <code class="language-plaintext highlighter-rouge">url</code> that points to an image, and it will be displayed in the shape of a circle:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">Avatar</span> <span class="k">extends</span> <span class="cl">StatelessWidget</span> {
  <span class="k">const</span> <span class="cl">Avatar</span>({<span class="k">required</span> <span class="k">this</span>.<span class="ifr">url</span>})<span class="semicolon">;</span>
  <span class="k">final</span> <span class="cl">String</span> <span class="ifd">url</span><span class="semicolon">;</span>

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="k">return</span> <span class="ctr">ClipOval</span>(
      <span class="pr">child</span>: <span class="ctr">Image</span>(
        <span class="dpr">image</span>: <span class="ctr">AdaptiveImageProvider</span>(<span class="igr">url</span>)<span class="comma">,</span>
        <span class="pr">width</span>: <span class="li">56</span><span class="comma">,</span>
        <span class="pr">height</span>: <span class="li">56</span><span class="comma">,</span>
        <span class="pr">fit</span>: <span class="en">BoxFit</span>.<span class="enc">cover</span><span class="comma">,</span>
      )<span class="comma">,</span>
    )<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>In our app, users can upload their own pictures to set as their avatars.</p>

<p>As such, there are three possible URLs that the <code class="language-plaintext highlighter-rouge">Avatar</code> widget has to handle:</p>
<ol>
  <li>user hasn’t uploaded their avatar image - use a preloaded image, e.g. <code class="language-plaintext highlighter-rouge">assets://images/person.png</code></li>
  <li>the user is offline - display a locally cached image, e.g. <code class="language-plaintext highlighter-rouge">file://path/to/image.png</code></li>
  <li>someone is viewing the users’ profile for the first time - display an image from the web, e.g. <code class="language-plaintext highlighter-rouge">https://example.com/images/abc123.png</code></li>
</ol>

<p>The <code class="language-plaintext highlighter-rouge">AdaptiveImageProvider</code> class makes this easy - just provide a <code class="language-plaintext highlighter-rouge">url</code> and it will figure out how to display it.
And since it’s extending from the <code class="language-plaintext highlighter-rouge">ImageProvider</code> class that comes from Flutter, you can use it with popular libraries,
such as cached_network_image and others.</p>

<h2 id="other-use-cases">Other use cases</h2>
<p>Although the above use cases are the more common ones, the list of possible applications doesn’t end there.</p>

<p>Here are some more:</p>
<ul>
  <li>making an in-memory mode of your app where nothing goes to server, where server sync is a premium feature</li>
  <li>running your app in a “lorem ipsum” mode with placeholder asset images to automate taking app store screenshots in multiple languages</li>
  <li>creating a “retail demo” version of your app for Apple, so that they can showcase it in Apple Stores (this actually happened at my previous workplace)</li>
</ul>

<p>I’m sure there are more possible applications, but here are the ones from the top of my head.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[How to display images from multiple different sources easily and cleanly in Flutter.]]></summary></entry><entry><title type="html">Flutter’s setState() might not be what you think it is</title><link href="https://iiro.dev/set-state/" rel="alternate" type="text/html" title="Flutter’s setState() might not be what you think it is" /><published>2021-03-29T00:00:00+00:00</published><updated>2021-03-29T00:00:00+00:00</updated><id>https://iiro.dev/set-state</id><content type="html" xml:base="https://iiro.dev/set-state/"><![CDATA[<p>Here’s a somewhat embarrassing assumption I made about <code class="language-plaintext highlighter-rouge">setState</code> when I started to learn Flutter
<a href="https://www.codemate.com/considering-flutter/">almost 4 years ago</a>.</p>

<p>We all know <code class="language-plaintext highlighter-rouge">setState</code> from the counter example:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">_MyWidgetState</span> <span class="k">extends</span> <span class="cl">State</span>&lt;<span class="cl">MyWidget</span>&gt; {
  <span class="cl">int</span> <span class="ifd">_counter</span> = <span class="li">0</span><span class="semicolon">;</span>

  <span class="k">void</span> <span class="imd">_incrementCounter</span>() {
    <span class="imr">setState</span>(() {
      <span class="isr">_counter</span> = <span class="igr">_counter</span> + <span class="li">1</span><span class="semicolon">;</span>
    })<span class="semicolon">;</span>
  }

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="k">return</span> <span class="ctr">Text</span>(<span class="ls">"The current value is <span class="lvr">$<span class="igr">_counter</span></span>!"</span>)<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>Here’s how <em>I thought</em> it worked:</p>
<ol>
  <li>There’s one stateful field called <code class="language-plaintext highlighter-rouge">_counter</code>.</li>
  <li>We also have a <code class="language-plaintext highlighter-rouge">Text</code> widget that displays the value of <code class="language-plaintext highlighter-rouge">_counter</code>.</li>
  <li>Every time we want to update <code class="language-plaintext highlighter-rouge">_counter</code>, we also have to wrap it in an anonymous function and pass it to <code class="language-plaintext highlighter-rouge">setState</code>.
Otherwise, the framework doesn’t know what was updated.</li>
</ol>

<p>Four months into my Flutter journey, I found out that <strong>assumption number three was not true</strong>.</p>

<p>We do have to call <code class="language-plaintext highlighter-rouge">setState</code> when updating <code class="language-plaintext highlighter-rouge">_counter</code>, but there’s absolutely no need to do it in an anonymous callback.</p>

<p>This:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="imr">setState</span>(() {
  <span class="isr">_counter</span> = <span class="igr">_counter</span> + <span class="li">1</span><span class="semicolon">;</span>
})<span class="semicolon">;</span>
</code></pre></div></div>

<p>is exactly the same as this:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="isr">_counter</span> = <span class="igr">_counter</span> + <span class="li">1</span><span class="semicolon">;</span>
<span class="imr">setState</span>(() {})<span class="semicolon">;</span>
</code></pre></div></div>

<p>The Flutter framework <strong>does not</strong> magically inspect code inside the anonymous function and then do some
diffing to see what changed.</p>

<p>Whenever you call <code class="language-plaintext highlighter-rouge">setState</code>, the widget rebuilds - even if nothing changed and the callback passed to <code class="language-plaintext highlighter-rouge">setState</code> was empty.</p>

<p>What I had initially assumed about <code class="language-plaintext highlighter-rouge">setState</code> was totally made up in my own head.</p>

<h2 id="a-peek-behind-the-curtains">A peek behind the curtains</h2>

<p>Let’s see how the code behind <code class="language-plaintext highlighter-rouge">setState</code> looks like:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">protected</span></span>
<span class="k">void</span> <span class="imd">setState</span>(<span class="fnta">VoidCallback</span> <span class="pd">fn</span>) {
  <span class="k">assert</span>(<span class="pr">fn</span> != <span class="k">null</span>)<span class="semicolon">;</span>
  <span class="k">assert</span>(() {
    <span class="k">if</span> (<span class="tlgr">_debugLifecycleState</span> == <span class="en">_StateLifecycle</span>.<span class="enc">defunct</span>) {
      <span class="k">throw</span> <span class="ctr">FlutterError</span>.<span class="ctr">fromParts</span>(<span class="ll">[<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>]</span>)<span class="semicolon">;</span>
    }
    <span class="k">if</span> (<span class="tlgr">_debugLifecycleState</span> == <span class="en">_StateLifecycle</span>.<span class="enc">created</span> &amp;&amp; !<span class="igr">mounted</span>) {
      <span class="k">throw</span> <span class="ctr">FlutterError</span>.<span class="ctr">fromParts</span>(<span class="ll">[<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>]</span>)<span class="semicolon">;</span>
    }
    <span class="k">return</span> <span class="lb"><span class="k">true</span></span><span class="semicolon">;</span>
  }())<span class="semicolon">;</span>
  <span class="k">final</span> <span class="tnd">dynamic</span> <span class="dlvd">result</span> = <span class="pr">fn</span>() <span class="b">as</span> <span class="tnd">dynamic</span><span class="semicolon">;</span>
  <span class="k">assert</span>(() {
    <span class="k">if</span> (<span class="dlvr">result</span> <span class="k">is</span> <span class="cl">Future</span>) {
      <span class="k">throw</span> <span class="ctr">FlutterError</span>.<span class="ctr">fromParts</span>(<span class="ll">[<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>]</span>)<span class="semicolon">;</span>
    }
    <span class="k">return</span> <span class="k"><span class="lb">true</span></span><span class="semicolon">;</span>
  }())<span class="semicolon">;</span>
  <span class="igr">_element</span>.<span class="imr">markNeedsBuild</span>()<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>Let’s remove the assertions:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">protected</span></span>
<span class="k">void</span> <span class="imd">setState</span>(<span class="fnta">VoidCallback</span> <span class="pd">fn</span>) {
  <span class="k">final</span> <span class="tnd">dynamic</span> <span class="dlvd">result</span> = <span class="pr">fn</span>() <span class="b">as</span> <span class="tnd">dynamic</span><span class="semicolon">;</span>
  <span class="igr">_element</span>.<span class="imr">markNeedsBuild</span>()<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>We can actually boil it down even more:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">protected</span></span>
<span class="k">void</span> <span class="imd">setState</span>(<span class="fnta">VoidCallback</span> <span class="pd">fn</span>) {
  <span class="pr">fn</span>()<span class="semicolon">;</span>
  <span class="igr">_element</span>.<span class="imr">markNeedsBuild</span>()<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>All that <code class="language-plaintext highlighter-rouge">setState</code> does is calling the provided callback and then the associated element gets scheduled for a new build.</p>

<p>No magical field diffing here.</p>

<h2 id="so-why-do-we-have-setstate-then">So why do we have setState then?</h2>

<p>Because of UX studies ran by the Flutter team.</p>

<p>They do <a href="https://www.youtube.com/watch?v=Zx732oQN_Rs">a lot of it</a>.</p>

<p>Back in 2017 and 2018, there was a GitHub issue label called <em>“first hour”</em>. The Flutter team ran developer UX
studies and observed how Flutter newcomers would use Flutter when left on their own for an hour.
Those studies would shape future API decisions for Flutter.</p>

<p>Apparently, the <code class="language-plaintext highlighter-rouge">setState</code> thing is one of these findings.</p>

<p>From the <a href="https://github.com/flutter/flutter/issues/12296">GitHub issue</a> that made me realize I had been living in a lie:</p>

<blockquote>
  <p>We used to just have a markNeedsBuild method but we found people called it like a good luck charm – any time they 
weren’t sure if they needed to call it, they’d call it.</p>

  <p>We changed to a method that takes a (synchronously-invoked) callback, and suddenly people had much less trouble with it.</p>
</blockquote>

<p>There’s also <a href="https://stackoverflow.com/a/44379367">a StackOverflow answer</a> from Collin Jackson from the Flutter team back then:</p>

<blockquote>
  <p>When Flutter had a “markNeedsBuild” function, developers ended up just sort of calling it at random times.
When the syntax switched to setState((){ }), developers were much more likely to use the API correctly.</p>
</blockquote>

<p>So the whole <code class="language-plaintext highlighter-rouge">setState</code> API is just a mental trick.
And it seems to work - apparently it lead into people building their widgets less often.</p>

<p>I never had problems knowing when to call <code class="language-plaintext highlighter-rouge">setState</code>, but I can definitely see myself wondering <em>“who’s Mark and what
is he building?”</em></p>

<p>Mystery solved.</p>

<h2 id="conclusion">Conclusion</h2>

<p>What does all of this mean?</p>

<p>Should we now convert all our <code class="language-plaintext highlighter-rouge">setState</code> calls to something like this?</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="isr">_counter</span> = <span class="igr">_counter</span> + <span class="li">1</span><span class="semicolon">;</span>
<span class="imr">setState</span>(() {})<span class="semicolon">;</span>
</code></pre></div></div>

<p>Probably not.</p>

<p>Even though there’s no difference, calling <code class="language-plaintext highlighter-rouge">setState</code> with an anonymous callback feels right.</p>

<p>At this point, the anonymous callback is an established convention and anything else just feels weird.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[The story of how some people calling markNeedsBuild() "like a good luck charm" gave us the setState() API]]></summary></entry><entry><title type="html">Parsing and editing HTML in Dart - the right way™</title><link href="https://iiro.dev/parsing-html-in-dart/" rel="alternate" type="text/html" title="Parsing and editing HTML in Dart - the right way™" /><published>2020-11-29T00:00:00+00:00</published><updated>2020-11-29T00:00:00+00:00</updated><id>https://iiro.dev/parsing-html-in-dart</id><content type="html" xml:base="https://iiro.dev/parsing-html-in-dart/"><![CDATA[<p>During the last few months, I’ve been quietly building yet another side project.</p>

<p>In one component of that side project I needed to take in some HTML, change some things, and return slightly different HTML.</p>

<blockquote>
  <p>“I’ll just slap together some incomprehensible regex… and voilà! That wasn’t hard at all!”</p>
</blockquote>

<p>If you’ve ever gone on StackOverflow to find, or god forbid, ask for <em>the one HTML regex to rule them all</em>, somebody will tell you quite quickly that <a href="https://stackoverflow.com/a/1732454">it isn’t such a great idea</a>.</p>

<p>Since HTML is far from a regular language, parsing it with regular expressions can be full of weird edge cases.
The nice thing is that proper HTML parsers handle the edge cases for you.</p>

<p>As an added benefit, using a proper HTML parser is also simpler than hacking together some messy regular expressions.</p>

<h2 id="the-hello-world">The Hello World</h2>

<p>Given Dart’s history, it has a quite solid toolbox for working with all things <a href="https://pub.dev/packages/js">Javascript</a>, <a href="https://api.dart.dev/stable/2.7.1/dart-html/dart-html-library.html">the DOM</a>, <a href="https://pub.dev/packages/csslib">CSS</a> and, you guessed it, <a href="https://pub.dev/packages/html">parsing HTML</a>.</p>

<p>To get started, all we need to do is to add <code class="language-plaintext highlighter-rouge">package:html</code> to our pubspec file:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">dependencies</span><span class="pi">:</span>
  <span class="na">html</span><span class="pi">:</span> <span class="c1"># check pub.dev for latest version</span>
</code></pre></div></div>

<p>It’s time to parse some good old HTML.</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:html&#47;parser.dart'</span> <span class="b">as</span> <span class="ip">html_parser</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="k">final</span> <span class="lvd">document</span> = <span class="ip">html_parser</span>.<span class="tlfr">parseFragment</span>(<span class="ls">'''
    &lt;p&gt;&lt;a href=&quot;https:&#47;&#47;dart.dev&quot;&gt;Dart&lt;&#47;a&gt;&lt;&#47;p&gt;
    &lt;p&gt;&lt;a href=&quot;https:&#47;&#47;flutter.dev&quot;&gt;Flutter&lt;&#47;a&gt;&lt;&#47;p&gt;
  '''</span>)<span class="semicolon">;</span>

  <span class="k">final</span> <span class="lvd">anchors</span> = <span class="lvr">document</span>.<span class="imr">querySelectorAll</span>(<span class="ls">'a'</span>)<span class="semicolon">;</span>

  <span class="k">for</span> (<span class="k">final</span> <span class="lvd">anchor</span> <span class="k">in</span> <span class="lvr">anchors</span>) {
    <span class="k">final</span> <span class="lvd">href</span> = <span class="lvr">anchor</span>.<span class="igr">attributes</span>[<span class="ls">'href'</span>]<span class="semicolon">;</span>
    <span class="tlfr">print</span>(<span class="ls">'<span class="lvr">${<span class="lvr">anchor</span>.<span class="igr">text</span>}</span> - <span class="lvr">$<span class="lvr">href</span></span>'</span>)<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>That would print out the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Dart - https://dart.dev
Flutter - https://flutter.dev
</code></pre></div></div>

<p>Since we’re parsing some HTML, but not a whole web page, we used <code class="language-plaintext highlighter-rouge">parseFragment</code>.
If we were parsing a whole webpage, we’d use the <code class="language-plaintext highlighter-rouge">parse</code> method instead.</p>

<p>The <code class="language-plaintext highlighter-rouge">parseFragment</code> method gives us a DOM tree that kinda looks like this:</p>

<figure>
  <a href="/images/parsing-html-in-dart/dom-tree.png ">
    <img alt="
The resulting DOM tree from parsing the HTML text input.
" src="/images/parsing-html-in-dart/dom-tree.png " />
  </a>
  <figcaption>
    <p>The resulting DOM tree from parsing the HTML text input.</p>
  </figcaption>
</figure>

<aside style="top: 1630px ">
  <p>The diagram is missing a <code class="language-plaintext highlighter-rouge">List&lt;Node&gt;</code> between the <code class="language-plaintext highlighter-rouge">DocumentFragment</code> and <code class="language-plaintext highlighter-rouge">p</code> objects.</p>

  <p>I didn’t know how to make a clear diagram that showed the real structure.</p>
</aside>

<p>For simple cases, we can just use the <code class="language-plaintext highlighter-rouge">.querySelector()</code> method on the <code class="language-plaintext highlighter-rouge">document</code>.
This works when we want to find all the <code class="language-plaintext highlighter-rouge">&lt;a&gt;</code> elements on a page or something equally simple.</p>

<p>But if we’re building something more complex, like a pretty printer for HTML code, chances are we need to iterate over the list of nodes, and then use some recursion in order to process every single nested element.
We’ll have examples of this later.</p>

<h2 id="displaying-top-10-google-search-results">Displaying top 10 Google search results</h2>

<p>Let’s imagine we want to get the top 10 Google search results for the query <code class="language-plaintext highlighter-rouge">flutter blogs</code> and print them to the console.</p>

<p>Easy-peasy.</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:html&#47;parser.dart'</span> <span class="b">as</span> <span class="ip">html_parser</span><span class="semicolon">;</span></span>
<span class="dir"><span class="b">import</span> <span class="ls">'package:http&#47;http.dart'</span> <span class="b">as</span> <span class="ip">http</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() <span class="b">async</span> {
  <span class="ceol">&#47;&#47; Fetch Google search results for &quot;flutter blogs&quot;.</span>
  <span class="k">final</span> <span class="lvd">response</span> = <span class="b">await</span> <span class="ip">http</span>
      .<span class="tlfr">get</span>(<span class="cl">Uri</span>.<span class="smr">parse</span>(<span class="ls">'https:&#47;&#47;www.google.com&#47;search?q=flutter+blogs'</span>))<span class="semicolon">;</span>
  <span class="k">final</span> <span class="lvd">document</span> = <span class="ip">html_parser</span>.<span class="tlfr">parse</span>(<span class="lvr">response</span>.<span class="igr">body</span>)<span class="semicolon">;</span>

  <span class="ceol">&#47;&#47; Google has the best class names.</span>
  <span class="k">final</span> <span class="lvd">results</span> = <span class="lvr">document</span>.<span class="imr">getElementsByClassName</span>(<span class="ls">'BNeawe vvjwJb AP7Wnd'</span>)<span class="semicolon">;</span>

  <span class="tlfr">print</span>(<span class="ls">'Top 10 results for &quot;flutter blogs&quot;:<span class="backslash">&#92;</span>n<span class="backslash">&#92;</span>n'</span>)<span class="semicolon">;</span>

  <span class="k">var</span> <span class="lvd">placement</span> = <span class="li">1</span><span class="semicolon">;</span>
  <span class="k">for</span> (<span class="k">final</span> <span class="lvd">result</span> <span class="k">in</span> <span class="lvr">results</span>) {
    <span class="tlfr">print</span>(<span class="ls">'#<span class="lvr">$<span class="lvr">placement</span></span>: <span class="lvr">${<span class="lvr">result</span>.<span class="igr">text</span>}</span>'</span>)<span class="semicolon">;</span>
    <span class="lvr">placement</span>++<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>That should print this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Top 10 results for "flutter blogs":

#1: Flutter – Medium
#2: Flutter Blog by GeekyAnts – The GeekyAnts Blog
#3: Flutter Blogs (@FlutterBlogs) | Twitter
#4: Blogs Archive - FlutterDevs - Why Flutter?
#5: Guest Blog: Flutter Trends and Community Updates | Syncfusion Blogs
#6: The top 45 must-follow Flutter experts on Twitter - Codemagic blog
#7: iiro.dev | Dart and Flutter tutorials, articles, tips and tricks ...
#8: Flutter Blog Engine | ButterCMS
#9: Top Apps Made with Flutter – 17 Stories by Developers and ...
#10: Flutter vs React Native: A Developer's Perspective - Nevercode
</code></pre></div></div>

<aside style="top: unset ">
  <p>If that doesn’t work as intended, <a href="https://gist.github.com/roughike/2f9342233fefdd0e7f95de829b04f991">here’s the raw HTML response</a> I tested parsing with.</p>
</aside>

<p>I don’t know about the other ones, but #7 on that list is total garbage.</p>

<p>I’m not placing that high on search results anymore - I guess I should write more articles.</p>

<h2 id="editing-the-parsed-dom-tree">Editing the parsed DOM tree</h2>

<p>As the DOM tree is a nested structure of Dart objects, we can also easily modify it on the fly.</p>

<p>Let’s imagine we’re building a comment system in Dart.</p>

<p>Among other things, such as disallowing malicious HTML and sanitizing other content, we probably want to handle the links.</p>

<p>We’ll want to:</p>
<ol>
  <li>open all links in a new window</li>
  <li>tell search engines (such as Google) to not give these links credit in search engine rankings.</li>
</ol>

<p>The way to do it is to add <code class="language-plaintext highlighter-rouge">rel="external nofollow"</code> and <code class="language-plaintext highlighter-rouge">target="_blank"</code> to each <code class="language-plaintext highlighter-rouge">&lt;a&gt;</code> element.</p>

<p>This is one way to do it:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:html&#47;parser.dart'</span> <span class="b">as</span> <span class="ip">html_parser</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="k">final</span> <span class="lvd">document</span> = <span class="ip">html_parser</span>.<span class="tlfr">parseFragment</span>(<span class="ls">'''
    &lt;p&gt;Nice blog post! I also wrote about the same thing on my blog,
    &lt;a href=&quot;https:&#47;&#47;example.com&quot;&gt;check it out&lt;&#47;a&gt;!&lt;&#47;p&gt;
  '''</span>)<span class="semicolon">;</span>

  <span class="ceol">&#47;&#47; Find all anchor (&quot;&lt;a&gt;&quot;) elements in the parsed document.</span>
  <span class="k">final</span> <span class="lvd">anchors</span> = <span class="lvr">document</span>.<span class="imr">querySelectorAll</span>(<span class="ls">'a'</span>)<span class="semicolon">;</span>

  <span class="k">for</span> (<span class="k">final</span> <span class="lvd">anchor</span> <span class="k">in</span> <span class="lvr">anchors</span>) {
    <span class="ceol">&#47;&#47; Iterate over all &lt;a&gt; elements and add these attributes</span>
    <span class="ceol">&#47;&#47; to each one.</span>
    <span class="lvr">anchor</span>.<span class="igr">attributes</span>.<span class="imr">addAll</span>(<span class="lm">{
      <span class="ls">'target'</span>: <span class="ls">'_blank'</span><span class="comma">,</span>
      <span class="ls">'rel'</span>: <span class="ls">'nofollow'</span><span class="comma">,</span>
    }</span>)<span class="semicolon">;</span>
  }

  <span class="tlfr">print</span>(<span class="lvr">document</span>.<span class="igr">outerHtml</span>)<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>That should print the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;p&gt;Nice blog post! I also wrote about the same thing on my blog,
&lt;a href="https://example.com" target="_blank" rel="nofollow"&gt;check it out&lt;/a&gt;!&lt;/p&gt;
</code></pre></div></div>

<h2 id="allowing-only-specific-html-tags">Allowing only specific HTML tags</h2>

<p>Remember how I told there are two ways to process the DOM tree, the simple way and the less simple way?</p>

<p>It’s time to learn the less simple way.
But there’s no reason to worry - it’s not too hard!</p>

<p>Maybe we’re building a comment form, and only want to allow bold, italics, and underline text?</p>

<aside style="top: 4870px ">
  <p>If you need to strip all unsafe HTML in Dart, just use <a href="https://pub.dev/packages/sanitize_html">package:sanitize_html</a>.</p>

  <p>Somebody at Google already did it for you, so you don’t have to.</p>
</aside>

<p>Easy peasy:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'dart:convert'</span><span class="semicolon">;</span></span>

<span class="dir"><span class="b">import</span> <span class="ls">'package:html&#47;dom.dart'</span><span class="semicolon">;</span></span>
<span class="dir"><span class="b">import</span> <span class="ls">'package:html&#47;parser.dart'</span> <span class="b">as</span> <span class="ip">html_parser</span><span class="semicolon">;</span></span>
<span class="dir"><span class="b">import</span> <span class="ls">'package:meta&#47;meta.dart'</span><span class="semicolon">;</span></span>

<span class="k">final</span> <span class="tlvd">_htmlEscape</span> = <span class="ctr">HtmlEscape</span>(<span class="cl">HtmlEscapeMode</span>.<span class="sgr">element</span>)<span class="semicolon">;</span>

<span class="k">class</span> <span class="cl">LimitedHtml</span> {
  <span class="cl">LimitedHtml</span>({<span class="tlgr">required</span> <span class="k">this</span>.<span class="ifr">allowedTagNames</span>})<span class="semicolon">;</span>
  <span class="k">final</span> <span class="cl">Set</span>&lt;<span class="cl">String</span>&gt; <span class="ifd">allowedTagNames</span><span class="semicolon">;</span>

  <span class="cl">String</span> <span class="imd">filter</span>(<span class="cl">String</span> <span class="pd">html</span>) {
    <span class="k">final</span> <span class="lvd">buffer</span> = <span class="ctr">StringBuffer</span>()<span class="semicolon">;</span>
    <span class="k">final</span> <span class="lvd">document</span> = <span class="ip">html_parser</span>.<span class="tlfr">parseFragment</span>(<span class="pr">html</span>)<span class="semicolon">;</span>
    <span class="imr">_visitNodes</span>(<span class="lvr">buffer</span><span class="comma">,</span> <span class="lvr">document</span>.<span class="igr">nodes</span>)<span class="semicolon">;</span>

    <span class="k">return</span> <span class="lvr">buffer</span>.<span class="imr">toString</span>().<span class="imr">trim</span>()<span class="semicolon">;</span>
  }

  <span class="k">void</span> <span class="imd">_visitNodes</span>(<span class="cl">StringSink</span> <span class="pd">sink</span><span class="comma">,</span> <span class="cl">List</span>&lt;<span class="cl">Node</span>&gt; <span class="pd">nodes</span>) {
    <span class="k">for</span> (<span class="k">final</span> <span class="lvd">node</span> <span class="k">in</span> <span class="pr">nodes</span>) {
      <span class="k">if</span> (<span class="lvr">node</span> <span class="k">is</span> <span class="cl">Element</span>) {
        <span class="imr">_visitElement</span>(<span class="pr">sink</span><span class="comma">,</span> <span class="lvr">node</span>)<span class="semicolon">;</span>
      } <span class="k">else</span> <span class="k">if</span> (<span class="lvr">node</span> <span class="k">is</span> <span class="cl">Text</span>) {
        <span class="pr">sink</span>.<span class="imr">write</span>(<span class="tlgr">_htmlEscape</span>.<span class="imr">convert</span>(<span class="lvr">node</span>.<span class="igr">text</span>))<span class="semicolon">;</span>
      }
    }
  }

  <span class="k">void</span> <span class="imd">_visitElement</span>(<span class="cl">StringSink</span> <span class="pd">sink</span><span class="comma">,</span> <span class="cl">Element</span> <span class="pd">element</span>) {
    <span class="k">final</span> <span class="lvd">tag</span> = <span class="pr">element</span>.<span class="igr">localName</span>!.<span class="imr">toLowerCase</span>()<span class="semicolon">;</span>

    <span class="k">if</span> (<span class="igr">allowedTagNames</span>.<span class="imr">contains</span>(<span class="lvr">tag</span>)) {
      <span class="pr">sink</span>.<span class="imr">write</span>(<span class="ls">'&lt;<span class="lvr">$<span class="lvr">tag</span></span>&gt;'</span>)<span class="semicolon">;</span>
      <span class="imr">_visitNodes</span>(<span class="pr">sink</span><span class="comma">,</span> <span class="pr">element</span>.<span class="igr">nodes</span>)<span class="semicolon">;</span>
      <span class="pr">sink</span>.<span class="imr">write</span>(<span class="ls">'&lt;/<span class="lvr">$<span class="lvr">tag</span></span>&gt;'</span>)<span class="semicolon">;</span>
    } <span class="k">else</span> {
      <span class="pr">sink</span>.<span class="imr">write</span>(<span class="pr">element</span>.<span class="igr">text</span>)<span class="semicolon">;</span>
    }
  }
}
</code></pre></div></div>

<p>In just roughly 30 lines, we built a reusable class that takes in a list of <code class="language-plaintext highlighter-rouge">allowedTagNames</code> and ignores everything else.</p>

<p>Let’s try it in action:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="k">final</span> <span class="lvd">html</span> = <span class="ls">'''
   &lt;p&gt;I can&#39;t &lt;u&gt;underline&lt;&#47;u&gt; enough how &lt;b&gt;bold&lt;&#47;b&gt; and &lt;i&gt;italic&lt;&#47;i&gt; night it was!&lt;&#47;p&gt;
   &lt;div&gt;
    &lt;a href=&quot;javascript:doSomethingBad()&quot;&gt;Click me please!&lt;&#47;a&gt;
   &lt;&#47;div&gt;
  '''</span><span class="semicolon">;</span>

  <span class="k">final</span> <span class="lvd">limitedHtml</span> = <span class="ctr">LimitedHtml</span>(<span class="pr">allowedTagNames</span>: {<span class="ls">'p'</span><span class="comma">,</span> <span class="ls">'b'</span><span class="comma">,</span> <span class="ls">'i'</span><span class="comma">,</span> <span class="ls">'u'</span>})<span class="semicolon">;</span>
  <span class="tlfr">print</span>(<span class="lvr">limitedHtml</span>.<span class="imr">filter</span>(<span class="lvr">html</span>))<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>That would print the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;p&gt;I can't &lt;u&gt;underline&lt;/u&gt; enough how &lt;b&gt;bold&lt;/b&gt; and &lt;i&gt;italic&lt;/i&gt; night it was!&lt;/p&gt;
   
Click me please!
</code></pre></div></div>

<p>Because the DOM tree can in theory nest infinitely, we have to use some recursion.
It’s always a bit of weird to wrap your head around it, but thankfully this is one not the worst kind to understand.</p>

<p>We’re also using a <code class="language-plaintext highlighter-rouge">StringBuffer</code>.
You can think of it as an efficient way for combining a lot of strings into one string.</p>

<p>In the case of a small amount of strings, it’s not needed.
But if we don’t know how many strings we need to combine and there might be a lot of them, <code class="language-plaintext highlighter-rouge">StringBuffer</code> is a good choice.</p>

<h2 id="converting-html-to-markdown">Converting HTML to Markdown</h2>

<p>There are a lot of Markdown to HTML converters, but what about going the other way?</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'dart:convert'</span><span class="semicolon">;</span></span>

<span class="dir"><span class="b">import</span> <span class="ls">'package:html&#47;dom.dart'</span><span class="semicolon">;</span></span>
<span class="dir"><span class="b">import</span> <span class="ls">'package:html&#47;parser.dart'</span> <span class="b">as</span> <span class="ip">html_parser</span><span class="semicolon">;</span></span>

<span class="k">final</span> <span class="tlvd">_htmlEscape</span> = <span class="ctr">HtmlEscape</span>(<span class="cl">HtmlEscapeMode</span>.<span class="sgr">element</span>)<span class="semicolon">;</span>

<span class="k">class</span> <span class="cl">HtmlToMarkdown</span> {
  <span class="cl">String</span> <span class="imd">convert</span>(<span class="cl">String</span> <span class="pd">html</span>) {
    <span class="k">final</span> <span class="lvd">buffer</span> = <span class="ctr">StringBuffer</span>()<span class="semicolon">;</span>
    <span class="k">final</span> <span class="lvd">document</span> = <span class="ip">html_parser</span>.<span class="tlfr">parseFragment</span>(<span class="pr">html</span>)<span class="semicolon">;</span>
    <span class="imr">_visitNodes</span>(<span class="lvr">buffer</span><span class="comma">,</span> <span class="lvr">document</span>.<span class="igr">nodes</span>)<span class="semicolon">;</span>

    <span class="k">return</span> <span class="lvr">buffer</span>.<span class="imr">toString</span>().<span class="imr">trim</span>()<span class="semicolon">;</span>
  }

  <span class="k">void</span> <span class="imd">_visitNodes</span>(<span class="cl">StringSink</span> <span class="pd">sink</span><span class="comma">,</span> <span class="cl">List</span>&lt;<span class="cl">Node</span>&gt; <span class="pd">nodes</span>) {
    <span class="k">for</span> (<span class="k">final</span> <span class="lvd">node</span> <span class="k">in</span> <span class="pr">nodes</span>) {
      <span class="k">if</span> (<span class="lvr">node</span> <span class="k">is</span> <span class="cl">Element</span>) {
        <span class="imr">_visitElement</span>(<span class="pr">sink</span><span class="comma">,</span> <span class="lvr">node</span>)<span class="semicolon">;</span>
      } <span class="k">else</span> <span class="k">if</span> (<span class="lvr">node</span> <span class="k">is</span> <span class="cl">Text</span>) {
        <span class="pr">sink</span>.<span class="imr">write</span>(<span class="tlgr">_htmlEscape</span>.<span class="imr">convert</span>(<span class="lvr">node</span>.<span class="igr">text</span>.<span class="imr">trim</span>()))<span class="semicolon">;</span>
      }
    }
  }

  <span class="k">void</span> <span class="imd">_visitElement</span>(<span class="cl">StringSink</span> <span class="pd">sink</span><span class="comma">,</span> <span class="cl">Element</span> <span class="pd">element</span>) {
    <span class="k">final</span> <span class="lvd">tag</span> = <span class="pr">element</span>.<span class="igr">localName</span>!.<span class="imr">toLowerCase</span>()<span class="semicolon">;</span>

    <span class="k">switch</span> (<span class="lvr">tag</span>) {
      <span class="k">case</span> <span class="ls">'p'</span>:
        <span class="pr">sink</span>.<span class="imr">writeln</span>()<span class="semicolon">;</span>
        <span class="pr">sink</span>.<span class="imr">writeln</span>()<span class="semicolon">;</span>
        <span class="imr">_visitNodes</span>(<span class="pr">sink</span><span class="comma">,</span> <span class="pr">element</span>.<span class="igr">nodes</span>)<span class="semicolon">;</span>
        <span class="k">break</span><span class="semicolon">;</span>
      <span class="k">case</span> <span class="ls">'strong'</span>:
        <span class="pr">sink</span>.<span class="imr">write</span>(<span class="ls">' **'</span>)<span class="semicolon">;</span>
        <span class="imr">_visitNodes</span>(<span class="pr">sink</span><span class="comma">,</span> <span class="pr">element</span>.<span class="igr">nodes</span>)<span class="semicolon">;</span>
        <span class="pr">sink</span>.<span class="imr">write</span>(<span class="ls">'** '</span>)<span class="semicolon">;</span>
        <span class="k">break</span><span class="semicolon">;</span>
      <span class="k">case</span> <span class="ls">'em'</span>:
        <span class="pr">sink</span>.<span class="imr">write</span>(<span class="ls">' _'</span>)<span class="semicolon">;</span>
        <span class="imr">_visitNodes</span>(<span class="pr">sink</span><span class="comma">,</span> <span class="pr">element</span>.<span class="igr">nodes</span>)<span class="semicolon">;</span>
        <span class="pr">sink</span>.<span class="imr">write</span>(<span class="ls">'_ '</span>)<span class="semicolon">;</span>
        <span class="k">break</span><span class="semicolon">;</span>
      <span class="k">default</span>:
        <span class="imr">_visitNodes</span>(<span class="pr">sink</span><span class="comma">,</span> <span class="pr">element</span>.<span class="igr">nodes</span>)<span class="semicolon">;</span>
        <span class="k">break</span><span class="semicolon">;</span>
    }
  }
}
</code></pre></div></div>

<p>It’s far from complete, but it’s good enough for a limited sample.</p>

<p>Let’s try it. This:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="k">final</span> <span class="lvd">html</span> = <span class="ls">'''
   &lt;p&gt;Hello &lt;strong&gt;there&lt;&#47;strong&gt; world!&lt;&#47;p&gt;
   &lt;p&gt;Have a &lt;em&gt;lovely&lt;&#47;em&gt; day!&lt;&#47;p&gt;
  '''</span><span class="semicolon">;</span>

  <span class="tlfr">print</span>(<span class="ctr">HtmlToMarkdown</span>().<span class="imr">convert</span>(<span class="lvr">html</span>))<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>prints the following:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hello <span class="gs">**there**</span> world!

It's quite a _lovely_ day!
</code></pre></div></div>

<p>Not to bad considering it’s just 50 lines of code.</p>

<p>There is an infinite amount of possible uses for parsing HTML in Dart.
Somebody even built <a href="https://pub.dev/packages/flutter_html">a package that converts HTML to Flutter widgets</a>.</p>

<p>What will you build?</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[Parsing HTML in Dart, the right, easy, and foolproof way. No messy regular expressions required.]]></summary></entry><entry><title type="html">Controlling time in Dart unit tests, the better way</title><link href="https://iiro.dev/controlling-time-with-package-clock/" rel="alternate" type="text/html" title="Controlling time in Dart unit tests, the better way" /><published>2020-11-14T00:00:00+00:00</published><updated>2020-11-14T00:00:00+00:00</updated><id>https://iiro.dev/controlling-time-with-package-clock</id><content type="html" xml:base="https://iiro.dev/controlling-time-with-package-clock/"><![CDATA[<p>In Dart, we can get the current date and time by calling <code class="language-plaintext highlighter-rouge">DateTime.now()</code>.</p>

<p>That’s easy enough.
But what’s not that easy is testing it.
Since there’s no built-in way to override what it returns, any Dart code that directly depends on <code class="language-plaintext highlighter-rouge">DateTime.now()</code> is not testable.</p>

<p>However, there’s a very neat way to tackle this, and it’s not what you might think.</p>

<aside style="top: 80px ">
  <p>If you’d want that to be a part of the Dart SDK, there’s an open <a href="https://github.com/dart-lang/sdk/issues/28985">GitHub issue</a> you might want to give a thumbs up on.</p>
</aside>

<h2 id="why-testing-datetimenow-is-hard">Why testing DateTime.now() is hard</h2>

<p>Let’s imagine that we’re making an app that tells the current day of the week.</p>

<p>We have a class called <code class="language-plaintext highlighter-rouge">Greeter</code> that returns the appropriate greeting for each day.</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>greeter.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:intl/intl.dart'</span><span class="semicolon">;</span></span>

<span class="k">final</span> <span class="tlvd">_format</span> = <span class="ctr">DateFormat</span>(<span class="ls">'EEEE'</span>)<span class="semicolon">;</span>

<span class="k">class</span> <span class="cl">Greeter</span> {
  <span class="cl">String</span> <span class="imd">greet</span>() {
    <span class="k">final</span> <span class="lvd">now</span> = <span class="ctr">DateTime</span>.<span class="ctr">now</span>()<span class="semicolon">;</span>
    <span class="k">final</span> <span class="lvd">dayOfWeek</span> = <span class="tlgr">_format</span>.<span class="imr">format</span>(<span class="lvr">now</span>)<span class="semicolon">;</span>
    <span class="k">return</span> <span class="ls">'Happy <span class="lvr">$<span class="lvr">dayOfWeek</span></span><span class="comma">,</span> you!'</span><span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>For example, if it’s Tuesday, <code class="language-plaintext highlighter-rouge">Greeter().greet()</code> returns <em>“Happy Tuesday, you!”</em>.</p>

<aside style="top: 835px ">
  <p>I actually have a colleague who wishes <em>“Happy &lt;current day&gt;!”</em> every time he comes to the office.</p>

  <p>Yes, even on Mondays. To him, every day seems to be a happy day.</p>
</aside>

<h3 id="writing-some-failing-tests">Writing some failing tests</h3>

<p>We want to be sure nobody introduces bugs to the very important greeting functionality, so we write a test case:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>greeter_test.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">test</span>(<span class="ls">'returns a proper greeting on a Tuesday'</span><span class="comma">,</span> () {
    <span class="k">final</span> <span class="lvd">greeting</span> = <span class="ctr">Greeter</span>().<span class="imr">greet</span>()<span class="semicolon">;</span>

    <span class="ceol">&#47;&#47; How on Earth do we make this expectation pass</span>
    <span class="ceol">&#47;&#47; if it&#39;s not Tuesday when we run this test?</span>
    <span class="tlfr">expect</span>(
      <span class="lvr">greeting</span><span class="comma">,</span>
      <span class="ls">'Happy Tuesday, you!'</span><span class="comma">,</span>
    )<span class="semicolon">;</span>
  })<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>The problem with this test is that it passes only once a week.</p>

<p>Unless our team agrees to only run tests on Tuesdays, that test case is not very useful. 
To make it pass every day of the week, we need to be able to control the current time somehow.</p>

<h3 id="making-it-testable---the-usual-way">Making it testable - the usual way</h3>

<p>We could refactor <code class="language-plaintext highlighter-rouge">Greeter</code> to something like this:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>greeter.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="cl">DateTime</span> <span class="tlfd">_getSystemTime</span>() =&gt; <span class="ctr">DateTime</span>.<span class="ctr">now</span>()<span class="semicolon">;</span>

<span class="k">class</span> <span class="cl">Greeter</span> {
  <span class="cl">Greeter</span>([<span class="k">this</span>.<span class="ifr">_getCurrentTime</span> = <span class="tlfr">_getSystemTime</span>])<span class="semicolon">;</span>
  <span class="k">final</span> <span class="cl">DateTime</span> <span class="k">Function</span>() <span class="ifd">_getCurrentTime</span><span class="semicolon">;</span>

  <span class="cl">String</span> <span class="imd">greet</span>() {
    <span class="k">final</span> <span class="lvd">now</span> = <span class="igr">_getCurrentTime</span>()<span class="semicolon">;</span>
    <span class="k">final</span> <span class="lvd">dayOfWeek</span> = <span class="tlgr">_format</span>.<span class="imr">format</span>(<span class="lvr">now</span>)<span class="semicolon">;</span>
    <span class="k">return</span> <span class="ls">'Happy <span class="lvr">$<span class="lvr">dayOfWeek</span></span><span class="comma">,</span> you!'</span><span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>The class now has an optional constructor parameter: a function called <code class="language-plaintext highlighter-rouge">_getCurrentTime()</code>.</p>

<p>By default, it returns the current time, but the behavior can be overridden.
In our tests, we pass <code class="language-plaintext highlighter-rouge">Greeter</code> a function that returns a fixed <code class="language-plaintext highlighter-rouge">DateTime</code> instead.</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>greeter_test.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">test</span>(<span class="ls">'returns a proper greeting on a Tuesday'</span><span class="comma">,</span> () {
    <span class="k">final</span> <span class="lvd">greeting</span> = <span class="ctr">Greeter</span>(() =&gt; <span class="ctr">DateTime</span>(<span class="li">2020</span><span class="comma">,</span> <span class="li">09</span><span class="comma">,</span> <span class="li">01</span>)).<span class="imr">greet</span>()<span class="semicolon">;</span>
    <span class="tlfr">expect</span>(<span class="lvr">greeting</span><span class="comma">,</span> <span class="ls">'Happy Tuesday, you!'</span>)<span class="semicolon">;</span>
  })<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>With these changes, our tests now pass 7 days a week!</p>

<h2 id="the-end">The end?</h2>

<p>That approach works for the very simple use case we had, but it’s not a fun thing to do at scale.</p>

<p>We would need to do this dance with every single class or widget that needs the current datetime.
This inevitably results in “prop drilling” when dealing with widgets and other nested classes.
That’s not fun.</p>

<aside style="top: 2665px ">
  <p><em>Prop drilling</em> is a term that comes from the ReactJS world, but also applies to Flutter.</p>

  <p>It means having to pass the same arguments again and again down to child widgets in highly nested widget trees.</p>
</aside>

<blockquote>
  <p>Well, what about InheritedWidget - or better yet, provider?</p>
</blockquote>

<p>We could come up with a class called <code class="language-plaintext highlighter-rouge">Clock</code> that encapsulates the behavior:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="cl">DateTime</span> <span class="tlfd">_defaultTime</span>() =&gt; <span class="ctr">DateTime</span>.<span class="ctr">now</span>()<span class="semicolon">;</span>

<span class="k">class</span> <span class="cl">Clock</span> {
  <span class="cl">Clock</span>([<span class="k">this</span>.<span class="ifr">now</span> = <span class="tlfr">_defaultTime</span>])<span class="semicolon">;</span>
  <span class="k">final</span> <span class="cl">DateTime</span> <span class="k">Function</span>() <span class="ifd">now</span><span class="semicolon">;</span>
}
</code></pre></div></div>

<p>We could then pass <code class="language-plaintext highlighter-rouge">Clock</code> down with an <code class="language-plaintext highlighter-rouge">InheritedWidget</code> or <a href="https://pub.dev/packages/provider">provider</a> to make it available for a subtree.</p>

<p>This would avoid the problem of prop drilling and still keep the logic testable.
However, we wouldn’t be able to access current time without a <code class="language-plaintext highlighter-rouge">BuildContext</code>.
It would also complicate accessing the <code class="language-plaintext highlighter-rouge">Clock</code> in <code class="language-plaintext highlighter-rouge">initState()</code>, since <code class="language-plaintext highlighter-rouge">context</code> is not available there yet.</p>

<p>Also, if we have a lot of Dart classes depending on the current datetime that are not Flutter widgets, we’d still have to pass the <code class="language-plaintext highlighter-rouge">Clock</code> as a constructor argument over and over again.</p>

<p>There’s got to be a better way!</p>

<h3 id="what-about-just-making-clock-a-singleton">What about just making Clock a singleton?</h3>

<p>That would work.</p>

<p>I did it years back myself with inKino.
You can see <a href="https://github.com/roughike/inKino/blob/64bc20dfb5bfb31e3fd3b623e6b07500dd6e6c0f/core/lib/src/utils/clock.dart">the source code for Clock here</a>, and <a href="https://github.com/roughike/inKino/blob/64bc20dfb5bfb31e3fd3b623e6b07500dd6e6c0f/core/test/redux/show_middleware_test.dart#L164-L189">here’s an example test case</a>, although it’s a bit specific to Redux.</p>

<p>However, I can’t help but feel a bit off with this one.</p>

<p>It’s global behavior that can be overridden from anywhere.
This could result in frustrating, hard to notice bugs.</p>

<p><a id="using-clock"></a></p>
<h2 id="the-better-way---controlling-time-with-packageclock">The better way - controlling time with package:clock</h2>

<p>Even though there’s no built-in way to mock <code class="language-plaintext highlighter-rouge">DateTime.now()</code>, there’s a good alternative that comes pretty close.</p>

<p>It’s <a href="https://pub.dev/packages/clock">a package called clock</a>, and it’s maintained by the Dart team.</p>

<p>The package exposes a top-level variable called <code class="language-plaintext highlighter-rouge">clock</code>.
To get the current time, we call <code class="language-plaintext highlighter-rouge">clock.now()</code>:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:clock/clock.dart'</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="ceol">&#47;&#47; prints current date and time</span>
  <span class="tlfr">print</span>(<span class="tlgr">clock</span>.<span class="imr">now</span>())<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>In order to override the current time, we wrap any code that calls <code class="language-plaintext highlighter-rouge">clock.now()</code> with a function called <code class="language-plaintext highlighter-rouge">withClock</code>:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:clock/clock.dart'</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">withClock</span>(
    <span class="ctr">Clock</span>.<span class="ctr">fixed</span>(<span class="ctr">DateTime</span>(<span class="li">2000</span>))<span class="comma">,</span>
    () {
      <span class="ceol">&#47;&#47; always prints 2000-01-01 00:00:00.</span>
      <span class="tlfr">print</span>(<span class="tlgr">clock</span>.<span class="imr">now</span>())<span class="semicolon">;</span>
    }<span class="comma">,</span>
  )<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">withClock</code> function takes in two arguments:</p>
<ol>
  <li>a <code class="language-plaintext highlighter-rouge">Clock</code> - in our case, a clock with a fixed date set to 2000/01/01.</li>
  <li>a function, inside of which the <code class="language-plaintext highlighter-rouge">clock</code> getter always returns what we passed in as the first argument.</li>
</ol>

<p>That’s pretty neat.</p>

<p>It also only requires minimal changes to existing code.
And there’s no need to add yet another constructor parameter and pass stuff around.</p>

<h3 id="how-does-it-work">How does it work?</h3>

<p>Short answer: it leverages <a href="https://dart.dev/articles/archive/zones#storing-zone-local-values">Zone-local values</a>.</p>

<p>Here’s how the <code class="language-plaintext highlighter-rouge">withClock</code> function looks like:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">Clock</span> {
  <span class="redacted"><span class="hide">// </span>...</span>
}

<span class="ceol">&#47;&#47; An unique object to use as a key for the clock.</span>
<span class="k">final</span> <span class="tlvd">_clockKey</span> = <span class="ctr">Object</span>()<span class="semicolon">;</span>

<span class="tp">T</span> <span class="tlfd">withClock</span>&lt;<span class="tp">T</span>&gt;(<span class="cl">Clock</span> <span class="pd">clock</span><span class="comma">,</span> <span class="tp">T</span> <span class="k">Function</span>() <span class="pd">callback</span>) {
  <span class="k">return</span> <span class="tlfr">runZoned</span>(
    <span class="pr">callback</span><span class="comma">,</span>
    <span class="pr">zoneValues</span>: <span class="lm">{
      <span class="ceol">&#47;&#47; Override _clockKey with the provided clock.</span>
      <span class="tlgr">_clockKey</span>: <span class="pr">clock</span><span class="comma">,</span>
    }</span><span class="comma">,</span>
  )<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>It runs the passed anonymous function in a new <code class="language-plaintext highlighter-rouge">Zone</code>, setting a Zone-local value for the <code class="language-plaintext highlighter-rouge">_clockKey</code> to contain the provided clock.</p>

<p>Any code inside <code class="language-plaintext highlighter-rouge">callback</code> that queries <code class="language-plaintext highlighter-rouge">_clockKey</code> from the current Zone will receive an overridden <code class="language-plaintext highlighter-rouge">Clock</code>.</p>

<p>You might see where this is going.</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="ceol">&#47;&#47; Look up if the current Zone has a clock assigned, and</span>
<span class="ceol">&#47;&#47; return that. If not, return a fresh new instance of Clock.</span>
<span class="cl">Clock</span> <span class="b">get</span> <span class="tlgd">clock</span> =&gt; <span class="cl">Zone</span>.<span class="sgr">current</span>[<span class="tlgr">_clockKey</span>] <span class="b">as</span> <span class="cl">Clock</span>? ?? <span class="ctr">Clock</span>()<span class="semicolon">;</span>
</code></pre></div></div>

<p>The top-level <code class="language-plaintext highlighter-rouge">clock</code> getter checks for the same <code class="language-plaintext highlighter-rouge">_clockKey</code> in the current Zone.</p>

<p>If a <code class="language-plaintext highlighter-rouge">Clock</code> already exists in the current Zone (=we have wrapped that piece of code with the <code class="language-plaintext highlighter-rouge">withClock</code> function), it returns the provided <code class="language-plaintext highlighter-rouge">Clock</code>.</p>

<p>Otherwise, it returns a fresh instance of <code class="language-plaintext highlighter-rouge">Clock</code> that defaults to returning <code class="language-plaintext highlighter-rouge">DateTime.now()</code>.</p>

<aside style="top: 5185px ">
  <p>Why is the key a new instance of an <code class="language-plaintext highlighter-rouge">Object()</code> instead of a unique string?</p>

  <p>Well, since <code class="language-plaintext highlighter-rouge">Object() != Object()</code>, creating an <code class="language-plaintext highlighter-rouge">Object()</code> and using that as the key is the simplest way to create a unique key for the clock.</p>
</aside>

<h2 id="fixing-our-tests">Fixing our tests</h2>

<p>Now that we have this brand new trick up our sleeves, we’ll finally make our sample test case work properly.</p>

<p>Let’s implement this in our <code class="language-plaintext highlighter-rouge">Greeter</code> class. To make it work, we  only need to make one tiny modification to greeter.dart:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>greeter.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:clock/clock.dart'</span><span class="semicolon">;</span></span>

<span class="k">class</span> <span class="cl">Greeter</span> {
  <span class="cl">String</span> <span class="imd">greet</span>() {
    <span class="redacted"><span class="hide">// </span>...</span>
    <span class="k">final</span> <span class="lvd">now</span> = <span class="tlgr">clock</span>.<span class="imr">now</span>()<span class="semicolon">;</span>
    <span class="redacted"><span class="hide">// </span>...</span>
  }
}
</code></pre></div></div>

<p>After that, we wrap our tests with the <code class="language-plaintext highlighter-rouge">withClock()</code> function:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>greeter_test.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">test</span>(<span class="ls">'returns a proper greeting on a Tuesday'</span><span class="comma">,</span> () {
    <span class="k">final</span> <span class="lvd">greeting</span> = <span class="tlfr">withClock</span>(
      <span class="ctr">Clock</span>.<span class="ctr">fixed</span>(<span class="ctr">DateTime</span>(<span class="li">2020</span><span class="comma">,</span> <span class="li">09</span><span class="comma">,</span> <span class="li">01</span>))<span class="comma">,</span>
      () =&gt; <span class="ctr">Greeter</span>().<span class="imr">greet</span>()<span class="comma">,</span>
    )<span class="semicolon">;</span>

    <span class="tlfr">expect</span>(<span class="lvr">greeting</span><span class="comma">,</span> <span class="ls">'Happy Tuesday, you!'</span>)<span class="semicolon">;</span>
  })<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>Now the test passes on every day of the week - not just Tuesday!</p>

<h2 id="other-use-cases">Other use cases</h2>

<p>Aside from overriding current time in just unit tests, depending on your app, you might benefit from this in your manual testing too.</p>

<p>For example, in Reflectly, we have some things that should be only loaded once a day.
In order to test this manually, I just wrap <code class="language-plaintext highlighter-rouge">runApp</code> with <code class="language-plaintext highlighter-rouge">withClock</code> that starts with the current date.</p>

<p>Then I bump the day by one to simulate tomorrow, the day after, and so on.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[How to control DateTime.now() in Dart unit tests - the better way.]]></summary></entry><entry><title type="html">When in doubt, just use Provider</title><link href="https://iiro.dev/just-use-provider/" rel="alternate" type="text/html" title="When in doubt, just use Provider" /><published>2020-07-12T00:00:00+00:00</published><updated>2020-07-12T00:00:00+00:00</updated><id>https://iiro.dev/just-use-provider</id><content type="html" xml:base="https://iiro.dev/just-use-provider/"><![CDATA[<p>There still seems to be a lot of confusion about what’s <em>“the best”</em> state management library to use with Flutter.</p>

<p>Disappointingly, the right answer to these types of questions is always somewhere along the lines of <em>“there’s no single best library - it all depends”</em>.</p>

<blockquote>
  <p>“It depends?!
Well, gee, thanks a lot for nothing!
I guess I’ll try all the state management libraries in existence and figure out the best one myself.
See you again around the time when my teeth fall out and I have dentures.”</p>
</blockquote>

<p>Well, what does “the best state management library” mean to you?</p>

<p>If you’re looking for something that:</p>

<ol>
  <li>scales when your app scales, and</li>
  <li>doesn’t make you want to create voodoo dolls out of the person who made the library and stab them in their ears with chopsticks</li>
</ol>

<p>then fair enough.</p>

<p><strong>You should know that most of the available state management libraries fit that criteria</strong>.</p>

<p>But you wouldn’t be on Reddit complaining that <em>“there’s too many Flutter state management libraries!1!! I want fewer options!1!1”</em> if this was your criteria.</p>

<p>In reality, you’re looking for <em>“something that the Flutter team and the community as a whole 100% agree is the best library now and forever and that everybody will be forced to use until the heat death of the universe”</em>.</p>

<p>Spoiler alert: there is no such thing.
And, likely, there will never be such a thing.</p>

<h2 id="just-pick-a-popular-library-and-stick-with-it">Just pick a popular library and stick with it</h2>

<p><em>“But why does it have to be popular?”</em>, you ask.</p>

<p><em>“For two reasons”</em>, I answer:</p>

<ol>
  <li>The more popular it is, the more learning resources and examples it will have.</li>
  <li>It’s more likely that your colleagues are already somewhat familiar with it. Which means you can focus on building on whatever it is that you’re building.</li>
</ol>

<p>And when I say “popular”, I don’t mean <em>“the hot new thing that just appeared yesterday that everybody is talking about”</em>.</p>

<p>You also want something that has been around for a while.</p>

<blockquote>
  <p>“So, what are some examples of popular libraries for state management?”</p>
</blockquote>

<p>In no particular order, some of the most established options would be <a href="https://pub.dev/packages/scoped_model">scoped_model</a>, <a href="https://pub.dev/packages/redux">redux</a>, <a href="https://pub.dev/packages/mobx">mobx</a>, <a href="https://pub.dev/packages/bloc">bloc</a>, and <a href="https://pub.dev/packages/provider">provider</a>.</p>

<p>If you ask me, anything out of these usual suspects is a bit on the hit-or-miss territory.</p>

<blockquote>
  <p>“Okay, but that’s still five libraries, and I still don’t know which one to choose.”</p>
</blockquote>

<p>That’s why we have the <span class="pedantic" data-pedantic-content="WIDJUCNWCNPCAS">WIDJUP</span> rule.</p>

<p>When In Doubt, Just Use <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span>.</p>

<h2 id="provider-is-all-you-need-for-state-management"><span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> is all you need for state management</h2>

<p>If you’re overwhelmed by all the state management libraries and don’t know which one to choose, Just Use <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span>™.</p>

<p><span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> has everything for your state management needs, both today and tomorrow.</p>

<p>Don’t get stuck in analysis paralysis; call <em>1-800-PROVIDER</em> and get your very own <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> today!</p>

<blockquote>
  <p>“But Provider is not state management! It’s just a better InheritedWidget.”</p>
</blockquote>

<p><em>You know what I mean</em>.</p>

<p>Just like you know what your friend means when he tells you that he’s eating 8000 calories every day to prepare for a sumo wrestling competition.
Yes, he means kilocalories, not calories.
Everybody knows that.</p>

<p>But fine.</p>

<div style="display: flex; align-items: center; margin-bottom: 30px">
<label class="switch">
  <input type="checkbox" onclick="togglePedanticMode()" />
  <span class="slider round"></span>
</label><span style="padding-left: 10px; font-weight: bold; padding-top: 6px; max-width: 80%">pedantic mode <span class="pedantic" data-pedantic-content="ON">off</span></span>
</div>

<p>Turn on the switch.
I hope that it makes you happy.</p>

<blockquote>
  <p>“Very funny.”</p>
</blockquote>

<p>Thanks.</p>

<blockquote>
  <p>“Whatever. So, <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> is all I need? Isn’t that quite a bold statement?”</p>
</blockquote>

<p>Not at all. Here’s a bold statement for you:</p>

<p><strong>A bold statement</strong>.</p>

<blockquote>
  <p>“Sigh. What I meant is that can I use <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> in serious applications?”</p>
</blockquote>

<p>Yes, just like you can use <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> in <em>silly applications</em>.</p>

<p>Such as in a fart soundboard app.</p>

<blockquote>
  <p>“Can you just stop with the dad jokes and write your articles like a normal person?”</p>
</blockquote>

<p><em>*turns on professional mode*</em></p>

<h2 id="provider-is-mature-enough"><span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> is mature enough</h2>

<h3 id="its-popular-but-not-new-and-shiny">It’s popular, but not new and shiny</h3>

<p><span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> is the most popular state management library on Pub.</p>

<p>It’s also probably the most liked library on Pub at 1.5k likes and counting.
I say “probably”, because Pub doesn’t allow sorting by likes.</p>

<p>Aside from being popular, <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> has also existed for more than a year at this point.
Having gone through several major version changes, the API should’ve stabilized enough by now.</p>

<p>Being popular, but not brand-spanking-new at the same time, is a good thing.</p>

<p>For what it’s worth, <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> is also recommended by Google and it has been recommended <a href="https://www.youtube.com/watch?v=d_m5csmrf7I">for over a year now</a>.</p>

<h3 id="its-like-scoped_model">It’s like scoped_model</h3>

<p>Do you remember how back in the good old days, there were pretty much two viable state management libraries?</p>

<p>No?
Well, Pepperidge Farm and a small group of early adopters remember.</p>

<p>Those two options were <a href="https://pub.dev/packages/scoped_model">scoped_model</a> and <a href="https://pub.dev/packages/flutter_redux">flutter_redux</a>.
Both packages have existed since August 2017.</p>

<blockquote>
  <p>“What does this have to do with <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span>?”</p>
</blockquote>

<p>I’ll show you.</p>

<p>Here’s one way to use <a href="https://pub.dev/packages/scoped_model">scoped_model</a>:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">Counter</span> <span class="k">extends</span> <span class="cl">Model</span> {
  <span class="cl">int</span> <span class="ifd">_counter</span> = <span class="li">0</span><span class="semicolon">;</span>
  <span class="cl">int</span> <span class="b">get</span> <span class="igd">counter</span> =&gt; <span class="igr">_counter</span><span class="semicolon">;</span>
  <span class="b">set</span> <span class="isd">counter</span>(<span class="cl">int</span> <span class="pd">value</span>) {
    <span class="isr">_counter</span> = <span class="pr">value</span><span class="semicolon">;</span>
    <span class="imr">notifyListeners</span>()<span class="semicolon">;</span>
  }
}

<span class="k">class</span> <span class="cl">MyWidget</span> <span class="k">extends</span> <span class="cl">StatelessWidget</span> {
  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="ceol">&#47;&#47; This assumes that there&#39;s ScopedModel&lt;Counter&gt;</span>
    <span class="ceol">&#47;&#47; widget somewhere in the widget tree and</span>
    <span class="ceol">&#47;&#47; it wraps &quot;MyWidget&quot;.</span>
    <span class="k">return</span> <span class="ctr">ScopedModelDescendant</span>&lt;<span class="cl">Counter</span>&gt;(
      <span class="pr">builder</span>: (<span class="pd">_</span><span class="comma">,</span> <span class="pd">__</span><span class="comma">,</span> <span class="pd">model</span>) =&gt; <span class="ctr">Text</span>(<span class="ls">'<span class="lvr">${<span class="pr">model</span>.<span class="igr">counter</span>}</span>'</span>)<span class="comma">,</span>
    )<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>Here’s the same example, but with <a href="https://pub.dev/packages/provider"><span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span></a>:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">Counter</span> <span class="k">extends</span> <span class="cl">ChangeNotifier</span> {
  <span class="ceol">&#47;&#47; The rest of it is the same.</span>
}

<span class="k">class</span> <span class="cl">MyWidget</span> <span class="k">extends</span> <span class="cl">StatelessWidget</span> {
  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="ceol">&#47;&#47; Assumes a ChangeNotifierProvider&lt;Counter&gt; above</span>
    <span class="ceol">&#47;&#47; in the widget tree.</span>
    <span class="k">return</span> <span class="ctr">Consumer</span>&lt;<span class="cl">Counter</span>&gt;(
      <span class="ceol">&#47;&#47; The rest of it is the same.</span>
    )<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>See the difference?</p>

<ol>
  <li>with <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span>, we extend <code class="language-plaintext highlighter-rouge">ChangeNotifier</code> instead of <code class="language-plaintext highlighter-rouge">Model</code></li>
  <li>we pass the model around with <code class="language-plaintext highlighter-rouge">ChangeNotifierProvider</code> instead of <code class="language-plaintext highlighter-rouge">ScopedModel</code></li>
  <li>we access the model with <code class="language-plaintext highlighter-rouge">Consumer</code> instead of <code class="language-plaintext highlighter-rouge">ScopedModelDescendant</code></li>
</ol>

<p>It’s the same thing, but with different names. 
<a href="https://youtu.be/SRvCvsRp5ho?t=30">There’s a guy from the ’80s with long hair who says it better than I ever could</a>.</p>

<blockquote>
  <p>Okay, so scoped_model has existed for three years. And scoped_model and <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> are very similar. So what?</p>
</blockquote>

<p>Well, it proves that <strong>the approach from scoped_model has stood the test of time</strong>.
Three years and counting.
It works.</p>

<p>It’s maybe even more than three years if you account for the fact that Fuchsia has been using the same approach internally before scoped_model extracted it to a package.</p>

<h2 id="provider-is-simple"><span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> is simple</h2>

<p>While provider has more advanced classes such as <code class="language-plaintext highlighter-rouge">ProxyProviders</code>, you don’t have to use them.</p>

<p>All you need to get started is to know the following:</p>
<ol>
  <li>use <code class="language-plaintext highlighter-rouge">ChangeNotifiers</code> to hold your state</li>
  <li>provide a <code class="language-plaintext highlighter-rouge">ChangeNotifier</code> to a subtree with <code class="language-plaintext highlighter-rouge">ChangeNotifierProvider</code></li>
  <li>obtain a <code class="language-plaintext highlighter-rouge">ChangeNotifier</code> from ancestor widgets and rebuild automatically with <code class="language-plaintext highlighter-rouge">Consumer</code>.</li>
</ol>

<p>There’s a good tutorial on <a href="https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple">the Flutter website</a> on how to get started.</p>

<p>Once you become more comfortable with the concepts, you might want to take a look at <a href="https://pub.dev/documentation/provider/latest/provider/Selector-class.html">Selector</a>, which helps filter the data that triggers rebuilds to your widgets.
But that’s really it.</p>

<h2 id="provider-is-scalable"><span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> is scalable</h2>

<p>Like many other state management libraries, <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> scales when your app scales.</p>

<p>But just like with scoped_model, it’s possible to shoot yourself in the foot by doing something weird.</p>

<h3 id="local-state--global-state">Local state &gt; global state</h3>

<p>When I see people complaining about scoped_model or provider not being scalable, this is almost always what they are doing:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">runApp</span>(
    <span class="ctr">MultiProvider</span>(
      <span class="pr">providers</span>: <span class="ll">[
        <span class="ctr">ChangeNotifierProvider</span>&lt;<span class="cl">MyNotifier1</span>&gt;(<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>)<span class="comma">,</span>
        <span class="ctr">ChangeNotifierProvider</span>&lt;<span class="cl">MyNotifier2</span>&gt;(<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>)<span class="comma">,</span>
        <span class="ctr">ChangeNotifierProvider</span>&lt;<span class="cl">MyNotifier3</span>&gt;(<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>)<span class="comma">,</span>

        <span class="ceol">&#47;&#47; * a few ChangeNotifierProviders later *</span>
        <span class="ctr">ChangeNotifierProvider</span>&lt;<span class="cl">MyNotifier99</span>&gt;(<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>)<span class="comma">,</span>
      ]</span><span class="comma">,</span>
      <span class="pr">child</span>: <span class="ctr">MaterialApp</span>(<span class="redacted"><span class="hide">/* </span>...<span class="hide"> */</span></span>)<span class="comma">,</span>
    )<span class="comma">,</span>
  )<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>In this case, instead of wrapping individual routes with their equivalent <code class="language-plaintext highlighter-rouge">ChangeNotifiers</code>, someone decided to make everything sit above the whole app.</p>

<p><strong>This lifts local state into heights where it doesn’t have to go to</strong>.</p>

<p>There’s suddenly no boundaries on who accesses what.
This, in turn, makes it impossible to dispose of unneeded <code class="language-plaintext highlighter-rouge">ChangeNotifiers</code> when users navigate away from their relevant routes.</p>

<p>Sharing a <code class="language-plaintext highlighter-rouge">ChangeNotifier</code> between many distinct routes also tends to result in very bloated <code class="language-plaintext highlighter-rouge">ChangeNotifiers</code>.
This is not fun when your app starts having a lot of routes.</p>

<p>If you keep your state local, there’s no real reason why <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span> wouldn’t scale with your app.</p>

<h2 id="youre-probably-already-depending-on-provider">You’re probably already depending on provider</h2>

<p>Given the popularity of provider, chances are that you already have it in your pubspec file.
If not, you might be depending on a library that uses it internally.</p>

<p>And if you’re already depending on it, why not just go ahead and use the same thing to manage state too?</p>

<h2 id="but-what-about-clean-architecture">But what about clean architecture?</h2>

<p>It’s a good idea to separate business logic from the UI framework.</p>

<p>But it’s also a good idea to be pragmatic about it.</p>

<p><strong>Most apps don’t have to go all-in with clean architecture</strong> just because that’s how you get the most internet points from <a href="https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition">your enterprise Java friends</a>.</p>

<p>If you’re a single developer hoping to build a successful app, you don’t want to get stuck wondering how to plumb Interactors together with Presenters, Services, Repositories, and then write the mappers that convert them in and out of domain models.</p>

<p>What you want to do instead is to build the MVP and ship it.</p>

<p>But if it’s a hobby app, and your sole purpose is to learn how clean architecture works, then it’s a very different story.
In that case, the answer is <em>“manage state with something that doesn’t have Flutter dependencies”</em>.</p>

<h2 id="but-what-about-insert-new-shiny-state-management-library-here">But what about &lt;insert new shiny state management library here&gt;?</h2>

<p>Do you remember the advice at the very beginning?</p>

<p><em>Pick a popular library and stick with it</em>.</p>

<p>Think about this:</p>

<p><strong>You could’ve been using <a href="https://pub.dev/packages/scoped_model">scoped_model</a> since 2017, ignoring all the other state management libraries in between (even <span class="pedantic" data-pedantic-content="ChangeNotifiers with ChangeNotifierProviders, Consumers, and Selectors">Provider</span>), and you would be fine today. Yeah, really.</strong></p>

<p>A new state management library coming out today doesn’t suddenly make your existing code rotten.</p>

<h3 id="stop-chasing-shiny-things">Stop chasing shiny things</h3>

<p>As an example, look at Invoice Ninja.</p>

<p>Out of <a href="https://pub.dev/packages/scoped_model">scoped_model</a> and <a href="https://pub.dev/packages/flutter_redux">flutter_redux</a>, Invoice Ninja chose flutter_redux.
And they stuck with it.</p>

<p>They released <a href="https://github.com/invoiceninja/flutter-client/tree/8261691d7391b802c44ef815330b00c3ab31a1f8">their first version</a> back in the summer of 2018.
Even back then, a large portion of the Flutter developer community didn’t really like Redux.
But that didn’t matter.</p>

<p>Redux worked fine for Invoice Ninja, and that was the only thing that mattered.
They still use Redux to this day.</p>

<p>Sure, Redux is boilerplatey, and it has its flaws, but it doesn’t matter.
The virtue of using the newest and hippest community-approved libraries doesn’t necessarily make your business succeed.</p>

<h3 id="just-build-it-already">Just build it already</h3>

<p>You know what makes your business succeed?</p>

<p>Shipping useful apps does.</p>

<p>Invoice Ninja didn’t keep chasing the coolest state management library of the week.
They didn’t complain about there being <em>“too many state management libraries”</em>.
And they still don’t.</p>

<p>Invoice Ninja <strong>picked a state management library, stuck to it</strong>, and focused on their business instead of worrying if people think that Redux is cool enough.</p>

<p>Want to hear a secret?</p>

<p><strong>You can do it too</strong>.</p>

<p>But instead of picking a library and starting to build apps with it, you sit on your hands and complain on Reddit that there are too many state management libraries.</p>

<script type="text/javascript">
var isPedanticMode = false;

function togglePedanticMode() {
    isPedanticMode = !isPedanticMode;
    updateTitle();
    togglePedanticSpans();
}

function updateTitle() {
    var providerPart = isPedanticMode? 'ChangeNotifiers used with ChangeNotifierProviders & Consumers' : 'Provider';
    var title = 'When in doubt, just use ' + providerPart;
    document.getElementsByClassName('post-title')[0].innerHTML = title;
    document.title= title;
}

function togglePedanticSpans() {
  var spans = document.getElementsByClassName('pedantic');
  
  for (var i = 0; i < spans.length; i++) {
    var span = spans[i];
    
    if (!span.hasAttribute('data-normal-content')) {
        span.setAttribute('data-normal-content', span.innerHTML);
    }
    
    span.innerHTML = isPedanticMode? 
        span.getAttribute('data-pedantic-content') :
        span.getAttribute('data-normal-content');
  }
}
</script>

<style type="text/css">
.pedantic-on {display: none;}
.switch {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;
}

.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #ccc;
  -webkit-transition: .4s;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  -webkit-transition: .4s;
  transition: .4s;
}

input:checked + .slider {
  background-color: #2196F3;
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
  -webkit-transform: translateX(26px);
  -ms-transform: translateX(26px);
  transform: translateX(26px);
}

.slider.round {
  border-radius: 34px;
}

.slider.round:before {
  border-radius: 50%;
}
</style>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[Some pointers about what Flutter state management library to choose in the age of gazillion state management libraries.]]></summary></entry><entry><title type="html">Restricting system textScaleFactor, when you have to</title><link href="https://iiro.dev/restricting-system-text-scale-factor/" rel="alternate" type="text/html" title="Restricting system textScaleFactor, when you have to" /><published>2020-05-18T00:00:00+00:00</published><updated>2020-05-18T00:00:00+00:00</updated><id>https://iiro.dev/restricting-system-text-scale-factor</id><content type="html" xml:base="https://iiro.dev/restricting-system-text-scale-factor/"><![CDATA[<p><small><strong><u>note:</u></strong> I received feedback pointing out that <strong>if the user chooses a huge font, they need it</strong>, and you should let them.
I agree.
However, <strong>sometimes it’s not up to you to decide</strong>.
Or maybe something else is a bigger priority right now.
In those situations, you need to find a compromise.
Anything is better than locking <code class="language-plaintext highlighter-rouge">textScaleFactor</code> to a hardcoded value. 
This article is written that scenario in mind.</small></p>

<p>Up until very recently, we were setting the <code class="language-plaintext highlighter-rouge">textScaleFactor</code> manually to <code class="language-plaintext highlighter-rouge">1.0</code> on every <code class="language-plaintext highlighter-rouge">Text</code> widget in Reflectly like this:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">override</span></span>
<span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
  <span class="k">return</span> <span class="ctr">Column</span>(
    <span class="pr">children</span>: <span class="ll">[
      <span class="ctr">Text</span>(
        <span class="ls">'something very important'</span><span class="comma">,</span>
        <span class="pr">textScaleFactor</span>: <span class="ld">1.0</span><span class="comma">,</span>
      )<span class="comma">,</span>
      <span class="ctr">Text</span>(
        <span class="ls">'something equally important'</span><span class="comma">,</span>
        <span class="pr">textScaleFactor</span>: <span class="ld">1.0</span><span class="comma">,</span>
      )<span class="comma">,</span>
    ]</span><span class="comma">,</span>
  )<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>What initially to me seemed like a very weird thing to do (<em>it defaults to <code class="language-plaintext highlighter-rouge">1.0</code> - why set it explicitly?</em>), proved to have some reasoning behind it.</p>

<p>It was there to ignore the system-wide text size settings.
The <code class="language-plaintext highlighter-rouge">textScaleFactor</code>, if unset, defaults to the value from the nearest <code class="language-plaintext highlighter-rouge">MediaQuery</code>.</p>

<p>The value for the <code class="language-plaintext highlighter-rouge">MediaQuery</code>, if not overridden, comes from device settings.</p>

<figure>
  <a href="/images/clamping-text-scale-factor/ios-text-settings.png ">
    <img alt="
Screenshot of iOS accessibility settings with text size set to maximum.
" src="/images/clamping-text-scale-factor/ios-text-settings.png " />
  </a>
  <figcaption>
    <p>Screenshot of iOS accessibility settings with text size set to maximum.</p>
  </figcaption>
</figure>

<p>The <code class="language-plaintext highlighter-rouge">textScaleFactor</code> that comes from the device settings can be anywhere from <code class="language-plaintext highlighter-rouge">1.0</code> to some very large values.</p>

<p>If we go to the accessibility settings of our iDevice and choose the largest possible text size, Reflectly becomes quite unflattering:</p>

<figure>
  <a href="/images/clamping-text-scale-factor/huge-text-size.png ">
    <img alt="
On the left, the main timeline page of Reflectly with normal text size.
 On the right, the same page with text size set to maximum settings.
" src="/images/clamping-text-scale-factor/huge-text-size.png " />
  </a>
  <figcaption>
    <p>On the left, the main timeline page of Reflectly with normal text size.
 On the right, the same page with text size set to maximum settings.</p>
  </figcaption>
</figure>

<p>Not very pretty, is it?</p>

<p>The hardcoded <code class="language-plaintext highlighter-rouge">textScaleFactors</code> for every single <code class="language-plaintext highlighter-rouge">Text</code> widget made at least some sense now.</p>

<h2 id="providing-a-default-textscalefactor-for-a-subtree">Providing a default textScaleFactor for a subtree</h2>

<p>Setting <code class="language-plaintext highlighter-rouge">textScaleFactor</code> manually for every single <code class="language-plaintext highlighter-rouge">Text</code> widget is not a very scalable solution.</p>

<p>If we forget just one <code class="language-plaintext highlighter-rouge">Text</code> widget, it will stand out and make our UI look inconsistent.</p>

<p>The good news is that we can override <code class="language-plaintext highlighter-rouge">textScaleFactor</code> for an entire subtree:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">override</span></span>
<span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
  <span class="k">return</span> <span class="ctr">WidgetsApp</span>(
    <span class="redacted"><span class="hide">// </span>...</span>
    <span class="pr">builder</span>: (<span class="pd">context</span><span class="comma">,</span> <span class="pd">child</span>) {
      <span class="ceol">&#47;&#47; Obtain the current media query information.</span>
      <span class="k">final</span> <span class="lvd">mediaQueryData</span> = <span class="cl">MediaQuery</span>.<span class="smr">of</span>(<span class="pr">context</span>)<span class="semicolon">;</span>

      <span class="k">return</span> <span class="ctr">MediaQuery</span>(
        <span class="ceol">&#47;&#47; Set the default textScaleFactor to 1.0 for</span>
        <span class="ceol">&#47;&#47; the whole subtree.</span>
        <span class="dpr">data</span>: <span class="lvr">mediaQueryData</span>.<span class="imr">copyWith</span>(<span class="pr">textScaleFactor</span>: <span class="ld">1.0</span>)<span class="comma">,</span>
        <span class="id">child</span>: <span class="pr">child</span>!<span class="comma">,</span>
      )<span class="semicolon">;</span>
    }<span class="comma">,</span>
  )<span class="semicolon">;</span>
}
</code></pre></div></div>

<p><small><strong><u>note:</u></strong> Although this entire article talks about <code class="language-plaintext highlighter-rouge">WidgetsApp</code>, it works exactly same with <code class="language-plaintext highlighter-rouge">MaterialApp</code> and <code class="language-plaintext highlighter-rouge">CupertinoApp</code>.</small></p>

<p>Now we don’t need to provide <code class="language-plaintext highlighter-rouge">textScaleFactor</code> to our <code class="language-plaintext highlighter-rouge">Text</code> widgets anymore.</p>

<p>It means that this:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">override</span></span>
<span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
  <span class="k">return</span> <span class="ctr">Text</span>(
    <span class="ls">'Hello world!'</span><span class="comma">,</span>
    <span class="pr">textScaleFactor</span>: <span class="ld">1.0</span><span class="comma">,</span>
  )<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>is exactly same as this:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">override</span></span>
<span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
  <span class="k">return</span> <span class="ctr">Text</span>(<span class="ls">'Hello world!'</span>)<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>No matter what the device text size is, our <code class="language-plaintext highlighter-rouge">Text</code> widgets will always ignore it and act as if the factor was always <code class="language-plaintext highlighter-rouge">1.0</code>.</p>

<p>Yay for ignoring a user preference without having to <em>repeat repeat ourselves ourselves</em>!</p>

<h3 id="how-it-works">How it works</h3>

<p>Let’s refer to a piece in <a href="https://api.flutter.dev/flutter/widgets/Text/textScaleFactor.html">textScaleFactor documentation</a> for the <code class="language-plaintext highlighter-rouge">Text</code> widget:</p>

<blockquote>
  <p>If null, will use the MediaQueryData.textScaleFactor <strong>obtained from the ambient MediaQuery</strong>.</p>
</blockquote>

<p>We’re making the nearest <code class="language-plaintext highlighter-rouge">MediaQuery</code> be the one where we set a default text scale factor.</p>

<figure>
  <a href="/images/clamping-text-scale-factor/media-query-before-after.png ">
    <img alt="
A diagram that shows the rough difference between before and after, with some oomph to make the change more prominent.
" src="/images/clamping-text-scale-factor/media-query-before-after.png " />
  </a>
  <figcaption>
    <p>A diagram that shows the rough difference between before and after, with some oomph to make the change more prominent.</p>
  </figcaption>
</figure>

<p>Whenever a widget calls <code class="language-plaintext highlighter-rouge">MediaQuery.of(context)</code>, it will get our <code class="language-plaintext highlighter-rouge">MediaQuery</code> with the overridden <code class="language-plaintext highlighter-rouge">textScaleFactor</code>.</p>

<p>And that is what the <code class="language-plaintext highlighter-rouge">Text</code> widget will use when resolving the default <code class="language-plaintext highlighter-rouge">textScaleFactor</code>.</p>

<h2 id="making-it-more-adaptive">Making it more adaptive</h2>

<p>Setting the <code class="language-plaintext highlighter-rouge">textScaleFactor</code> to <code class="language-plaintext highlighter-rouge">1.0</code> and calling it a day means ignoring your users needs.
They’re asking for a bigger text size and you’re not delivering it.</p>

<p>On the other side of the argument you might have your designer or client.
They don’t want the app to look ugly.</p>

<p>Or maybe some other feature/bug is more important right now, and you’ll fix this later.</p>

<p>Like with anything else in life, there’s a middle ground solution that pleases both:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="a">@<span class="tlgr">override</span></span>
<span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
  <span class="k">return</span> <span class="ctr">WidgetsApp</span>(
    <span class="redacted"><span class="hide">// </span>...</span>
    <span class="pr">builder</span>: (<span class="pd">context</span><span class="comma">,</span> <span class="pd">child</span>) {
      <span class="ceol">&#47;&#47; Obtain the current media query information.</span>
      <span class="k">final</span> <span class="lvd">mediaQueryData</span> = <span class="cl">MediaQuery</span>.<span class="smr">of</span>(<span class="pr">context</span>)<span class="semicolon">;</span>

      <span class="ceol">&#47;&#47; Take the textScaleFactor from system and make</span>
      <span class="ceol">&#47;&#47; sure that it&#39;s no less than 1.0, but no more</span>
      <span class="ceol">&#47;&#47; than 1.5.</span>
      <span class="k">final</span> <span class="cl">num</span> <span class="lvd">constrainedTextScaleFactor</span> =
          <span class="lvr">mediaQueryData</span>.<span class="igr">textScaleFactor</span>.<span class="imr">clamp</span>(<span class="ld">1.0</span><span class="comma">,</span> <span class="ld">1.5</span>)<span class="semicolon">;</span>

      <span class="k">return</span> <span class="ctr">MediaQuery</span>(
        <span class="dpr">data</span>: <span class="lvr">mediaQueryData</span>.<span class="imr">copyWith</span>(
          <span class="pr">textScaleFactor</span>: <span class="lvr">constrainedTextScaleFactor</span> <span class="b">as</span> <span class="cl">double</span>?<span class="comma">,</span>
        )<span class="comma">,</span>
        <span class="id">child</span>: <span class="pr">child</span>!<span class="comma">,</span>
      )<span class="semicolon">;</span>
    }<span class="comma">,</span>
  )<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>Instead of a hardcoded <code class="language-plaintext highlighter-rouge">textScaleFactor</code>, this time we’re providing a constrained system value.</p>

<p>By using <code class="language-plaintext highlighter-rouge">clamp()</code>, we’re giving the system <code class="language-plaintext highlighter-rouge">textScaleFactor</code> a lower and upper bound.</p>

<p>This allows the users to control the text size without going completely overboard.</p>

<h2 id="writing-tests-for-it">Writing tests for it</h2>

<blockquote>
  <p>“There’s nothing to test! It’s just a couple lines of code!”</p>
</blockquote>

<p>I thought so too, until we released this thing and Crashlytics started reporting about crashes in production.</p>

<p>Without going into details, believe me when I say it was a stupid mistake.</p>

<p>I fixed the bug with accompanying test cases that make sure it doesn’t happen again.
Here’s roughly how it works.</p>

<h3 id="extracting-the-behavior-to-a-widget">Extracting the behavior to a widget</h3>

<p>We don’t care about other stuff in our <code class="language-plaintext highlighter-rouge">main.dart</code> - we only want to test the text scale factor logic.</p>

<p>The easiest way to do this is to extract the logic into a separate widget, and write tests for that widget.</p>

<p>We’ll call it <code class="language-plaintext highlighter-rouge">TextScaleFactorClamper</code>:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code>
<span class="k">class</span> <span class="cl">TextScaleFactorClamper</span> <span class="k">extends</span> <span class="cl">StatelessWidget</span> {
  <span class="k">const</span> <span class="cl">TextScaleFactorClamper</span>({<span class="tlgr">required</span> <span class="k">this</span>.<span class="ifr">child</span>})<span class="semicolon">;</span>
  <span class="k">final</span> <span class="cl">Widget</span> <span class="ifd">child</span><span class="semicolon">;</span>

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="k">final</span> <span class="lvd">mediaQueryData</span> = <span class="cl">MediaQuery</span>.<span class="smr">of</span>(<span class="pr">context</span>)<span class="semicolon">;</span>
    <span class="k">final</span> <span class="cl">num</span> <span class="lvd">constrainedTextScaleFactor</span> =
        <span class="lvr">mediaQueryData</span>.<span class="igr">textScaleFactor</span>.<span class="imr">clamp</span>(<span class="ld">1.0</span><span class="comma">,</span> <span class="ld">1.5</span>)<span class="semicolon">;</span>

    <span class="k">return</span> <span class="ctr">MediaQuery</span>(
      <span class="dpr">data</span>: <span class="lvr">mediaQueryData</span>.<span class="imr">copyWith</span>(
        <span class="pr">textScaleFactor</span>: <span class="lvr">constrainedTextScaleFactor</span> <span class="b">as</span> <span class="cl">double</span>?<span class="comma">,</span>
      )<span class="comma">,</span>
      <span class="id">child</span>: <span class="igr">child</span><span class="comma">,</span>
    )<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>Now the logic is nicely contained inside <code class="language-plaintext highlighter-rouge">TextScaleFactorClamper</code>.</p>

<p>It takes the system <code class="language-plaintext highlighter-rouge">textScaleFactor</code>, makes sure it’s between 1.0 - 1.5, and passes it down as the default value for the subtree.</p>

<h3 id="testing-it">Testing it</h3>

<p>Now that the logic has been extracted, testing it in isolation becomes quite easy:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:flutter_test&#47;flutter_test.dart'</span><span class="semicolon">;</span></span>

<span class="dir"><span class="b">import</span> <span class="ls">'text_scale_factor_clamper.dart'</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">group</span>(<span class="ls">'TextScaleFactorClamper'</span><span class="comma">,</span> () {
    <span class="cl">double</span>? <span class="lvd">effectiveTextScaleFactor</span><span class="semicolon">;</span>

    <span class="tlfr">setUp</span>(() {
      <span class="lvr">effectiveTextScaleFactor</span> = <span class="k">null</span><span class="semicolon">;</span>
    })<span class="semicolon">;</span>

    <span class="cl">Future</span>&lt;<span class="k">void</span>&gt; <span class="lfd">pumpWithTextScaleFactor</span>(<span class="cl">WidgetTester</span> <span class="pd">tester</span><span class="comma">,</span> <span class="cl">double</span> <span class="pd">factor</span>) {
      <span class="k">return</span> <span class="pr">tester</span>.<span class="imr">pumpWidget</span>(
        <span class="ctr">MediaQuery</span>(
          <span class="dpr">data</span>: <span class="ctr">MediaQueryData</span>(<span class="pr">textScaleFactor</span>: <span class="pr">factor</span>)<span class="comma">,</span>
          <span class="id">child</span>: <span class="ctr">TextScaleFactorClamper</span>(
            <span class="dpr">child</span>: <span class="ctr">Builder</span>(<span class="dpr">builder</span>: (<span class="dpd">context</span>) {
              <span class="ceol">&#47;&#47; Obtain the effective textScaleFactor in this context and assign</span>
              <span class="ceol">&#47;&#47; the value to a variable, so that we can check if it&#39;s what we</span>
              <span class="ceol">&#47;&#47; want.</span>
              <span class="lvr">effectiveTextScaleFactor</span> = <span class="cl">MediaQuery</span>.<span class="smr">of</span>(<span class="dpr">context</span>).<span class="igr">textScaleFactor</span><span class="semicolon">;</span>

              <span class="ceol">&#47;&#47; We don&#39;t care about what&#39;s rendered, so let&#39;s just return the</span>
              <span class="ceol">&#47;&#47; most minimal widget we can.</span>
              <span class="k">return</span> <span class="k">const</span> <span class="ctr">SizedBox</span>.<span class="ctr">shrink</span>()<span class="semicolon">;</span>
            })<span class="comma">,</span>
          )<span class="comma">,</span>
        )<span class="comma">,</span>
      )<span class="semicolon">;</span>
    }

    <span class="tlfr">testWidgets</span>(<span class="ls">'constrains the text scale factor to always be between 1.0-1.5'</span><span class="comma">,</span>
        (<span class="pd">tester</span>) <span class="b">async</span> {
      <span class="b">await</span> <span class="lfr">pumpWithTextScaleFactor</span>(<span class="pr">tester</span><span class="comma">,</span> <span class="li">5</span>)<span class="semicolon">;</span>
      <span class="tlfr">expect</span>(<span class="lvr">effectiveTextScaleFactor</span><span class="comma">,</span> <span class="ld">1.5</span>)<span class="semicolon">;</span>

      <span class="b">await</span> <span class="lfr">pumpWithTextScaleFactor</span>(<span class="pr">tester</span><span class="comma">,</span> <span class="ld">0.1</span>)<span class="semicolon">;</span>
      <span class="tlfr">expect</span>(<span class="lvr">effectiveTextScaleFactor</span><span class="comma">,</span> <span class="li">1</span>)<span class="semicolon">;</span>

      <span class="b">await</span> <span class="lfr">pumpWithTextScaleFactor</span>(<span class="pr">tester</span><span class="comma">,</span> -<span class="ld">5.0</span>)<span class="semicolon">;</span>
      <span class="tlfr">expect</span>(<span class="lvr">effectiveTextScaleFactor</span><span class="comma">,</span> <span class="li">1</span>)<span class="semicolon">;</span>

      <span class="b">await</span> <span class="lfr">pumpWithTextScaleFactor</span>(<span class="pr">tester</span><span class="comma">,</span> <span class="ld">1.25</span>)<span class="semicolon">;</span>
      <span class="tlfr">expect</span>(<span class="lvr">effectiveTextScaleFactor</span><span class="comma">,</span> <span class="ld">1.25</span>)<span class="semicolon">;</span>
    })<span class="semicolon">;</span>
  })<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>The meat and potatoes is in the <code class="language-plaintext highlighter-rouge">pumpWithTextScaleFactor()</code> method.</p>

<p>It uses the <a href="https://api.flutter.dev/flutter/widgets/Builder-class.html">Builder widget</a> to obtain the correct context that has our <code class="language-plaintext highlighter-rouge">textScaleFactor</code> applied.
The effective text scale factor is then assigned to a variable, aptly named <code class="language-plaintext highlighter-rouge">effectiveTextScaleFactor</code>.</p>

<p>After that, it’s easy to just pump widgets with varying text scale factors and verify the correctness with <code class="language-plaintext highlighter-rouge">expect()</code> statements.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Now we can be relatively certain our app displays reasonably sized text, and most importantly, doesn’t crash.</p>

<p>Better yet, we’re able to pass the sane default value to an entire subtree.
Surely beats manually setting <code class="language-plaintext highlighter-rouge">textScaleFactor</code> to <code class="language-plaintext highlighter-rouge">1.0</code> on every single <code class="language-plaintext highlighter-rouge">Text</code> widget!</p>

<p>I guess that’s the lesson for today - if it feels like there has to be a better way to do something, there almost always is.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[How to restrict the system textScaleFactor and keep both your users and designers happy. Also a drinking game: every time it says "textScaleFactor", take one shot.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://iiro.dev/images/clamping-text-scale-factor/twitter-card.png" /><media:content medium="image" url="https://iiro.dev/images/clamping-text-scale-factor/twitter-card.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Running arbitrary strings as Dart code</title><link href="https://iiro.dev/how-to-eval-in-dart/" rel="alternate" type="text/html" title="Running arbitrary strings as Dart code" /><published>2019-08-15T00:00:00+00:00</published><updated>2019-08-15T00:00:00+00:00</updated><id>https://iiro.dev/how-to-eval-in-dart</id><content type="html" xml:base="https://iiro.dev/how-to-eval-in-dart/"><![CDATA[<p><small>(<strong>NOTE:</strong> <a href="https://twitter.com/mraleph/status/1161907757383131136">This only works in JIT mode</a>.
It’s a viable solution only if you’re building a Dart app that runs with Dart VM and which isn’t a <a href="https://dart.dev/tools/dart2aot">AOT compiled</a>.
<strong>You can’t use this approach with Flutter</strong>.
Or you can, but it stops working in release mode.)</small></p>

<p>In Javascript, it’s possible to evaluate a string as code:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>why-would-you-do-that.js </div>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">code</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">console.log("hey!")</span><span class="dl">'</span><span class="p">;</span>

<span class="c1">// Execute "code" as Javascript</span>
<span class="nf">eval</span><span class="p">(</span><span class="nx">code</span><span class="p">);</span>
</code></pre></div></div>

<p>Javascript has <code class="language-plaintext highlighter-rouge">eval()</code>, which is a special function that takes in a string and <em>evaluates</em> it as Javascript code.
The above example prints a friendly <em>“hey!”</em> to the console.</p>

<blockquote>
  <p><em>“But Liro, why would you not just call <code class="language-plaintext highlighter-rouge">console.log()</code> directly?”</em></p>
</blockquote>

<p>First, my name is <em>Iiro</em>.
And to answer the question, you’re absolutely right.
You would in fact do just that.</p>

<p>Here’s the same thing but with less code:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>much-better.js </div>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hey!</span><span class="dl">"</span><span class="p">)</span>
</code></pre></div></div>

<p>If the result of both examples is the same, why would we ever want to <code class="language-plaintext highlighter-rouge">eval()</code> in the first place?</p>

<p>In this case, we wouldn’t.
That code snippet is <strong>just as an example</strong> - not an actual use case.
A real use case for using <code class="language-plaintext highlighter-rouge">eval()</code> is quite hard to come up with.</p>

<h2 id="so---when-do-i-need-to-eval">So - when do I need to <code class="language-plaintext highlighter-rouge">eval()</code>?</h2>

<p>Most of the time, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval!">you should not use eval()</a> at all.
Chances are that you’re never going to need it in application development.</p>

<p><strong>In very rare cases</strong>, you might actually need it.
Most likely when building developer tools - after all, there’s no <a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">REPL</a> without eval.</p>

<p>I found myself needing <code class="language-plaintext highlighter-rouge">eval()</code> today when building something in Dart.
And guess what, it was in the context of building a developer tool.</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">final</span> <span class="id">functionName</span> = <span class="ls">'applyMagicSauce'</span><span class="semicolon">;</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">functionName</code> here is hacked together during runtime.
It’s done by searching for implementers of a specific abstract class in a Dart project.</p>

<p>Those abstract classes are written by me, but the implementations come from third-party libraries.
There might be multiple <code class="language-plaintext highlighter-rouge">functionName</code>s to call.</p>

<p>I needed to take that <code class="language-plaintext highlighter-rouge">functionName</code> and do something like this:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">final</span> <span class="id">functionName</span> = <span class="ls">'applyMagicSauce'</span><span class="semicolon">;</span>
<span class="k">final</span> <span class="id">input</span> = <span class="ls">'some arbitrary string'</span><span class="semicolon">;</span>

<span class="ceol">&#47;&#47; Construct a call to &quot;applyMagicSauce()&quot;</span>
<span class="ceol">&#47;&#47; with &quot;some arbitrary string&quot; as input.</span>
<span class="k">final</span> <span class="id">result</span> = <span class="id">eval</span>(<span class="ls">'<span class="lvr">$<span class="id">functionName</span></span>("<span class="lvr">$<span class="id">input</span></span>")'</span>)<span class="semicolon">;</span>
</code></pre></div></div>

<p>Calling <code class="language-plaintext highlighter-rouge">applyMagicSauce(..)</code> and passing it a string returns a slightly different string.
I needed to find a way to call it.</p>

<h2 id="where-did-you-hide-your-eval-dart">Where did you hide your <code class="language-plaintext highlighter-rouge">eval()</code>, Dart?</h2>

<p>Dart <strong>does not</strong> have an <code class="language-plaintext highlighter-rouge">eval()</code> function.
What a bummer.</p>

<p>If you’re <em>running Dart in a web browser</em>, you could kinda do it by calling Javascript from Dart like this:</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code>
<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="k">final</span> <span class="dlvd">result</span> = <span class="ip">js</span>.<span class="tlgr">context</span>.<span class="imr">callMethod</span>(<span class="ls">"eval"</span><span class="comma">,</span> <span class="cb">/* ... */</span>)<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>But that <strong>won’t work when running Dart on the VM</strong>.</p>

<p>I’m also pretty sure I wouldn’t get anything useful back if I tried to send some Dart to a Javascript <code class="language-plaintext highlighter-rouge">eval()</code> function.</p>

<p>After some more investigative Googling, I stumpled upon <a href="https://github.com/dart-lang/sdk/issues/36989#issuecomment-493402079">this comment</a> on a GitHub issue:</p>

<blockquote>
  <p>You can create a new library with a main function, <strong>encode the source code as a data: 
URI and spawn it as a new isolate</strong>, but that will not allow you to create new functions 
in the current isolate. You’ll have to communicate with the new code using isolate port communication.</p>
</blockquote>

<p>I decided to try just that.
There was a function called <a href="https://api.flutter.dev/flutter/dart-isolate/Isolate/spawnUri.html">Isolate.spawnUri</a> that seemed to be just what I needed.</p>

<p>It takes in an <code class="language-plaintext highlighter-rouge">Uri</code> - which among other things, can be constructed from a string by calling <a href="https://api.flutter.dev/flutter/dart-core/Uri/Uri.dataFromString.html">Uri.dataFromString</a>.</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code>
<span class="k">void</span> <span class="tlfd">main</span>() <span class="b">async</span> {
  <span class="k">final</span> <span class="lvd">uri</span> = <span class="ctr">Uri</span>.<span class="ctr">dataFromString</span>(
    <span class="ls">'''
    void main() {
      print("Hellooooooo from the other side!");
    }
    '''</span><span class="comma">,</span>
    <span class="pr">mimeType</span>: <span class="ls">'application/dart'</span><span class="comma">,</span>
  )<span class="semicolon">;</span>
  <span class="b">await</span> <span class="cl">Isolate</span>.<span class="smr">spawnUri</span>(<span class="lvr">uri</span><span class="comma">,</span> <span class="ll">[]</span><span class="comma">,</span> <span class="k">null</span>)<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>Guess what?
The damn thing worked.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ironman$ dart lib/eval.dart

Hellooooooo from the other side!
</code></pre></div></div>

<p>Yay for executing arbitrary strings as Dart code!
What about sending something back?</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code>
<span class="k">void</span> <span class="tlfd">main</span>() <span class="b">async</span> {
  <span class="k">final</span> <span class="lvd">name</span> = <span class="ls">'Eval Knievel'</span><span class="semicolon">;</span>
  <span class="k">final</span> <span class="lvd">uri</span> = <span class="ctr">Uri</span>.<span class="ctr">dataFromString</span>(
    <span class="ls">'''
    import "dart:isolate"<span class="semicolon">;</span>

    void main(_<span class="comma">,</span> SendPort port) {
      port.send("Nice to meet you<span class="comma">,</span> <span class="lvr">$<span class="lvr">name</span></span>!")<span class="semicolon">;</span>
    }
    '''</span><span class="comma">,</span>
    <span class="pr">mimeType</span>: <span class="ls">'application&#47;dart'</span><span class="comma">,</span>
  )<span class="semicolon">;</span>

  <span class="k">final</span> <span class="lvd">port</span> = <span class="ctr">ReceivePort</span>()<span class="semicolon">;</span>
  <span class="b">await</span> <span class="cl">Isolate</span>.<span class="smr">spawnUri</span>(<span class="lvr">uri</span><span class="comma">,</span> <span class="ll">[]</span><span class="comma">,</span> <span class="lvr">port</span>.<span class="igr">sendPort</span>)<span class="semicolon">;</span>

  <span class="k">final</span> <span class="cl">String</span>? <span class="lvd">response</span> = <span class="b">await</span> (<span class="lvr">port</span>.<span class="igr">first</span> <span class="b">as</span> <span class="id">FutureOr</span>&lt;<span class="cl">String</span>?&gt;)<span class="semicolon">;</span>
  <span class="tlfr">print</span>(<span class="lvr">response</span>)<span class="semicolon">;</span>
}
</code></pre></div></div>

<p><small>(Isolates in Dart are quite… <em>isolated</em>. 
They don’t share memory. 
In order to pass data around, we need to use <em>ports</em> - which might seem a little cumbersome.
On the other hand, we don’t run into synchronization issues, so there’s that.)</small></p>

<p>That one worked too:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ironman$ dart lib/eval.dart

Nice to meet you, Eval Knievel!
</code></pre></div></div>

<p>Remember <code class="language-plaintext highlighter-rouge">applyMagicSauce()</code> from earlier?
Let’s imagine I have the full <code class="language-plaintext highlighter-rouge">package:</code> URI for the containing file in a variable called <code class="language-plaintext highlighter-rouge">packageUri</code>.
Because I actually do.</p>

<p>I also know that the name of the function I want to call is <code class="language-plaintext highlighter-rouge">applyMagicSauce</code>.
It takes in one argument which is a String.</p>

<p>It’s time to stitch some strings together.</p>

<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code>
<span class="k">void</span> <span class="tlfd">main</span>() <span class="b">async</span> {
  <span class="ceol">&#47;&#47; I filled out the content of these variables here</span>
  <span class="ceol">&#47;&#47; for clarity. In a real scenario, you&#39;d probably</span>
  <span class="ceol">&#47;&#47; parse these from somewhere.</span>
  <span class="k">final</span> <span class="lvd">packageUri</span> = <span class="ls">'package:magic&#47;magic_sauce.dart'</span><span class="semicolon">;</span>
  <span class="k">final</span> <span class="lvd">functionName</span> = <span class="ls">'applyMagicSauce'</span><span class="semicolon">;</span>
  <span class="k">final</span> <span class="lvd">input</span> = <span class="ls">'Hellooooooo from the other side!'</span><span class="semicolon">;</span>
  <span class="k">final</span> <span class="lvd">uri</span> = <span class="ctr">Uri</span>.<span class="ctr">dataFromString</span>(
    <span class="ls">'''
    import "dart:isolate"<span class="semicolon">;</span>
    import '<span class="lvr">$<span class="lvr">packageUri</span></span>'<span class="semicolon">;</span>

    void main(_<span class="comma">,</span> SendPort port) {
      port.send(<span class="lvr">$<span class="lvr">functionName</span></span>("<span class="lvr">$<span class="lvr">input</span></span>"))<span class="semicolon">;</span>
    }
    '''</span><span class="comma">,</span>
    <span class="pr">mimeType</span>: <span class="ls">'application&#47;dart'</span><span class="comma">,</span>
  )<span class="semicolon">;</span>

  <span class="k">final</span> <span class="lvd">port</span> = <span class="ctr">ReceivePort</span>()<span class="semicolon">;</span>
  <span class="k">final</span> <span class="lvd">isolate</span> =
  <span class="b">await</span> <span class="cl">Isolate</span>.<span class="smr">spawnUri</span>(<span class="lvr">uri</span><span class="comma">,</span> <span class="ll">[]</span><span class="comma">,</span> <span class="lvr">port</span>.<span class="igr">sendPort</span>)<span class="semicolon">;</span>
  <span class="k">final</span> <span class="cl">String</span>? <span class="lvd">response</span> = <span class="b">await</span> (<span class="lvr">port</span>.<span class="igr">first</span> <span class="b">as</span> <span class="id">FutureOr</span>&lt;<span class="cl">String</span>?&gt;)<span class="semicolon">;</span>

  <span class="lvr">port</span>.<span class="imr">close</span>()<span class="semicolon">;</span>
  <span class="lvr">isolate</span>.<span class="imr">kill</span>()<span class="semicolon">;</span>

  <span class="tlfr">print</span>(<span class="lvr">response</span>)<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>After running this, the result of <code class="language-plaintext highlighter-rouge">applyMagicSauce</code> will be in the <code class="language-plaintext highlighter-rouge">response</code> variable.</p>

<p>If the magic sauce function was shuffling a string around, we would get something like this printed on the console:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ironman$ dart lib/eval.dart

lor meetei ooo sdel fthooh!oHoor
</code></pre></div></div>

<h2 id="final-words">Final words</h2>

<p>In Javascript, the first rule of using <code class="language-plaintext highlighter-rouge">eval</code> is <strong>to not use it</strong>.
It applies for Dart too.</p>

<p>It’s usually hacky and there’s going to be a better way.
If you’re just building Flutter apps, you’re probably never going to need it.
In fact, if you’re building a Flutter app, this approach <a href="https://twitter.com/mraleph/status/1161907757383131136">will not work for you</a>.</p>

<p>The second rule is that <em>eval can be evil</em>.
If you do run it with Dart VM, you need to seriously think about making sure that you’re not accidentally running something malicious on your end user’s computer.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[Javascript has eval() for running a string as code. Although you most likely should never use it, sometimes you just might need to. Dart does not have eval(), but what do you do when you absolutely need it?]]></summary></entry><entry><title type="html">From mobile to web in less than 4 beers</title><link href="https://iiro.dev/reflectly-mobile-to-web-with-flutter/" rel="alternate" type="text/html" title="From mobile to web in less than 4 beers" /><published>2019-05-15T00:00:00+00:00</published><updated>2019-05-15T00:00:00+00:00</updated><id>https://iiro.dev/reflectly-mobile-to-web-with-flutter</id><content type="html" xml:base="https://iiro.dev/reflectly-mobile-to-web-with-flutter/"><![CDATA[<p>When I joined Reflectly eight months ago, I was tasked to recreate a subset of our Flutter app — but for <em>the web</em>.</p>

<p>Our initial goal was to implement basic account functionality with support for payments and discount codes. 
This would improve the experience for new users signing up via referral links. 
The long-term plan was to have a 100% feature parity with our existing mobile app.</p>

<figure>
  <a href="/images/reflectly-mobile-to-web-with-flutter/onboarding-angulardart.gif ">
    <img alt="
A screen capture of our onboarding flow for web, written in AngularDart — featuring wonky icon fonts.
" src="/images/reflectly-mobile-to-web-with-flutter/onboarding-angulardart.gif " />
  </a>
  <figcaption>
    <p>A screen capture of our onboarding flow for web, written in AngularDart — featuring wonky icon fonts.</p>
  </figcaption>
</figure>

<p>Thanks to our relatively clean architecture, I could reuse most of our existing code from our core package. 
This meant that most of the work in creating the web app went into building the HTML/CSS-based UI layer in <a href="https://angulardart.dev/">AngularDart</a>.</p>

<p>One of the bigger tasks was to rebuild our UI component library from scratch. 
This included elements such as buttons, text inputs, cards, shadows and a flexible system for common animations and interactions.</p>

<figure>
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/tactile-button.gif">
    <img src="/images/reflectly-mobile-to-web-with-flutter/tactile-button.gif" alt="Our tactile component in use with a button with inset shadows and touch feedback." />
  </a></p>
  <figcaption>Our <strong>&lt;tactile&gt;</strong> component in use with a <strong>&lt;button&gt;</strong> with inset shadows and touch feedback.</figcaption>
</figure>

<p>To put it briefly, I spent a lot of time rebuilding what we already had in our mobile app but for the web — two months to be exact.</p>

<p>I also had to cut corners in terms of the more complex animations and overall visual beauty. 
The deadline was approaching fast, and I already had my hands full trying to vertically center divs in CSS.</p>

<h2 id="you-cant-always-get-what-you-want">You can’t always get what you want…</h2>

<p>I wanted to take the full Reflectly experience to the web. 
Some of our users had been requesting it too.</p>

<p>While AngularDart is an excellent framework, spending two months on rebuilding what we already had seemed kind of a wasted effort. 
Especially if you’re a team of three engineers, out of which only two do frontend.</p>

<figure class="big">
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/angulardart-dashboard.png">
    <img src="/images/reflectly-mobile-to-web-with-flutter/angulardart-dashboard.png" alt="Our simplified AngularDart web dashboard." />
  </a></p>
  <figcaption>Our simplified <strong>AngularDart</strong> web dashboard.</figcaption>
</figure>

<p>We launch new features, bug fixes and experiment with new designs and features every week. 
It can be very time consuming to build the UI with Flutter first and then vertically center divs with CSS for the web later.</p>

<p>The reality was that we <strong>didn’t have the resources</strong> to continue bringing Reflectly for the web at the time. 
We decided to freeze our web development efforts until our situation improved.</p>

<h2 id="or-can-you">…or can you?</h2>

<p>Shortly after releasing our AngularDart web app, the first Flutter Live event was held at the Science Museum in London.</p>

<p>At Flutter Live, the Flutter team announced <a href="https://medium.com/flutter-io/hummingbird-building-flutter-for-the-web-e687c2a023a8">Hummingbird</a>, which was later rebranded to <a href="https://flutter.dev/web">Flutter for web</a>. 
A couple of months later, I got an invite to try out the early preview of Flutter for web.</p>

<figure class="big">
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/themes.gif">
    <img src="/images/reflectly-mobile-to-web-with-flutter/themes.gif" alt="Which one is web? Our onboarding flow, written in Flutter — running on iOS and Chrome." />
  </a></p>
  <figcaption>Which one is web? Our onboarding flow, <strong>written in Flutter</strong> — running on iOS and Chrome.</figcaption>
</figure>

<p>Being quite excited, I grabbed <a href="https://youtu.be/J5DQRPRBiFI?t=1836">four beers</a> from the nearby supermarket and went back to our office. 
Before finishing the last beer, I had our existing codebase (<a href="https://medium.com/reflectly-engineering/reflectly-from-react-native-to-flutter-2e3dffced2ea">written in Flutter</a>) running on a new platform — the web.</p>

<p>Being able to ship on Android and iOS with a single codebase was one thing, but <em>being able to run it on the web too</em> is quite something else.</p>

<figure class="big">
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/statistics.gif">
    <img src="/images/reflectly-mobile-to-web-with-flutter/statistics.gif" alt="Our complex statistics screen, also written in Flutter, running on as a native Android app and in Chrome." />
  </a></p>
  <figcaption>Our complex statistics screen, also <strong>written in Flutter</strong>, running on as a native Android app and in Chrome.</figcaption>
</figure>

<p><em>(Sidenote: during the EAP, the gzipped Javascript file for our app was just about 190kb. It might get smaller in the future, for example with the upcoming non-nullable types for Dart.)</em></p>

<p>We also did a quick test on how larger layouts would work with Flutter for web and what it would require to adapt to different screen sizes.</p>

<figure class="big">
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/stats-desktop.gif">
    <img src="/images/reflectly-mobile-to-web-with-flutter/stats-desktop.gif" alt="Our statistics page made adaptive, in less than three hours." />
  </a></p>
  <figcaption>Our statistics page made adaptive, in less than three hours.</figcaption>
</figure>

<p>Since our statistics page is composed by laying out several smaller custom widgets on the screen, we can just arrange them differently for desktop.</p>

<p>From initial design handoff to making our statistics page adaptive, it took only three hours of work. 
Since Flutter has support for <a href="https://iirokrankka.com/2018/01/28/implementing-adaptive-master-detail-layouts/">adapting to different screen sizes</a>, it’s easy to create container layouts that work for both mobile and web.</p>

<h2 id="the-best-kind-of-code-is-no-code">The best kind of code is no code</h2>

<p>Our AngularDart web app has almost 6000 lines of code.</p>

<p>That might seem small (and it is) but keep in mind that it’s just 5–10% of the features that we have in our Flutter app. 
That’s 5659 lines of code that I don’t want to maintain and I would be thrilled to throw away.</p>

<figure class="big">
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/angulardart-line-count.png">
    <img src="/images/reflectly-mobile-to-web-with-flutter/angulardart-line-count.png" alt="Lines of code in our AngularDart web app, counted with cloc." />
  </a></p>
  <figcaption>Lines of code in our <strong>AngularDart</strong> web app, counted with <a href="https://github.com/aldanial/cloc">cloc</a>.</figcaption>
</figure>

<p>My urge to delete all the web-specific code is not because of AngularDart. I actually quite like it. What I don’t like is doing the same work twice.</p>

<p>With our current setup, we have just about <em>40% code sharing</em>. The remaining 60%? That’s platform specific code in our Flutter and AngularDart apps.</p>

<blockquote>
  <p>“I just love maintaining two separate codebases that do the same thing!” — said no one ever.</p>
</blockquote>

<p>Less web-specific code means fewer web bugs; no web-specific code means no web bugs. At least in the ideal world.</p>

<h2 id="a-couple-cases-of-were-not-there-yet">A couple cases of “we’re not there yet”</h2>

<p>There are some minor speed bumps in the road preventing us from taking Reflectly to a web browser near you.</p>

<h3 id="no-grpc-web-support-for-dart-yet">No gRPC-web support for Dart (yet)</h3>

<p>We are very happy users of gRPC in our Flutter mobile app — every single API call is made with protobufs and gRPC goodness.</p>

<p>Although not Flutter for web specific, one of the most pressing blockers for us is the <a href="https://github.com/grpc/grpc-dart/issues/43">lack of gRPC-web support for Dart</a>.</p>

<figure>
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/no-grpc-for-you.png">
    <img src="/images/reflectly-mobile-to-web-with-flutter/no-grpc-for-you.png" alt="The issue ticket for gRPC-Web support has been open for some time in the gRPC-dart repository." />
  </a></p>
  <figcaption>The <a href="https://github.com/grpc/grpc-dart/issues/43">issue ticket</a> for gRPC-Web support has been open for some time in the gRPC-dart repository.</figcaption>
</figure>

<p>Although it’s possible to <a href="https://github.com/dart-lang/js_facade_gen">generate a Dart wrapper</a> over the regular gRPC-web library using the TypeScript definitions, that’s not a road we want to take in the long term.</p>

<p>Lucky enough for us, there’s some <a href="https://github.com/grpc/grpc-dart/tree/grpc-web">good progress</a> on the way.</p>

<h3 id="no-plugin-support-for-the-web-yet">No plugin support for the web (yet)</h3>

<p>We are currently using about ten plugins in our Flutter app, but none of them will run on a web browser just yet. 
This is because there is no web plugin support in Flutter for web as of today.</p>

<p>Once the support is there, we expect those web implementations to come up for most of the existing plugins.
While most of them will be easy to migrate, more complicated ones such as <a href="https://github.com/tekartik/sqflite">sqflite</a> will take a longer time.</p>

<h2 id="not-locking-ourselves-down">Not locking ourselves down</h2>

<p>While we are very excited about the current state of Flutter for web, we don’t want to <em>bet our business</em> in an early technology preview.</p>

<p>Flutter on mobile is <em>very forgiving</em> regarding performance.
We’re not yet sure if it will be the same story for Flutter for web.
According to <a href="https://github.com/flutter/flutter_web">the Flutter for web README</a>, “performance work is only just beginning”.</p>

<p>So far, we’ve seen that our statistics page has some perceivable lag on bigger screens when the graph animations are running.
Time will tell if there are some low-hanging performance fruits up for grabs.</p>

<figure>
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/code-sharing-setup.png">
    <img src="/images/reflectly-mobile-to-web-with-flutter/code-sharing-setup.png" alt="An overly simplified diagram of our current codesharing setup. If Flutter for web doesn’t work out for us, we can continue our AngularDart codesharing efforts." />
  </a></p>
  <figcaption>An overly simplified diagram of <strong>our current codesharing setup</strong>. If Flutter for web doesn’t work out for us, we can continue our AngularDart codesharing efforts.</figcaption>
</figure>

<p>We are also worried about how natural Flutter for web feels on each respective platform. 
Will it feel too much like Adobe Flash, and if it does, is that a problem for our users?</p>

<p>Thanks to our relatively clean architecture, we are not tied into Flutter for web. 
If for some reason, we decide that it’s not a good fit for us, we will continue our codesharing efforts with AngularDart and build our web UI separately with the native web stack.</p>

<h2 id="await-ing-on-a-very-exciting-future">“Await-ing” on a very exciting Future</h2>

<p>As a small team of three engineers, we couldn’t be more excited about the future of Flutter.</p>

<p>We <a href="https://medium.com/reflectly-engineering/reflectly-from-react-native-to-flutter-2e3dffced2ea">jumped on board quite early</a> with our Android and iOS apps — and we couldn’t be happier about it. More importantly, <em>our users</em> are extremely happy too. Our app is constantly getting solid five-star ratings and positive reviews.</p>

<figure>
  <p><a href="/images/reflectly-mobile-to-web-with-flutter/reflectly-top-charts.png">
    <img src="/images/reflectly-mobile-to-web-with-flutter/reflectly-top-charts.png" alt="Our cute little mascot hanging at the very top of Health &amp; Fitness in the US App Store." />
  </a></p>
  <figcaption>Our cute little mascot hanging at the very <strong>top of Health &amp; Fitness</strong> in the US App Store.</figcaption>
</figure>

<p>We’ve also recently been lucky enough to hang out with brands like Calm, Headspace and Fitbit in the top ranking apps of the Health &amp; Fitness category.</p>

<p>Using Flutter for mobile has been the <strong>best technical decision</strong> we’ve made. We can’t wait to say the same about Flutter for web.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[We at Reflectly were part of the Flutter for Web early access preview. Although it's very early, it looks super promising. This is a short story about our plans to expand to web and how we're super excited to learn HTML: How To Maintain Less code with Flutter for Web.]]></summary></entry><entry><title type="html">Splitting widgets to methods is an antipattern</title><link href="https://iiro.dev/splitting-widgets-to-methods-performance-antipattern/" rel="alternate" type="text/html" title="Splitting widgets to methods is an antipattern" /><published>2018-12-11T00:00:00+00:00</published><updated>2018-12-11T00:00:00+00:00</updated><id>https://iiro.dev/splitting-widgets-to-methods-performance-antipattern</id><content type="html" xml:base="https://iiro.dev/splitting-widgets-to-methods-performance-antipattern/"><![CDATA[<p>It has been almost 6 months since I wrote an article on <a href="/2018/06/18/putting-build-methods-on-a-diet/">cleaning up Flutter UI code</a>.</p>

<p>The article had several tips on how to organize your Flutter UI code for less clutter and more readability.
And it’s still a quite popular article.
However, there was this one tip there that advocated doing something that turned out to be a not that good thing to do.</p>

<p>To escape the Lisp-y <em>bracket hell</em>, I advocated for splitting long build methods into multiple separate smaller methods.
The following code sample is entirely nonsensical, but we’ll pretend that this is a snippet that does something really useful.</p>

<p>So, for example, if we have a widget that looks something like this:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>Example of a nested build method </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">_MyStatefulWidgetState</span> <span class="k">extends</span> <span class="cl">State</span>&lt;<span class="cl">MyStatefulWidget</span>&gt; {
  <span class="cl">int</span> <span class="ifd">_counter</span> = <span class="li">0</span><span class="semicolon">;</span>

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="k">return</span> <span class="ctr">Row</span>(
      <span class="pr">children</span>: <span class="ll">[
        <span class="ctr">Text</span>(<span class="ls">'Counter: <span class="lvr">$<span class="igr">_counter</span></span>'</span>)<span class="comma">,</span>
        <span class="ctr">Container</span>(
          <span class="pr">child</span>: <span class="ctr">Column</span>(
            <span class="pr">children</span>: <span class="ll">[
              <span class="ctr">Text</span>(<span class="ls">'Hello'</span>)<span class="comma">,</span>
              <span class="ctr">Row</span>(
                <span class="pr">children</span>: <span class="ll">[
                  <span class="ctr">Text</span>(<span class="ls">'there'</span>)<span class="comma">,</span>
                  <span class="ctr">Text</span>(<span class="ls">'world!'</span>)<span class="comma">,</span>
                ]</span><span class="comma">,</span>
              )<span class="comma">,</span>
            ]</span><span class="comma">,</span>
          )<span class="comma">,</span>
        )<span class="comma">,</span>
      ]</span><span class="comma">,</span>
    )<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>Looking at the code, you might get a sense that the nesting level gets a little crazy.</p>

<p>And they do.
It would be awesome to reduce the indentation level a little bit.
Since <a href="https://medium.com/@darky12s/flutter-reducing-widgets-boilerplate-3e635f10685e">widgets can be a little boilerplatey</a>, the first solution that comes into mind is to split the nesting part into a separate method.</p>

<p>Our first intuition might end us having something that looks like this:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>Example of a nested build method - split into a separate method </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">_MyStatefulWidgetState</span> <span class="k">extends</span> <span class="cl">State</span>&lt;<span class="cl">MyStatefulWidget</span>&gt; {
  <span class="cl">int</span> <span class="ifd">_counter</span> = <span class="li">0</span><span class="semicolon">;</span>

  <span class="cl">Widget</span> <span class="imd">_buildNonsenseWidget</span>() {
    <span class="k">return</span> <span class="ctr">Container</span>(
      <span class="pr">child</span>: <span class="ctr">Column</span>(
        <span class="pr">children</span>: <span class="ll">[
          <span class="ctr">Text</span>(<span class="ls">'Hello'</span>)<span class="comma">,</span>
          <span class="ctr">Row</span>(
            <span class="pr">children</span>: <span class="ll">[
              <span class="ctr">Text</span>(<span class="ls">'there'</span>)<span class="comma">,</span>
              <span class="ctr">Text</span>(<span class="ls">'world!'</span>)<span class="comma">,</span>
            ]</span><span class="comma">,</span>
          )<span class="comma">,</span>
        ]</span><span class="comma">,</span>
      )<span class="comma">,</span>
    )<span class="semicolon">;</span>
  }

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="k">return</span> <span class="ctr">Row</span>(
      <span class="pr">children</span>: <span class="ll">[
        <span class="ctr">Text</span>(<span class="ls">'Counter: <span class="lvr">$<span class="igr">_counter</span></span>'</span>)<span class="comma">,</span>

        <span class="ceol">&#47;&#47; The deeply nesting widget is now refactored into a</span>
        <span class="ceol">&#47;&#47; separate method and we have a cleaner build method. Yay!</span>
        <span class="imr">_buildNonsenseWidget</span>()<span class="comma">,</span>
      ]</span><span class="comma">,</span>
    )<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>Problem solved, right?
Time to call it a day and go home!</p>

<p>Well, not quite.</p>

<h3 id="the-problem-with-splitting-widgets-into-methods">The problem with splitting widgets into methods</h3>

<p>At first glance, splitting long build methods into small functions makes perfect sense.
And you can certainly see this pattern used in the wild a lot.
It’s also in heavy use in our codebase at <a href="https://reflectly.app">Reflectly</a> - and believe me, we have a bunch of UI code.</p>

<p>Now, 6 months later after my initial article, I’m here to tell you that you shouldn’t do this.
If you picked up this bad habit after reading my article, you might be a little pissed at me right now.
And you’re welcome.
This is what friends do for each other. <span style="font-size: 12px">I’m very sorry.</span></p>

<p>The issue with this was first brought to my attention by <a href="https://twitter.com/flutter_wm">Wm Leler</a> in the comment section of the aforementioned article.</p>

<figure>
  <p><a href="/images/splitting-widgets-to-functions-antipattern/wm.png">
    <img src="/images/splitting-widgets-to-functions-antipattern/wm.png" alt="Wm Leler's comment explaining how splitting widgets to functions is a performance antipattern." />
  </a></p>
  <figcaption>Wm Leler's comment explaining how splitting widgets to functions is a performance antipattern.</figcaption>
</figure>

<p>For those that don’t already know, Wm is a developer advocate for Flutter.</p>

<p>Some of you that read Wm’s comment will have an a-ha moment right now.
However, some of you, including me initially, won’t.
And that’s fine - we’ll learn what is going on here.</p>

<h3 id="so-whats-the-problem-really">So what’s the problem, really?</h3>

<p>Whenever the value of <code class="language-plaintext highlighter-rouge">_counter</code> changes, the framework calls the <code class="language-plaintext highlighter-rouge">build</code> method.
This triggers our widget to rebuild itself.
The problem is that <code class="language-plaintext highlighter-rouge">_buildNonsenseWidget()</code> gets called <strong>every time</strong> the value of <code class="language-plaintext highlighter-rouge">_counter</code> changes - which ends up rebuilding the widget tree over and over again.</p>

<h4 id="rebuilding-for-nothing">Rebuilding for nothing</h4>

<p>In this case, there’s <strong>no reason to rebuild</strong> that particular widget tree.</p>

<p>The widget tree returned by <code class="language-plaintext highlighter-rouge">_buildNonsenseWidget()</code> is stateless by nature - <strong>we only need to build it once</strong>.
Sadly, because the widget tree is built by the <code class="language-plaintext highlighter-rouge">_buildNonsenseWidget()</code> method, the Flutter framework rebuilds it every time when the parent widget rebuilds.</p>

<p>Essentially, we’re wasting precious CPU cycles in <em>rebuilding something that doesn’t need to be rebuilt</em>.
This happens because from the framework’s perspective, there’s no difference between <a href="https://www.youtube.com/watch?v=RAGcDi0DRtU">a long-ass</a> build method and a build method split into multiple smaller methods.
Mind you, this is only a simple example - this has a more significant impact on more complex apps.</p>

<h2 id="splitting-long-build-methods---revisited">Splitting long build methods - revisited</h2>

<p>The solution for this one is relatively simple, although it results in a couple of extra lines of code.
Instead of splitting build methods into smaller methods, we split them into widgets - StatelessWidgets, that is.</p>

<p>When we refactor the previous example, we’ll end up with this:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>Example of a nested build method - split into a widget </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="cl">_MyStatefulWidgetState</span> <span class="k">extends</span> <span class="cl">State</span>&lt;<span class="cl">MyStatefulWidget</span>&gt; {
  <span class="cl">int</span> <span class="ifd">_counter</span> = <span class="li">0</span><span class="semicolon">;</span>

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="k">return</span> <span class="ctr">Row</span>(
      <span class="pr">children</span>: <span class="ll">[
        <span class="ctr">Text</span>(<span class="ls">'Counter: <span class="lvr">$<span class="igr">_counter</span></span>'</span>)<span class="comma">,</span>

        <span class="ceol">&#47;&#47; The deeply nesting widget is now refactored into a</span>
        <span class="ceol">&#47;&#47; stateless const widget. No more needless rebuilding!</span>
        <span class="k">const</span> <span class="ctr">_NonsenseWidget</span>()<span class="comma">,</span>
      ]</span><span class="comma">,</span>
    )<span class="semicolon">;</span>
  }
}

<span class="k">class</span> <span class="cl">_NonsenseWidget</span> <span class="k">extends</span> <span class="cl">StatelessWidget</span> {
  <span class="k">const</span> <span class="cl">_NonsenseWidget</span>()<span class="semicolon">;</span>

  <span class="a">@<span class="tlgr">override</span></span>
  <span class="cl">Widget</span> <span class="imd">build</span>(<span class="cl">BuildContext</span> <span class="pd">context</span>) {
    <span class="k">return</span> <span class="ctr">Container</span>(
      <span class="pr">child</span>: <span class="ctr">Column</span>(
        <span class="pr">children</span>: <span class="ll">[
          <span class="ctr">Text</span>(<span class="ls">'Hello'</span>)<span class="comma">,</span>
          <span class="ctr">Row</span>(
            <span class="pr">children</span>: <span class="ll">[
              <span class="ctr">Text</span>(<span class="ls">'there'</span>)<span class="comma">,</span>
              <span class="ctr">Text</span>(<span class="ls">'world!'</span>)<span class="comma">,</span>
            ]</span><span class="comma">,</span>
          )<span class="comma">,</span>
        ]</span><span class="comma">,</span>
      )<span class="comma">,</span>
    )<span class="semicolon">;</span>
  }
}
</code></pre></div></div>

<p>While it’s a little more code, this is much better.</p>

<p>Now the <code class="language-plaintext highlighter-rouge">_NonsenseWidget</code> is built only once and all of the unnecessary rebuilds are gone.
The parent widget can rebuild itself multiple times, but the <code class="language-plaintext highlighter-rouge">_NonsenseWidget</code> doesn’t care - it’s built once and once only.</p>

<p><small>(This is only one part of the story and applies to <code class="language-plaintext highlighter-rouge">const</code> StatelessWidgets - see this answer for <a href="https://stackoverflow.com/a/53234826/940036">what is the difference between functions and classes to create widgets?</a> - a StackOverflow answer by Remi Rousselet.)</small></p>

<h4 id="splitting-widgets-into-smaller-widgets---more-complex-examples">Splitting widgets into smaller widgets - more complex examples</h4>

<p>You might be thinking that the above was a really simple example and it doesn’t represent the complexity of a real app.</p>

<p>And you’d be right.
I recently updated the <a href="https://github.com/roughike/inKino">open source inKino app</a> to follow the advice on this article.
For example, I think <a href="https://github.com/roughike/inKino/blob/development/mobile/lib/ui/event_details/event_backdrop_photo.dart">this is a good sample</a> of splitting widgets into smaller <code class="language-plaintext highlighter-rouge">StatelessWidget</code>s in a bigger app.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Instead of splitting you build methods into multiple smaller methods, split them into <code class="language-plaintext highlighter-rouge">StatelessWidget</code>s.
This way, you won’t be rebuilding your static widget trees multiple times for nothing but wasted CPU cycles.
When it comes to optimizing performance of Flutter apps, this is probably one of the lowest hanging fruits.</p>

<p>If you <strong>really prefer</strong> building your widget trees with methods, you might want to take a look at a package called <a href="https://medium.com/@darky12s/flutter-reducing-widgets-boilerplate-3e635f10685e">functional_widget by Remi Rousselet</a>.
It alleviates the problems that come with building widget trees with methods by using code generation.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[Almost 6 months ago, I advised you to split long build methods into multiple smaller methods. And it turns out I was wrong. You should be splitting your widgets into smaller widgets instead - and there's a good reason why.]]></summary></entry><entry><title type="html">Why does using Image.network crash widget tests?</title><link href="https://iiro.dev/image-network-widget-tests/" rel="alternate" type="text/html" title="Why does using Image.network crash widget tests?" /><published>2018-09-16T00:00:00+00:00</published><updated>2018-09-16T00:00:00+00:00</updated><id>https://iiro.dev/image-network-widget-tests</id><content type="html" xml:base="https://iiro.dev/image-network-widget-tests/"><![CDATA[<p>Running widget tests headlessly without any simulator or emulator is pretty dope. It’s quite mindblowing to run some automated taps, swipes, and flings and not need to stare that <code class="language-plaintext highlighter-rouge">Waiting for emulator to start...</code> message for minutes.</p>

<p>But then, with the tears of pure joy falling down your cheeks, you try to run a widget test that pumps an <code class="language-plaintext highlighter-rouge">Image.network</code> widget. And suddenly those tears are not tears of joy anymore.</p>

<p>A really dumbed-down version that illustrates the problem might look like this:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>test/my_widget_test.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:flutter_test/flutter_test.dart'</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">testWidgets</span>(<span class="ls">'my image test'</span><span class="comma">,</span> (<span class="cl">WidgetTester</span> <span class="pd">tester</span>) <span class="b">async</span> {
    <span class="b">await</span> <span class="pr">tester</span>.<span class="imr">pumpWidget</span>(
      <span class="ctr">MaterialApp</span>(
        <span class="pr">home</span>: <span class="ctr">Image</span>.<span class="ctr">network</span>(<span class="ls">'https://example.com/image.png'</span>)<span class="comma">,</span>
      )<span class="comma">,</span>
    )<span class="semicolon">;</span>

    <span class="ceol">&#47;&#47; Crash!</span>
  })<span class="semicolon">;</span>
}
</code></pre></div></div>

<p>If you try to run the above test, it will crash. Any url you provide, no matter if it’s fake or real, will cause the widget to load the image url and fail instantly.</p>

<p>You’ll be greeted with a long error message that looks like this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
The following _Exception was thrown resolving an image codec:
Exception: HTTP request failed, statusCode: 400, https://example.com/image.png
...
When the exception was thrown, this was the stack:
#0      NetworkImage._loadAsync (package:flutter/src/painting/image_provider.dart:490:7)
&lt;asynchronous suspension&gt;
#1      NetworkImage.load (package:flutter/src/painting/image_provider.dart:469:14)
#2      ImageProvider.resolve.&lt;anonymous closure&gt;.&lt;anonymous closure&gt; (package:flutter/src/painting/image_provider.dart:266:86)
...
Test failed. See exception logs above.
</code></pre></div></div>

<p>This might seem weird and annoying at first, but it turns out that this is a quite sane default behavior to have.</p>

<h2 id="why-does-this-happen">Why does this happen?</h2>

<p>By default, all HTTP requests in widget tests <a href="https://github.com/flutter/flutter/blob/e235ccd76ff88063c9ca358ed12818f4907a8017/packages/flutter_test/lib/src/binding.dart#L1458-L1467">will always return empty responses</a>, with an HTTP status <code class="language-plaintext highlighter-rouge">400 - Bad Request</code>. In other words - every HTTP request will raise an exception and fail your widget tests. And this is perfectly good default behavior in tests. But why?</p>

<p>Making real HTTP requests in tests is problematic. For one, it makes running tests slower. It also makes tests unpredictable and flaky. They could fail due to poor connectivity issues or server not responding, and those are something out of our control. We don’t want flaky tests depending on outside conditions.</p>

<p>It would be quite nice if there was some way to intercept those requests that the <code class="language-plaintext highlighter-rouge">Image.network</code> widget makes in widget tests. And there is.</p>

<h2 id="the-solution">The solution</h2>

<p>The solution is to replace the default HTTP client with one that always responds with a transparent image and an HTTP status of <code class="language-plaintext highlighter-rouge">200 - OK</code>.  There’s a sample on how to do this <a href="https://github.com/flutter/flutter/blob/master/dev/manual_tests/test/mock_image_http.dart">in the Flutter repo</a> and in the <a href="https://github.com/flutter/flutter_markdown/pull/17">widget tests of the flutter_markdown library</a>, but it might get a little boring to copy and paste this thing around.</p>

<p>That’s why I created <a href="https://github.com/roughike/image_test_utils">a little library</a> that wraps this into a nice little package. There are only a few steps to start using it.</p>

<p><strong>Step 1:</strong> include it in your <code class="language-plaintext highlighter-rouge">dev_dependencies</code> block:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>pubspec.yaml </div>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">dev_dependencies</span><span class="pi">:</span>
  <span class="na">image_test_utils</span><span class="pi">:</span> <span class="s">^1.0.0</span>
</code></pre></div></div>

<p><strong>Step 2:</strong> wrap your widget test with a <code class="language-plaintext highlighter-rouge">provideMockedNetworkImages</code> method:</p>

<div class="code-snippet-name"><i class="far fa-folder"></i>test/my_widget_test.dart </div>
<div class="language-dart highlighter-dart-hl"><div class="highlight"><pre class="highlight"><code><span class="dir"><span class="b">import</span> <span class="ls">'package:flutter_test/flutter_test.dart'</span><span class="semicolon">;</span></span>
<span class="dir"><span class="b">import</span> <span class="ls">'package:image_test_utils/image_test_utils.dart'</span><span class="semicolon">;</span></span>

<span class="k">void</span> <span class="tlfd">main</span>() {
  <span class="tlfr">testWidgets</span>(<span class="ls">'my image test'</span><span class="comma">,</span> (<span class="cl">WidgetTester</span> <span class="pd">tester</span>) <span class="b">async</span> {
    <span class="tlfr">provideMockedNetworkImages</span>(() <span class="b">async</span> {
      <span class="ceol">&#47;&#47; Now we can pump NetworkImages without crashing our tests. Yay!</span>
      <span class="b">await</span> <span class="pr">tester</span>.<span class="imr">pumpWidget</span>(
        <span class="ctr">MaterialApp</span>(
          <span class="pr">home</span>: <span class="ctr">Image</span>.<span class="ctr">network</span>(<span class="ls">'https://example.com/image.png'</span>)<span class="comma">,</span>
        )<span class="comma">,</span>
      )<span class="semicolon">;</span>

      <span class="ceol">&#47;&#47; No crashes.</span>
    })<span class="semicolon">;</span>
  })<span class="semicolon">;</span>
}
</code></pre></div></div>

<p><small><i>(For a more comprehensive sample, see <a href="https://github.com/roughike/inKino/blob/development/test/ui/events/events_page_test.dart#L42-L51">this widget test</a> in the inKino app.)</i></small></p>

<p><strong>Step 3:</strong> there’s no step three! Just see your widget tests with <code class="language-plaintext highlighter-rouge">Image.network</code> widgets pass with flying colors.</p>

<p>Behind the scenes, wrapping your code with <code class="language-plaintext highlighter-rouge">provideMockedNetworkImages</code> creates a new <em>Zone</em>, in which the default HTTP client has been replaced with a mocked one. The mocked client always responds with a transparent image and a <code class="language-plaintext highlighter-rouge">200 - OK</code> status. And your <code class="language-plaintext highlighter-rouge">Image.network</code> widget tests are now happier than ever.</p>

<p>And yes - <code class="language-plaintext highlighter-rouge">provideMockedNetworkImages</code> will override all of your HTTP GET requests. But you generally don’t want to do any API communication in your tests anyway. And luckily anything other than <code class="language-plaintext highlighter-rouge">Image.network</code> is quite easy to mock out.</p>]]></content><author><name>Iiro Krankka</name></author><summary type="html"><![CDATA[Widget tests are fun. At least as long as you don't pump any Image.network widgets. But what if I told you there's a simple way to solve those crashes and continue having that widget testing fun? And no, I'm not as cool as Morpheus is, but I did take some Taekwon-Do lessons back in the day.]]></summary></entry></feed>