<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://didarul.codes/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://didarul.codes/blog/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-04-14T23:25:19+08:00</updated><id>https://didarul.codes/blog/feed.xml</id><title type="html">Didarul’s Blog</title><subtitle>Expert Flutter tutorials, development tips, and best practices for building high-performance mobile applications. Learn state management, architecture patterns, and UI/UX implementation in Flutter.</subtitle><author><name>Md Didarul Islam</name></author><entry><title type="html">Unifying Native APIs in Flutter: A Guide to ffigen and jnigen</title><link href="https://didarul.codes/blog/flutter%20advanced/2025/12/09/unifying-native-apis-flutter.html" rel="alternate" type="text/html" title="Unifying Native APIs in Flutter: A Guide to ffigen and jnigen" /><published>2025-12-09T14:00:00+08:00</published><updated>2025-12-09T14:00:00+08:00</updated><id>https://didarul.codes/blog/flutter%20advanced/2025/12/09/unifying-native-apis-flutter</id><content type="html" xml:base="https://didarul.codes/blog/flutter%20advanced/2025/12/09/unifying-native-apis-flutter.html"><![CDATA[<p>While Flutter’s Method Channels are the standard way to communicate with platform-native code, they aren’t always the most efficient or ergonomic solution. Especially when dealing with existing native libraries or synchronous operations.</p>

<p>In this post, we will explore how to call native code directly using <strong>Dart FFI</strong> (for C/iOS) and <strong>JNI</strong> (for Kotlin/Android). We will use code generators (<code class="language-plaintext highlighter-rouge">ffigen</code> and <code class="language-plaintext highlighter-rouge">jnigen</code>) to automate the binding process, and then wrap them in a clean, platform-agnostic Dart interface.</p>

<p>To demonstrate this, we will implement a simple “Caesar Cipher” encryption utility.</p>

<h2 id="1-the-native-implementations">1. The Native Implementations</h2>

<p>First, let’s look at the native code we want to access.</p>

<h3 id="android-kotlin">Android (Kotlin)</h3>
<p>On the Android side, we have a simple class <code class="language-plaintext highlighter-rouge">EncryptionUtil</code> that performs character shifting.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// android/src/main/kotlin/com/example/dart_native_bindings/EncryptionUtil.kt</span>
<span class="kd">class</span> <span class="nc">EncryptionUtil</span> <span class="p">{</span>
    <span class="k">fun</span> <span class="nf">encrypt</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="nc">String</span><span class="p">,</span> <span class="n">shift</span><span class="p">:</span> <span class="nc">Int</span><span class="p">):</span> <span class="nc">String</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">result</span> <span class="p">=</span> <span class="nc">StringBuilder</span><span class="p">()</span>
        <span class="k">for</span> <span class="p">(</span><span class="n">character</span> <span class="k">in</span> <span class="n">input</span><span class="p">.</span><span class="nf">toCharArray</span><span class="p">())</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">character</span> <span class="p">!=</span> <span class="sc">' '</span><span class="p">)</span> <span class="p">{</span>
                <span class="kd">val</span> <span class="py">originalAlphabetPosition</span> <span class="p">=</span> <span class="n">character</span><span class="p">.</span><span class="n">code</span> <span class="p">-</span> <span class="sc">'a'</span><span class="p">.</span><span class="n">code</span>
                <span class="kd">val</span> <span class="py">newAlphabetPosition</span> <span class="p">=</span> <span class="p">(</span><span class="n">originalAlphabetPosition</span> <span class="p">+</span> <span class="n">shift</span><span class="p">)</span> <span class="p">%</span> <span class="mi">26</span>
                <span class="kd">val</span> <span class="py">newCharacter</span> <span class="p">=</span> <span class="p">(</span><span class="sc">'a'</span><span class="p">.</span><span class="n">code</span> <span class="p">+</span> <span class="n">newAlphabetPosition</span><span class="p">).</span><span class="nf">toChar</span><span class="p">()</span>
                <span class="n">result</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">newCharacter</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">result</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">character</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">result</span><span class="p">.</span><span class="nf">toString</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="ios-c">iOS (C)</h3>

<p>For iOS, we are using C to interact with Dart FFI. Note that this function manually allocates memory for the result, which we will need to manage carefully in Dart.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ios/Classes/encryption.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span>
<span class="kt">char</span><span class="o">*</span> <span class="nf">encrypt</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">input</span><span class="p">,</span> <span class="kt">int</span> <span class="n">shift</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">input</span><span class="p">);</span>
    <span class="kt">char</span><span class="o">*</span> <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="n">length</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span> 
    
    <span class="k">for</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">char</span> <span class="n">ch</span> <span class="o">=</span> <span class="n">input</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="k">if</span><span class="p">(</span><span class="n">ch</span> <span class="o">&gt;=</span> <span class="sc">'a'</span> <span class="o">&amp;&amp;</span> <span class="n">ch</span> <span class="o">&lt;=</span> <span class="sc">'z'</span><span class="p">){</span>
            <span class="n">ch</span> <span class="o">=</span> <span class="n">ch</span> <span class="o">+</span> <span class="n">shift</span><span class="p">;</span>
            <span class="k">if</span><span class="p">(</span><span class="n">ch</span> <span class="o">&gt;</span> <span class="sc">'z'</span><span class="p">){</span>
                <span class="n">ch</span> <span class="o">=</span> <span class="n">ch</span> <span class="o">-</span> <span class="mi">26</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="n">result</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">ch</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">result</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">ch</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    <span class="n">result</span><span class="p">[</span><span class="n">length</span><span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span> <span class="c1">// Null terminator</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<hr />

<h2 id="2-generating-bindings">2. Generating Bindings</h2>

<p>Writing the Dart boilerplate to talk to these languages manually is error-prone. We use <code class="language-plaintext highlighter-rouge">ffigen</code> and <code class="language-plaintext highlighter-rouge">jnigen</code> to generate the bindings automatically.</p>

<h3 id="configuring-ffigen-ios">Configuring ffigen (iOS)</h3>

<p><code class="language-plaintext highlighter-rouge">ffigen</code> scans C header files and creates Dart FFI bindings.</p>

<p><strong>ffigen.yaml</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">headers</span><span class="pi">:</span>
  <span class="na">entry-points</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s1">'</span><span class="s">ios/Classes/*.c'</span>
<span class="na">output</span><span class="pi">:</span> <span class="s1">'</span><span class="s">lib/ios_bindings.dart'</span>
</code></pre></div></div>

<h3 id="configuring-jnigen-android">Configuring jnigen (Android)</h3>

<p><code class="language-plaintext highlighter-rouge">jnigen</code> works similarly but for Java/Kotlin classes via JNI.</p>

<p><strong>jnigen.yaml</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">android_sdk_config</span><span class="pi">:</span>
  <span class="na">add_gradle_deps</span><span class="pi">:</span> <span class="kc">true</span>

<span class="na">output</span><span class="pi">:</span>
  <span class="na">dart</span><span class="pi">:</span>
    <span class="na">path</span><span class="pi">:</span> <span class="s">lib/android_bindings.dart</span>
    <span class="na">structure</span><span class="pi">:</span> <span class="s">single_file</span>

<span class="na">source_path</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s2">"</span><span class="s">android/src/main/kotlin"</span>
<span class="na">classes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s2">"</span><span class="s">com.example.dart_native_bindings.EncryptionUtil"</span>
</code></pre></div></div>

<p>Once configured, running the generation commands (e.g., <code class="language-plaintext highlighter-rouge">dart run ffigen --config ffigen.yaml</code> and <code class="language-plaintext highlighter-rouge">dart run jnigen --config jnigen.yaml</code>) will produce <code class="language-plaintext highlighter-rouge">ios_bindings.dart</code> and <code class="language-plaintext highlighter-rouge">android_bindings.dart</code>.</p>

<hr />

<h2 id="3-the-unified-dart-interface">3. The Unified Dart Interface</h2>

<p>Now for the fun part: abstracting away the platform differences. We want our Flutter UI to simply call <code class="language-plaintext highlighter-rouge">encrypt()</code> without worrying about whether it’s running on Android or iOS.</p>

<p>We will create a <code class="language-plaintext highlighter-rouge">CommonEncryptor</code> class that employs the Strategy pattern.</p>

<h3 id="the-code">The Code</h3>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="s">'dart:ffi'</span> <span class="k">as</span> <span class="n">ffi</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'dart:io'</span><span class="o">;</span>

<span class="kn">import</span> <span class="s">'package:dart_native_bindings/android_bindings.dart'</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'package:dart_native_bindings/ios_bindings.dart'</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'package:ffi/ffi.dart'</span><span class="o">;</span>
<span class="kn">import</span> <span class="s">'package:jni/jni.dart'</span><span class="o">;</span>

<span class="c1">/// Provides a single encryption API regardless of platform.</span>
<span class="kd">class</span> <span class="nc">CommonEncryptor</span> <span class="p">{</span>
  <span class="n">CommonEncryptor</span><span class="p">()</span> <span class="o">:</span> <span class="n">_delegate</span> <span class="o">=</span> <span class="n">_createDelegate</span><span class="p">();</span>

  <span class="kd">final</span> <span class="n">_PlatformEncryptor</span> <span class="n">_delegate</span><span class="p">;</span>

  <span class="kt">String</span> <span class="n">encrypt</span><span class="p">(</span><span class="kt">String</span> <span class="n">input</span><span class="p">,</span> <span class="kt">int</span> <span class="n">shift</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">_delegate</span><span class="o">.</span><span class="na">encrypt</span><span class="p">(</span><span class="n">input</span><span class="p">,</span> <span class="n">shift</span><span class="p">);</span>

  <span class="kd">static</span> <span class="n">_PlatformEncryptor</span> <span class="n">_createDelegate</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">Platform</span><span class="o">.</span><span class="na">isAndroid</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="n">_AndroidEncryptor</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">Platform</span><span class="o">.</span><span class="na">isIOS</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="n">_IosEncryptor</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="k">throw</span> <span class="n">UnsupportedError</span><span class="p">(</span><span class="s">'Unsupported platform'</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">abstract</span> <span class="kd">interface</span> <span class="kd">class</span> <span class="nc">_PlatformEncryptor</span> <span class="p">{</span>
  <span class="kt">String</span> <span class="n">encrypt</span><span class="p">(</span><span class="kt">String</span> <span class="n">input</span><span class="p">,</span> <span class="kt">int</span> <span class="n">shift</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="the-android-implementation-jni">The Android Implementation (JNI)</h3>

<p>Here we interact with the generated <code class="language-plaintext highlighter-rouge">EncryptionUtil</code> class.</p>

<p><strong>Key Note on Memory:</strong> When using JNI, we must convert Dart strings to JNI strings (<code class="language-plaintext highlighter-rouge">toJString()</code>). After the operation, it is crucial to call <code class="language-plaintext highlighter-rouge">.release()</code> on JNI objects to prevent memory leaks in the Java Native Interface.</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">_AndroidEncryptor</span> <span class="kd">implements</span> <span class="n">_PlatformEncryptor</span> <span class="p">{</span>
  <span class="n">_AndroidEncryptor</span><span class="p">()</span> <span class="o">:</span> <span class="n">_util</span> <span class="o">=</span> <span class="n">EncryptionUtil</span><span class="p">();</span>

  <span class="kd">final</span> <span class="n">EncryptionUtil</span> <span class="n">_util</span><span class="p">;</span>

  <span class="nd">@override</span>
  <span class="kt">String</span> <span class="n">encrypt</span><span class="p">(</span><span class="kt">String</span> <span class="n">input</span><span class="p">,</span> <span class="kt">int</span> <span class="n">shift</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// 1. Convert Dart String to Java String</span>
    <span class="kd">final</span> <span class="n">inputJString</span> <span class="o">=</span> <span class="n">input</span><span class="o">.</span><span class="na">toJString</span><span class="p">();</span>
    
    <span class="c1">// 2. Call the native method</span>
    <span class="kd">final</span> <span class="n">encrypted</span> <span class="o">=</span> <span class="n">_util</span><span class="o">.</span><span class="na">encrypt</span><span class="p">(</span><span class="n">inputJString</span><span class="p">,</span> <span class="n">shift</span><span class="p">);</span>
    
    <span class="c1">// 3. Convert back to Dart String</span>
    <span class="kd">final</span> <span class="n">result</span> <span class="o">=</span> <span class="n">encrypted</span><span class="o">.</span><span class="na">toDartString</span><span class="p">();</span>
    
    <span class="c1">// 4. Clean up JNI references</span>
    <span class="n">encrypted</span><span class="o">.</span><span class="na">release</span><span class="p">();</span>
    <span class="n">inputJString</span><span class="o">.</span><span class="na">release</span><span class="p">();</span>
    
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="the-ios-implementation-ffi">The iOS Implementation (FFI)</h3>

<p>Here we use the <code class="language-plaintext highlighter-rouge">NativeLibrary</code> generated by ffigen.</p>

<p><strong>Key Note on Memory:</strong> Since our C code used <code class="language-plaintext highlighter-rouge">malloc</code> to allocate the result, we must use <code class="language-plaintext highlighter-rouge">malloc.free</code> in Dart to release that memory. Similarly, we allocate memory for the input string using <code class="language-plaintext highlighter-rouge">toNativeUtf8()</code>, which also requires freeing.</p>

<div class="language-dart highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">_IosEncryptor</span> <span class="kd">implements</span> <span class="n">_PlatformEncryptor</span> <span class="p">{</span>
  <span class="c1">// Load the process symbol table</span>
  <span class="n">_IosEncryptor</span><span class="p">()</span> <span class="o">:</span> <span class="n">_library</span> <span class="o">=</span> <span class="n">NativeLibrary</span><span class="p">(</span><span class="n">ffi</span><span class="o">.</span><span class="na">DynamicLibrary</span><span class="o">.</span><span class="na">process</span><span class="p">());</span>

  <span class="kd">final</span> <span class="n">NativeLibrary</span> <span class="n">_library</span><span class="p">;</span>

  <span class="nd">@override</span>
  <span class="kt">String</span> <span class="n">encrypt</span><span class="p">(</span><span class="kt">String</span> <span class="n">input</span><span class="p">,</span> <span class="kt">int</span> <span class="n">shift</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// 1. Allocate native memory for the string</span>
    <span class="kd">final</span> <span class="n">inputPtr</span> <span class="o">=</span> <span class="n">input</span><span class="o">.</span><span class="na">toNativeUtf8</span><span class="p">();</span>
    
    <span class="c1">// 2. Call the C function</span>
    <span class="kd">final</span> <span class="n">encryptedPtr</span> <span class="o">=</span> <span class="n">_library</span><span class="o">.</span><span class="na">encrypt</span><span class="p">(</span><span class="n">inputPtr</span><span class="o">.</span><span class="na">cast</span><span class="p">&lt;</span><span class="n">ffi</span><span class="o">.</span><span class="na">Char</span><span class="p">&gt;(),</span> <span class="n">shift</span><span class="p">);</span>
    
    <span class="c1">// 3. Free the input pointer immediately</span>
    <span class="n">malloc</span><span class="o">.</span><span class="na">free</span><span class="p">(</span><span class="n">inputPtr</span><span class="p">);</span>
    
    <span class="c1">// 4. Convert result to Dart string</span>
    <span class="kd">final</span> <span class="n">result</span> <span class="o">=</span> <span class="n">encryptedPtr</span><span class="o">.</span><span class="na">cast</span><span class="p">&lt;</span><span class="n">Utf8</span><span class="p">&gt;()</span><span class="o">.</span><span class="na">toDartString</span><span class="p">();</span>
    
    <span class="c1">// 5. Free the result pointer (allocated by C's malloc)</span>
    <span class="n">malloc</span><span class="o">.</span><span class="na">free</span><span class="p">(</span><span class="n">encryptedPtr</span><span class="p">);</span>
    
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

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

<p>By combining <code class="language-plaintext highlighter-rouge">jnigen</code> and <code class="language-plaintext highlighter-rouge">ffigen</code>, we can leverage the full power of native platforms with strictly typed bindings. While manual memory management (releasing pointers and JNI references) requires care, the performance gains and direct access to native libraries make it a powerful tool in the Flutter developer’s arsenal.</p>]]></content><author><name>Md Didarul Islam</name></author><category term="Flutter Advanced" /><category term="flutter" /><category term="native" /><category term="ffi" /><category term="jni" /><category term="android" /><category term="ios" /><summary type="html"><![CDATA[Explore how to call native code directly using Dart FFI and JNI. We will use code generators (ffigen and jnigen) to automate the binding process.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to Add iOS 26 Liquid Glass Effect in Flutter Using a Custom SwiftUI View</title><link href="https://didarul.codes/blog/ui/ux/tips%20&%20tricks/2025/06/17/liquid-glass-ios26-flutter.html" rel="alternate" type="text/html" title="How to Add iOS 26 Liquid Glass Effect in Flutter Using a Custom SwiftUI View" /><published>2025-06-17T10:00:00+08:00</published><updated>2025-06-17T10:00:00+08:00</updated><id>https://didarul.codes/blog/ui/ux/tips%20&amp;%20tricks/2025/06/17/liquid-glass-ios26-flutter</id><content type="html" xml:base="https://didarul.codes/blog/ui/ux/tips%20&amp;%20tricks/2025/06/17/liquid-glass-ios26-flutter.html"><![CDATA[<p>The new <strong>Liquid Glass</strong> effect in iOS 26 looks stunning, but Flutter doesn’t support it natively yet. Here’s how you can embed a native SwiftUI view with the Liquid Glass effect inside your Flutter app using PlatformView.</p>

<hr />

<h3 id="why-use-platformview">Why Use PlatformView?</h3>

<p>Flutter’s PlatformView API lets you embed native views (SwiftUI, UIKit, etc.) directly into your Flutter widget tree. This is perfect for using new iOS features before they’re available in Flutter.</p>

<hr />

<h3 id="demo">Demo</h3>

<p><img src="/assets/img/flutter_liquid_glass_platform_view_demo.gif" alt="Liquid Glass Demo" /></p>

<hr />

<h3 id="project-overview">Project Overview</h3>

<ul>
  <li><strong>Flutter iOS-only app</strong></li>
  <li>Custom SwiftUI view (<code class="language-plaintext highlighter-rouge">LiquidGlassExampleView</code>) with <code class="language-plaintext highlighter-rouge">.glassEffect</code> (iOS 26+)</li>
  <li>Integrated via PlatformView (<code class="language-plaintext highlighter-rouge">UiKitView</code> in Flutter)</li>
</ul>

<hr />

<h3 id="key-files">Key Files</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">lib/main.dart</code>: Uses <code class="language-plaintext highlighter-rouge">UiKitView</code> to display the native SwiftUI view.</li>
  <li><code class="language-plaintext highlighter-rouge">ios/Runner/HelloSwiftUI.swift</code>: Contains the SwiftUI view and PlatformView integration.</li>
  <li><code class="language-plaintext highlighter-rouge">ios/Runner/AppDelegate.swift</code>: Registers the PlatformView for Flutter.</li>
</ul>

<hr />

<h3 id="1-create-the-swiftui-view-with-liquid-glass">1. Create the SwiftUI View with Liquid Glass</h3>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">struct</span> <span class="kt">LiquidGlassExampleView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">Group</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">#available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">26.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">GlassEffectContainer</span> <span class="p">{</span>
                    <span class="kt">ContentBody</span><span class="p">()</span>
                        <span class="o">.</span><span class="nf">buttonStyle</span><span class="p">(</span><span class="o">.</span><span class="n">glass</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="kt">ContentBody</span><span class="p">()</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">ContentBody</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="kd">@State</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">offset</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="o">-</span><span class="mi">10</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="k">if</span> <span class="k">#available</span><span class="p">(</span><span class="n">iOS</span> <span class="mf">26.0</span><span class="p">,</span> <span class="o">*</span><span class="p">)</span> <span class="p">{</span>
                <span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"globe"</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">imageScale</span><span class="p">(</span><span class="o">.</span><span class="n">large</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">foregroundStyle</span><span class="p">(</span><span class="o">.</span><span class="n">tint</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">glassEffect</span><span class="p">()</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Liquid Glass"</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">glassEffect</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">.</span><span class="nf">rect</span><span class="p">(</span><span class="nv">cornerRadius</span><span class="p">:</span> <span class="mi">8</span><span class="p">))</span>
                    <span class="o">.</span><span class="nf">offset</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">offset</span><span class="p">)</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="c1">// Fallback for earlier iOS versions</span>
                <span class="kt">Image</span><span class="p">(</span><span class="nv">systemName</span><span class="p">:</span> <span class="s">"globe"</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">imageScale</span><span class="p">(</span><span class="o">.</span><span class="n">large</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">foregroundStyle</span><span class="p">(</span><span class="o">.</span><span class="n">tint</span><span class="p">)</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Liquid Glass"</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">padding</span><span class="p">(</span><span class="mi">16</span><span class="p">)</span>
                    <span class="o">.</span><span class="nf">offset</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">offset</span><span class="p">)</span>
            <span class="p">}</span>
            <span class="kt">Button</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="p">{</span>
                <span class="nf">withAnimation</span><span class="p">(</span><span class="o">.</span><span class="nf">linear</span><span class="p">(</span><span class="nv">duration</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">))</span> <span class="p">{</span>
                    <span class="n">offset</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">*</span> <span class="o">-</span><span class="mi">1</span>
                <span class="p">}</span>
            <span class="p">})</span> <span class="p">{</span>
                <span class="kt">Text</span><span class="p">(</span><span class="s">"Animate"</span><span class="p">)</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<hr />

<h3 id="2-expose-the-swiftui-view-to-flutter">2. Expose the SwiftUI View to Flutter</h3>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">HelloSwiftUIViewFactory</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">FlutterPlatformViewFactory</span> <span class="p">{</span>
    <span class="o">...</span>
    <span class="kd">func</span> <span class="nf">create</span><span class="p">(</span><span class="n">withFrame</span> <span class="nv">frame</span><span class="p">:</span> <span class="kt">CGRect</span><span class="p">,</span> <span class="n">viewIdentifier</span> <span class="nv">viewId</span><span class="p">:</span> <span class="kt">Int64</span><span class="p">,</span> <span class="n">arguments</span> <span class="nv">args</span><span class="p">:</span> <span class="kt">Any</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kd">any</span> <span class="kt">FlutterPlatformView</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">view</span> <span class="o">=</span> <span class="kt">HelloSwiftUIView</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">view</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">HelloSwiftUIView</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">FlutterPlatformView</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">hostingController</span><span class="p">:</span> <span class="kt">UIHostingController</span><span class="o">&lt;</span><span class="kt">LiquidGlassExampleView</span><span class="o">&gt;</span>
    <span class="o">...</span>
    <span class="k">override</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">swiftUIView</span> <span class="o">=</span> <span class="kt">LiquidGlassExampleView</span><span class="p">()</span>
        <span class="n">hostingController</span> <span class="o">=</span> <span class="kt">UIHostingController</span><span class="p">(</span><span class="nv">rootView</span><span class="p">:</span> <span class="n">swiftUIView</span><span class="p">)</span>
        <span class="n">hostingController</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">backgroundColor</span> <span class="o">=</span> <span class="o">.</span><span class="n">clear</span>
        <span class="o">...</span>
    <span class="p">}</span>
    <span class="kd">func</span> <span class="nf">view</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">UIView</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">hostingController</span><span class="o">.</span><span class="n">view</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<hr />

<h3 id="3-register-the-platformview-in-appdelegate">3. Register the PlatformView in AppDelegate</h3>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">override</span> <span class="kd">func</span> <span class="nf">application</span><span class="p">(</span>
  <span class="n">_</span> <span class="nv">application</span><span class="p">:</span> <span class="kt">UIApplication</span><span class="p">,</span>
  <span class="n">didFinishLaunchingWithOptions</span> <span class="nv">launchOptions</span><span class="p">:</span> <span class="p">[</span><span class="kt">UIApplication</span><span class="o">.</span><span class="kt">LaunchOptionsKey</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]?</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
  <span class="o">...</span>
  <span class="k">let</span> <span class="nv">factory</span> <span class="o">=</span> <span class="kt">HelloSwiftUIViewFactory</span><span class="p">(</span><span class="nv">messenger</span><span class="p">:</span> <span class="n">registrar</span><span class="o">.</span><span class="nf">messenger</span><span class="p">())</span>
  <span class="n">registrar</span><span class="o">.</span><span class="nf">register</span><span class="p">(</span><span class="n">factory</span><span class="p">,</span> <span class="nv">withId</span><span class="p">:</span> <span class="s">"HelloSwiftUIView"</span><span class="p">)</span>
  <span class="o">...</span>
<span class="p">}</span></code></pre></figure>

<hr />

<h3 id="4-use-the-platformview-in-flutter">4. Use the PlatformView in Flutter</h3>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">UiKitView</span><span class="p">(</span>
  <span class="nl">viewType:</span> <span class="s">'HelloSwiftUIView'</span><span class="p">,</span>
  <span class="nl">layoutDirection:</span> <span class="n">TextDirection</span><span class="o">.</span><span class="na">ltr</span><span class="p">,</span>
  <span class="nl">creationParams:</span> <span class="kc">null</span><span class="p">,</span>
  <span class="nl">creationParamsCodec:</span> <span class="n">StandardMessageCodec</span><span class="p">(),</span>
<span class="p">)</span></code></pre></figure>

<p>You can layer this over any Flutter widget, such as an image background:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">Stack</span><span class="p">(</span>
  <span class="nl">children:</span> <span class="p">[</span>
    <span class="n">Positioned</span><span class="o">.</span><span class="na">fill</span><span class="p">(</span>
      <span class="nl">child:</span> <span class="n">DecoratedBox</span><span class="p">(</span>
        <span class="nl">decoration:</span> <span class="n">BoxDecoration</span><span class="p">(</span>
          <span class="nl">image:</span> <span class="n">DecorationImage</span><span class="p">(</span>
            <span class="nl">image:</span> <span class="n">AssetImage</span><span class="p">(</span><span class="s">'assets/melaka.jpeg'</span><span class="p">),</span>
            <span class="nl">fit:</span> <span class="n">BoxFit</span><span class="o">.</span><span class="na">cover</span><span class="p">,</span>
          <span class="p">),</span>
        <span class="p">),</span>
      <span class="p">),</span>
    <span class="p">),</span>
    <span class="n">UiKitView</span><span class="p">(...),</span>
  <span class="p">],</span>
<span class="p">)</span></code></pre></figure>

<hr />

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

<p>With just a few steps, you can bring the latest iOS 26 visual effects to your Flutter app using SwiftUI and PlatformView. This approach lets you stay on the cutting edge of iOS design while keeping the productivity of Flutter.</p>

<hr />

<p><em>Happy coding with Flutter and iOS!</em></p>]]></content><author><name>Md Didarul Islam</name></author><category term="UI/UX" /><category term="Tips &amp; Tricks" /><category term="flutter" /><category term="ios" /><category term="swiftui" /><category term="liquid glass" /><category term="platformview" /><category term="ui/ux" /><category term="ios 26" /><summary type="html"><![CDATA[Learn how to use the new iOS 26 Liquid Glass effect in your Flutter app by embedding a custom SwiftUI view using PlatformView.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/flutter_liquid_glass_platform_view.png" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/flutter_liquid_glass_platform_view.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Understanding Dart Mixins: A Comprehensive Guide</title><link href="https://didarul.codes/blog/flutter%20basics/2025/04/29/dart-mixins.html" rel="alternate" type="text/html" title="Understanding Dart Mixins: A Comprehensive Guide" /><published>2025-04-29T10:00:00+08:00</published><updated>2025-04-29T10:00:00+08:00</updated><id>https://didarul.codes/blog/flutter%20basics/2025/04/29/dart-mixins</id><content type="html" xml:base="https://didarul.codes/blog/flutter%20basics/2025/04/29/dart-mixins.html"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>In the world of Flutter development, Dart mixins are a powerful feature that allows developers to share code between classes without using inheritance. This guide will explore what mixins are, how to use them and their advantages in Flutter applications.</p>

<h2 id="what-are-mixins">What are Mixins?</h2>
<p>Mixins are a way to reuse code in multiple classes without the need for inheritance. They allow you to define methods and properties that can be shared across different classes, promoting code reusability and reducing duplication.</p>

<h2 id="syntax-of-mixins">Syntax of Mixins</h2>
<p>Mixins are defined using the <code class="language-plaintext highlighter-rouge">mixin</code> keyword, followed by the mixin name. Here’s a simple example:</p>

<h3 id="mixin">Mixin</h3>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">mixin</span> <span class="nc">Logger</span> <span class="p">{</span>  
  <span class="kt">void</span> <span class="n">log</span><span class="p">(</span><span class="kt">String</span> <span class="n">message</span><span class="p">)</span> <span class="p">{</span>  
    <span class="n">print</span><span class="p">(</span><span class="s">'Log: </span><span class="si">$message</span><span class="s">'</span><span class="p">);</span>  
  <span class="p">}</span>  
<span class="p">}</span></code></pre></figure>

<h3 id="mixin-classes">Mixin classes</h3>
<p>Mixin classes can be used to add functionality to other classes without creating a complex inheritance hierarchy. These can be used both as a <code class="language-plaintext highlighter-rouge">class</code> and a <code class="language-plaintext highlighter-rouge">mixin</code>. The <code class="language-plaintext highlighter-rouge">mixin</code> keyword is used to define a mixin, while the <code class="language-plaintext highlighter-rouge">mixin class</code> keyword is used to define a mixin that can be used as a superclass.</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">mixin</span> <span class="nc">class</span> <span class="n">MyMixin</span> <span class="p">{</span>  
  <span class="kt">void</span> <span class="n">greet</span><span class="p">()</span> <span class="p">{</span>  
    <span class="n">print</span><span class="p">(</span><span class="s">'Hello from MyMixin!'</span><span class="p">);</span>  
  <span class="p">}</span>  
<span class="p">}</span></code></pre></figure>

<h3 id="using-mixins-in-classes">Using Mixins in Classes</h3>
<p>To use a mixin in a class, you simply use the <code class="language-plaintext highlighter-rouge">with</code> keyword followed by the mixin name. Here’s how you can use the <code class="language-plaintext highlighter-rouge">Logger</code> mixin in a class:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">MyClass</span> <span class="k">with</span> <span class="n">Logger</span> <span class="p">{</span>  
  <span class="kt">void</span> <span class="n">performAction</span><span class="p">()</span> <span class="p">{</span>  
    <span class="n">log</span><span class="p">(</span><span class="s">'Action performed'</span><span class="p">);</span>  
  <span class="p">}</span>  
<span class="p">}</span></code></pre></figure>

<h3 id="extending-mixins">Extending mixins</h3>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">MyPowerfulLogger</span> <span class="kd">extends</span> <span class="n">Logger</span> <span class="p">{</span>  
  <span class="kt">void</span> <span class="n">logWithTimestamp</span><span class="p">(</span><span class="kt">String</span> <span class="n">message</span><span class="p">)</span> <span class="p">{</span>  
    <span class="kd">final</span> <span class="n">timestamp</span> <span class="o">=</span> <span class="n">DateTime</span><span class="o">.</span><span class="na">now</span><span class="p">();</span>  
    <span class="n">print</span><span class="p">(</span><span class="s">'[</span><span class="si">$timestamp</span><span class="s">] Log: </span><span class="si">$message</span><span class="s">'</span><span class="p">);</span>  
  <span class="p">}</span>  
<span class="p">}</span></code></pre></figure>

<h2 id="advantages-of-using-mixins">Advantages of Using Mixins</h2>
<ul>
  <li><strong>Code Reusability</strong>: Mixins allow you to define common functionality in one place and reuse it across multiple classes.</li>
  <li><strong>Cleaner Code</strong>: By using mixins, you can keep your classes clean and focused on their primary responsibilities.</li>
  <li><strong>Flexibility</strong>: Mixins can be combined with other classes and mixins, allowing for flexible class designs.
    <h2 id="common-use-cases-for-mixins">Common Use Cases for Mixins</h2>
  </li>
  <li><strong>Logging</strong>: Mixins are often used for logging purposes, allowing you to add logging functionality to multiple classes without duplicating code.</li>
  <li><strong>Analytics</strong>: You can create mixins for analytics tracking, allowing you to track events across different classes.</li>
  <li><strong>User Authentication</strong>: Mixins can be used to handle user authentication logic, making it easy to add authentication features to different classes.
    <h2 id="best-practices-for-using-mixins">Best Practices for Using Mixins</h2>
  </li>
  <li><strong>Keep Mixins Focused</strong>: Each mixin should have a single responsibility. Avoid creating large mixins that do too much.</li>
  <li><strong>Avoid Overusing Mixins</strong>: While mixins are powerful, overusing them can lead to complex class hierarchies. Use them judiciously.</li>
  <li><strong>Document Your Mixins</strong>: Provide clear documentation for your mixins to help other developers understand their purpose and usage.
    <h2 id="conclusion">Conclusion</h2>
    <p>Dart mixins are a powerful feature that can greatly enhance your Flutter development experience. By understanding how to use mixins effectively, you can write cleaner, more maintainable code and promote code reusability in your applications. Whether you’re logging events, tracking analytics or handling user authentication, mixins can help you achieve your goals with ease.</p>
    <h2 id="further-reading">Further Reading</h2>
  </li>
  <li><a href="https://dart.dev/guides/language/language-tour#mixins">Dart Language Tour: Mixins</a></li>
  <li><a href="https://flutter.dev/docs/development/ui/advanced/mixins">Flutter Documentation: Mixins</a></li>
  <li><a href="https://dart.dev/guides/language/effective-dart/design#mixins">Effective Dart: Mixins</a>
    <h2 id="feedback">Feedback</h2>
    <p>I hope this guide has helped you understand Dart mixins better. If you have any questions or feedback, feel free to reach out to me!</p>
  </li>
</ul>]]></content><author><name>Md Didarul Islam</name></author><category term="Flutter Basics" /><category term="dart" /><category term="mixins" /><category term="flutter" /><category term="object-oriented programming" /><summary type="html"><![CDATA[A deep dive into Dart mixins, their syntax, and practical use cases in Flutter development.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Add a delay before displaying loading indicator</title><link href="https://didarul.codes/blog/tips%20&%20tricks/ui/ux/2025/04/19/add-loading-delay-for-flutter-loaders.html" rel="alternate" type="text/html" title="Add a delay before displaying loading indicator" /><published>2025-04-19T10:00:00+08:00</published><updated>2025-04-19T10:00:00+08:00</updated><id>https://didarul.codes/blog/tips%20&amp;%20tricks/ui/ux/2025/04/19/add-loading-delay-for-flutter-loaders</id><content type="html" xml:base="https://didarul.codes/blog/tips%20&amp;%20tricks/ui/ux/2025/04/19/add-loading-delay-for-flutter-loaders.html"><![CDATA[<p>When your app performs fast operations, showing a progress loader for a split second can create a jarring “flicker” effect. Users notice this and it can make your app feel unpolished.</p>

<h3 id="solution">Solution</h3>
<p>Add a minimum display delay to your loader. This ensures the loader only appears if the operation takes longer than expected. Have a look at the screenshot to understand it better.</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">Timer</span><span class="o">?</span> <span class="n">_loaderTimer</span><span class="p">;</span>  
<span class="kt">bool</span> <span class="n">_isLoading</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>  

<span class="n">Future</span><span class="p">&lt;</span><span class="kt">void</span><span class="p">&gt;</span> <span class="n">_fetchData</span><span class="p">()</span> <span class="kd">async</span> <span class="p">{</span>  
  <span class="c1">// Schedule the loader to appear after 500ms  </span>
  <span class="n">_loaderTimer</span> <span class="o">=</span> <span class="n">Timer</span><span class="p">(</span><span class="kd">const</span> <span class="n">Duration</span><span class="p">(</span><span class="nl">milliseconds:</span> <span class="mi">500</span><span class="p">),</span> <span class="p">()</span> <span class="p">{</span>  
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">_isLoading</span><span class="p">)</span> <span class="n">setState</span><span class="p">(()</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">_isLoading</span> <span class="o">=</span> <span class="kc">true</span><span class="p">);</span>  
  <span class="p">});</span>  

  <span class="k">try</span> <span class="p">{</span>  
    <span class="k">await</span> <span class="n">yourAsyncOperation</span><span class="p">();</span>  
  <span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>  
    <span class="c1">// Cancel the timer (if it hasn’t triggered yet)  </span>
    <span class="n">_loaderTimer</span><span class="o">?.</span><span class="na">cancel</span><span class="p">();</span>  
    <span class="n">setState</span><span class="p">(()</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">_isLoading</span> <span class="o">=</span> <span class="kc">false</span><span class="p">);</span>  
  <span class="p">}</span>  
<span class="p">}</span> </code></pre></figure>

<p>Why This example works:</p>
<ul>
  <li>No flicker: If the task finishes before 500ms, the loader never appears.</li>
  <li>Smooth UX: For longer tasks, the loader shows after the delay, avoiding abrupt transitions</li>
</ul>]]></content><author><name>Md Didarul Islam</name></author><category term="Tips &amp; Tricks" /><category term="UI/UX" /><category term="flutter" /><category term="loading indicator" /><category term="user experience" /><category term="timers" /><category term="async operations" /><summary type="html"><![CDATA[Learn how to add a delay before displaying a loading indicator in Flutter to improve user experience by avoiding flicker effects during fast operations.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Deploy Flutter Web app to Github Pages</title><link href="https://didarul.codes/blog/devops/2025/03/08/deploy-flutter-web-app-to-github-pages.html" rel="alternate" type="text/html" title="Deploy Flutter Web app to Github Pages" /><published>2025-03-08T22:00:00+08:00</published><updated>2025-03-08T22:00:00+08:00</updated><id>https://didarul.codes/blog/devops/2025/03/08/deploy-flutter-web-app-to-github-pages</id><content type="html" xml:base="https://didarul.codes/blog/devops/2025/03/08/deploy-flutter-web-app-to-github-pages.html"><![CDATA[<h2 id="why-github-pages-for-flutter-web">Why GitHub Pages for Flutter Web?</h2>

<blockquote>
  <p>Did you know you don’t need to setup a web hosting service to deploy your
Flutter web app?</p>
</blockquote>

<p>That’s right! When I wanted to create a quick preview for one of my Flutter apps to share with others, I wasn’t ready to
go through the hassle of setting up a new site with a traditional web hosting service.</p>

<p>After exploring my options, I found several ways to host a Flutter Web app:</p>

<ol>
  <li><strong>GitHub Pages</strong></li>
  <li><strong>Firebase Hosting</strong></li>
  <li><strong>Traditional web hosting</strong></li>
</ol>

<p>For a quick prototype or portfolio piece, GitHub Pages seemed ideal.
In this guide, I’ll walk you through the complete process of deploying your Flutter web app to GitHub Pages.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>Before we start, make sure you have:</p>

<ul>
  <li>A Flutter project with web support enabled</li>
  <li>Your GitHub Repository is public</li>
</ul>

<h2 id="setup">Setup</h2>

<h3 id="add-web-support-to-your-flutter-project">Add Web Support to Your Flutter Project</h3>

<p>If your project doesn’t already support web, you can add web support by running:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">flutter create <span class="nt">--platforms</span><span class="o">=</span>web .</code></pre></figure>

<p>You can verify web support is enabled with:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">flutter devices</code></pre></figure>

<p>You should see “Chrome” web browser listed in the available devices.</p>

<h3 id="build-the-project-for-web">Build the Project for Web</h3>

<p>We need to build the web app using this command:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">flutter build web <span class="nt">--release</span> <span class="nt">--base-href</span><span class="o">=</span>/YOUR_REPOSITORY_NAME_HERE/</code></pre></figure>

<p>Replace <code class="language-plaintext highlighter-rouge">YOUR_REPOSITORY_NAME_HERE</code> with your actual repository name.</p>

<p>For instance, if your repository name is <code class="language-plaintext highlighter-rouge">flutter-playground</code>, the command will be:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">flutter build web <span class="nt">--release</span> <span class="nt">--base-href</span><span class="o">=</span>/flutter-playground/</code></pre></figure>

<h3 id="understanding-the-base-href-parameter">Understanding the base-href Parameter</h3>

<p>The <code class="language-plaintext highlighter-rouge">--base-href</code> flag is crucial because:</p>

<ul>
  <li>GitHub Pages serves your site from a subdirectory (yourusername.github.io/repository-name)</li>
  <li>Without this parameter, your app would try to load assets from the root domain</li>
  <li>This flag ensures all relative URLs in your app are correctly prefixed with your repository path</li>
</ul>

<p>After running the build command, your compiled web app will be generated in the <code class="language-plaintext highlighter-rouge">build/web/</code> directory.</p>

<h3 id="prepare-for-github-pages">Prepare for GitHub Pages</h3>

<p>GitHub Pages can serve content from either:</p>

<ul>
  <li>The root directory</li>
  <li>The <code class="language-plaintext highlighter-rouge">/docs</code> folder in your main branch</li>
  <li>A dedicated branch (typically called <code class="language-plaintext highlighter-rouge">gh-pages</code>)</li>
</ul>

<p>For this guide, we’ll use the <code class="language-plaintext highlighter-rouge">/docs</code> approach as it’s straightforward and keeps everything in one branch.</p>

<p>Create a <code class="language-plaintext highlighter-rouge">docs/</code> directory in the root of your project and copy all contents from <code class="language-plaintext highlighter-rouge">build/web/</code>.
This can be done manually or using below command:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">mkdir</span> <span class="nt">-p</span> docs
<span class="nb">cp</span> <span class="nt">-R</span> build/web/<span class="k">*</span> docs/</code></pre></figure>

<h3 id="commit-and-push-to-github">Commit and Push to GitHub</h3>

<p>Add, commit and push the changes to your GitHub repository:
This can be done using your favorite git tool or using below command:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">git add docs/
git commit <span class="nt">-m</span> <span class="s2">"Add web build for GitHub Pages"</span>
git push origin main</code></pre></figure>

<h3 id="configure-github-pages-settings">Configure GitHub Pages Settings</h3>

<ol>
  <li>Go to your GitHub repository in a web browser</li>
  <li>Click on “Settings” tab</li>
  <li>Scroll down to the “Pages” section</li>
  <li>For “Branch”, select the <code class="language-plaintext highlighter-rouge">main</code> or <code class="language-plaintext highlighter-rouge">master</code> branch and <code class="language-plaintext highlighter-rouge">/docs</code> folder</li>
  <li>Click “Save”</li>
</ol>

<p><img src="/assets/img/github-pages-settings.png" alt="GitHub Pages Settings" /></p>

<p>GitHub will provide you with the URL where your site is published (typically
<code class="language-plaintext highlighter-rouge">https://username.github.io/repository-name</code>).</p>

<h3 id="access-your-deployed-app">Access Your Deployed App</h3>

<p>After a few minutes, your Flutter web app will be available at:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">https://username.github.io/repository-name</code></pre></figure>

<h2 id="automating-the-deployment-process">Automating the Deployment Process</h2>

<h3 id="using-a-script-from-local-machine">Using a Script From Local Machine</h3>

<p>Manually copying files and commiting can become tedious. We can
automate this process with a shell script.</p>

<p>Create a file named <code class="language-plaintext highlighter-rouge">deployment.sh</code> in your project root:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/bash</span>

<span class="nb">set</span> <span class="nt">-e</span>

<span class="nv">BUILD_DIRECTORY</span><span class="o">=</span><span class="s2">"build/web"</span>
<span class="nv">DEPLOY_DIRECTORY</span><span class="o">=</span><span class="s2">"docs"</span>
<span class="nv">COMMIT_MESSAGE</span><span class="o">=</span><span class="s2">"Deploy On Pages"</span>
<span class="nv">BASE_HREF</span><span class="o">=</span><span class="s2">"YOUR_REPO_NAME"</span>

build_flutter_web<span class="o">()</span> <span class="o">{</span>
<span class="nb">echo</span> <span class="s2">"Building Web application..."</span>

    <span class="nb">rm</span> <span class="nt">-rf</span> <span class="s2">"</span><span class="nv">$BUILD_DIRECTORY</span><span class="s2">"</span>
    
    <span class="k">if</span> <span class="o">!</span> flutter build web <span class="se">\</span>
        <span class="nt">--base-href</span><span class="o">=</span><span class="nv">$BASE_HREF</span> <span class="se">\</span>
        <span class="nt">--release</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">echo</span> <span class="s2">"Flutter build failed"</span>
        <span class="nb">exit </span>1
    <span class="k">fi</span>

<span class="o">}</span>

deploy_to_pages<span class="o">()</span> <span class="o">{</span>
<span class="nb">echo</span> <span class="s2">"Deploying to GitHub Pages..."</span>

    <span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$DEPLOY_DIRECTORY</span><span class="s2">"</span>
    
    <span class="nb">rm</span> <span class="nt">-rf</span> <span class="s2">"</span><span class="k">${</span><span class="nv">DEPLOY_DIRECTORY</span>:?<span class="k">}</span><span class="s2">/"</span><span class="k">*</span>
    
    <span class="k">if</span> <span class="o">!</span> <span class="nb">cp</span> <span class="nt">-r</span> <span class="s2">"</span><span class="nv">$BUILD_DIRECTORY</span><span class="s2">"</span>/<span class="k">*</span> <span class="s2">"</span><span class="nv">$DEPLOY_DIRECTORY</span><span class="s2">"</span><span class="p">;</span> <span class="k">then
        </span><span class="nb">echo</span> <span class="s2">"Failed to copy build files"</span>
        <span class="nb">exit </span>1
    <span class="k">fi

    if </span>git add .<span class="p">;</span> <span class="k">then
        </span>git commit <span class="nt">-m</span> <span class="s2">"</span><span class="nv">$COMMIT_MESSAGE</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> git push
    <span class="k">else
        </span><span class="nb">echo</span> <span class="s2">"Git operations failed"</span>
        <span class="nb">exit </span>1
    <span class="k">fi
    
    </span><span class="nb">echo</span> <span class="s2">"Deployment completed successfully!"</span>
    <span class="nb">echo</span> <span class="s2">"Please ensure GitHub Pages is configured to serve from the </span><span class="nv">$DEPLOY_DIRECTORY</span><span class="s2"> folder"</span>

<span class="o">}</span>

build_flutter_web
deploy_to_pages</code></pre></figure>

<p>Make the script executable:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">chmod</span> +x deployment.sh</code></pre></figure>

<p>Now, whenever you want to update your deployed app, simply run:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">./deploy.sh</code></pre></figure>

<h3 id="setting-up-github-actions-for-cicd">Setting Up GitHub Actions for CI/CD</h3>

<p>For even more automation, you can set up GitHub Actions to automatically build and deploy your Flutter web app whenever
you push changes to your repository.</p>

<p>Create a file at <code class="language-plaintext highlighter-rouge">.github/workflows/deploy.yml</code>:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to GitHub Pages</span>

<span class="na">on</span><span class="pi">:</span>
<span class="na">workflow_dispatch</span><span class="pi">:</span>  <span class="c1"># Allows manual triggering from GitHub UI</span>

<span class="na">jobs</span><span class="pi">:</span>
<span class="na">build-and-deploy</span><span class="pi">:</span>
<span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="s">steps</span><span class="err">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout repository</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">fetch-depth</span><span class="pi">:</span> <span class="m">0</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Flutter</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">subosito/flutter-action@v2</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">flutter-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.29.1'</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">flutter pub get</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Configure Git</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">git config --global user.name "GitHub Actions"</span>
          <span class="s">git config --global user.email "actions@github.com"</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Run deployment script</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">chmod +x ./deployment.sh</span>
          <span class="s">./deployment.sh</span></code></pre></figure>

<p>With this workflow, GitHub will automatically build and deploy your app when you push to the main branch.</p>

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

<p>I have this setup in my <a href="https://github.com/islamdidarmd/flutter-playground">Playground Repository</a>.
You can have a look for inspiration. Also, I created a <a href="https://gist.github.com/islamdidarmd/015ff82f26a319471cfc939c02b5bbec">gist</a>
for the deployment script</p>

<blockquote>
  <p><strong>P.S.</strong> If you found this helpful, don’t forget to check out my other Flutter articles and give a star to
my <a href="https://github.com/islamdidarmd/flutter-playground">Flutter Playground Repository</a> for more practical examples.</p>
</blockquote>]]></content><author><name>Md Didarul Islam</name></author><category term="DevOps" /><category term="flutter" /><category term="web" /><category term="github pages" /><category term="deployment" /><category term="CI/CD" /><category term="hosting" /><summary type="html"><![CDATA[Learn how to build and deploy your Flutter web application to GitHub Pages with step-by-step instructions, automation scripts, and best practices]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Getting Started with Flutter: A Quick Guide</title><link href="https://didarul.codes/blog/flutter%20basics/2025/03/04/getting-started-with-flutter.html" rel="alternate" type="text/html" title="Getting Started with Flutter: A Quick Guide" /><published>2025-03-04T14:00:00+08:00</published><updated>2025-03-04T14:00:00+08:00</updated><id>https://didarul.codes/blog/flutter%20basics/2025/03/04/getting-started-with-flutter</id><content type="html" xml:base="https://didarul.codes/blog/flutter%20basics/2025/03/04/getting-started-with-flutter.html"><![CDATA[<p>I have 8+ years of experience building mobile apps. Before Flutter, I worked as a Native Android developer. Now I use Flutter to build apps for many platforms at once. In this guide, I’ll show you how to get started with Flutter.</p>

<h2 id="what-is-flutter">What is Flutter?</h2>

<p>Flutter is a free tool from Google that lets you build apps using one set of code. You can make apps for phones, web, and computers all at once. I started using Flutter in 2021 and found it much better than other tools I had tried before.</p>

<p>With Flutter, you write code once and deploy it to multiple platforms:</p>

<ul>
  <li>iOS</li>
  <li>Android</li>
  <li>Web</li>
  <li>Desktop (Windows, macOS, Linux)</li>
</ul>

<h2 id="why-i-choose-flutter">Why I Choose Flutter</h2>

<p>After writing native Android code for 4 years, here’s why I switched to Flutter:</p>

<ol>
  <li><strong>One Codebase</strong>: Write once, use everywhere - saves 30-50% development time</li>
  <li><strong>Fast Updates</strong>: See changes right away while coding - no more waiting</li>
  <li><strong>Nice Looking UI</strong>: Make good-looking apps that feel natural on any device</li>
  <li><strong>Fast Apps</strong>: Flutter makes apps that run as fast as native apps</li>
  <li><strong>Helpful Community</strong>: Get help from other developers and use 30,000+ ready-made packages</li>
  <li><strong>Many Ready Widgets</strong>: Use pre-made buttons, screens, and other parts</li>
  <li><strong>Custom Designs</strong>: Create your own unique look for your apps</li>
</ol>

<h2 id="setting-up-your-development-environment">Setting Up Your Development Environment</h2>

<p>There are two ways to install Flutter: using FVM (recommended) or direct installation. I’ll show you both methods.</p>

<h3 id="1-install-flutter-using-fvm-recommended">1. Install Flutter using FVM (Recommended)</h3>

<p>FVM (Flutter Version Management) is a tool that helps you:</p>

<ul>
  <li>Manage multiple Flutter versions</li>
  <li>Switch between versions easily</li>
  <li>Keep consistent Flutter versions across your team</li>
  <li>Install Flutter versions per project</li>
</ul>

<p>Here’s how to set up FVM:</p>

<ol>
  <li>Install FVM using Homebrew (Mac):</li>
</ol>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">brew tap leoafarias/fvm
brew <span class="nb">install </span>fvm</code></pre></figure>

<p>Or using pub (all platforms):</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">dart pub global activate fvm</code></pre></figure>

<ol>
  <li>Install Flutter using FVM:</li>
</ol>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># Install latest a specific version</span>

fvm <span class="nb">install </span>3.29.0</code></pre></figure>

<ol>
  <li>Set up an existing project with FVM:</li>
</ol>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># Go to project directory</span>
<span class="nb">cd </span>my_flutter_app

<span class="c"># Use a specific Flutter version</span>
fvm use 3.29.0

<span class="c"># Verify configuration for this project</span>

fvm flutter <span class="nt">--version</span></code></pre></figure>

<p>Fvm will create <code class="language-plaintext highlighter-rouge">.fvmrc</code> configuration file. Do include this file in
version control.
For more detailed uses, have a look at <a href="https://fvm.app/documentation/guides/basic-commands">FVM Docs</a></p>

<h3 id="alternative-direct-flutter-installation">Alternative: Direct Flutter Installation</h3>

<p>If you prefer installing Flutter directly:</p>

<ol>
  <li>Go to <a href="https://docs.flutter.dev/get-started/install">Flutter’s download page</a></li>
  <li>Download Flutter for your OS</li>
  <li>Extract to a folder with a simple path</li>
</ol>

<p>For Mac users:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># Make a folder for Flutter</span>

<span class="nb">mkdir</span> ~/development
<span class="nb">cd</span> ~/development

<span class="c"># Unzip Flutter</span>

unzip ~/Downloads/flutter_macos_arm64_3.29.0-stable.zip</code></pre></figure>

<h3 id="2-add-flutter-to-your-path">2. Add Flutter to Your Path</h3>

<p>If using FVM, it handles the PATH automatically.</p>

<p>For direct installation, add Flutter to your path:</p>

<p>For <strong>Mac/Linux</strong>, add to <code class="language-plaintext highlighter-rouge">~/.zshrc</code> or <code class="language-plaintext highlighter-rouge">~/.bashrc</code>:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PATH</span><span class="s2">:</span><span class="nv">$HOME</span><span class="s2">/development/flutter/bin"</span></code></pre></figure>

<p>For <strong>Windows</strong>, add Flutter’s bin folder to your Path in System Properties.</p>

<h3 id="3-set-up-your-ide">3. Set Up Your IDE</h3>

<p>Choose an editor for coding Flutter:</p>

<ul>
  <li><strong>Android Studio</strong>
    <ul>
      <li>Go to Plugins → Browse</li>
      <li>Install “Flutter” plugin</li>
      <li>Install “Dart” plugin</li>
      <li>Restart</li>
    </ul>
  </li>
  <li><strong>VS Code</strong>
    <ul>
      <li>Install “Flutter” extension</li>
      <li>Install “Dart” extension</li>
      <li>Reload VS Code</li>
    </ul>
  </li>
</ul>

<h3 id="4-check-your-setup">4. Check Your Setup</h3>

<p>Run this command to see if everything works:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">flutter doctor</code></pre></figure>

<p>If you see X marks, here’s what to do:</p>

<ul>
  <li>❌ <strong>Android toolchain</strong>: Install Android Studio</li>
  <li>❌ <strong>Xcode</strong> (Mac only): Install from App Store</li>
  <li>❌ <strong>Chrome</strong>: Install Chrome browser</li>
  <li>❌ <strong>VS Code/Android Studio</strong>: Install missing plugins</li>
</ul>

<p>Just follow what flutter doctor tells you to fix these issues.</p>

<h2 id="creating-your-first-flutter-app">Creating Your First Flutter App</h2>

<p>Make your first app with these simple commands:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">flutter create my_first_app  <span class="c"># Makes a new app</span>
<span class="nb">cd </span>my_first_app             <span class="c"># Go to app folder</span>
flutter run                 <span class="c"># Start the app</span></code></pre></figure>

<p>This is what the main code looks like:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kn">import</span> <span class="s">'package:flutter/material.dart'</span><span class="o">;</span>

<span class="kt">void</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">runApp</span><span class="p">(</span><span class="kd">const</span> <span class="n">MyApp</span><span class="p">());</span>  <span class="c1">// App starts here</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">MyApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="n">MyApp</span><span class="p">({</span><span class="k">super</span><span class="o">.</span><span class="na">key</span><span class="p">});</span>

  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">MaterialApp</span><span class="p">(</span>
      <span class="nl">title:</span> <span class="s">'My First Flutter App'</span><span class="p">,</span>    <span class="c1">// App name</span>
      <span class="nl">theme:</span> <span class="n">ThemeData</span><span class="p">(</span>
        <span class="nl">primarySwatch:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">blue</span><span class="p">,</span>     <span class="c1">// Main color</span>
        <span class="nl">useMaterial3:</span> <span class="kc">true</span><span class="p">,</span>             <span class="c1">// Modern look</span>
      <span class="p">),</span>
      <span class="nl">home:</span> <span class="kd">const</span> <span class="n">MyHomePage</span><span class="p">(</span><span class="nl">title:</span> <span class="s">'My First Flutter App'</span><span class="p">),</span>  <span class="c1">// First screen</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h2 id="hot-reload-and-hot-restart">Hot Reload and Hot Restart</h2>

<p>Flutter has two great features that make coding faster:</p>

<p><strong>Hot Reload</strong>:</p>

<ul>
  <li>Updates your app in under 1 second</li>
  <li>Keeps your app’s current state</li>
  <li>Great for small UI changes</li>
  <li>Press <code class="language-plaintext highlighter-rouge">r</code> in terminal to use it</li>
</ul>

<p><strong>Hot Restart</strong>:</p>

<ul>
  <li>Starts your app fresh (takes 3-5 seconds)</li>
  <li>Resets everything</li>
  <li>Use when Hot Reload doesn’t work</li>
  <li>Press <code class="language-plaintext highlighter-rouge">R</code> in terminal to use it</li>
</ul>

<p>I use Hot Reload at least 20 times every hour - it saves so much time!</p>

<h2 id="essential-flutter-concepts">Essential Flutter Concepts</h2>

<h3 id="1-widgets">1. Widgets</h3>

<p>Everything in Flutter is a widget. The main types are:</p>

<ul>
  <li><strong>StatelessWidget</strong>: For parts of the app that don’t change</li>
  <li><strong>StatefulWidget</strong>: For parts that need to change</li>
</ul>

<p>Here’s a simple example:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">WelcomeMessage</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">message</span><span class="p">;</span>  <span class="c1">// Input data</span>
  
  <span class="kd">const</span> <span class="n">WelcomeMessage</span><span class="p">({</span><span class="k">super</span><span class="o">.</span><span class="na">key</span><span class="p">,</span> <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">message</span><span class="p">});</span>
  
  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// How it looks</span>
    <span class="k">return</span> <span class="n">Text</span><span class="p">(</span>
      <span class="n">message</span><span class="p">,</span>
      <span class="nl">style:</span> <span class="n">Theme</span><span class="o">.</span><span class="na">of</span><span class="p">(</span><span class="n">context</span><span class="p">)</span><span class="o">.</span><span class="na">textTheme</span><span class="o">.</span><span class="na">headlineMedium</span><span class="p">,</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// How to use it:</span>
<span class="n">WelcomeMessage</span><span class="p">(</span><span class="nl">message:</span> <span class="s">"Welcome to Flutter!"</span><span class="p">)</span></code></pre></figure>

<h3 id="2-state-management">2. State Management</h3>

<p>I pick different ways to manage state based on app size:</p>

<ul>
  <li><strong>setState</strong>: For small apps</li>
</ul>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart">  <span class="c1">// Simple counter</span>
  <span class="kt">void</span> <span class="n">_incrementCounter</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">setState</span><span class="p">(()</span> <span class="p">{</span>
      <span class="n">_counter</span><span class="o">++</span><span class="p">;</span>  <span class="c1">// This updates the screen</span>
    <span class="p">});</span>
  <span class="p">}</span>
  </code></pre></figure>

<ul>
  <li><strong>Provider</strong>: For medium apps</li>
  <li><strong>Bloc/Cubit</strong>: For large apps</li>
</ul>

<h3 id="3-basic-layout-widgets">3. Basic Layout Widgets</h3>

<p>These are the most important layout widgets I use all the time:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Container</code>: For styling (padding, margin, color, etc.)</li>
  <li><code class="language-plaintext highlighter-rouge">Row</code>: For side-by-side items</li>
  <li><code class="language-plaintext highlighter-rouge">Column</code>: For top-to-bottom items</li>
  <li><code class="language-plaintext highlighter-rouge">Stack</code>: For putting things on top of each other</li>
  <li><code class="language-plaintext highlighter-rouge">ListView</code>: For scrollable lists</li>
</ul>

<h2 id="debugging-flutter-apps">Debugging Flutter Apps</h2>

<p>Here are the ways to fix problems in Flutter apps:</p>

<h3 id="1-print-statements">1. Print Statements</h3>

<p>The simplest way to see what’s happening:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">print</span><span class="p">(</span><span class="s">'Got user data: </span><span class="si">$userData</span><span class="s">'</span><span class="p">);</span>  <span class="c1">// Show data in console</span>

<span class="c1">// Or with a logging package:</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="p">(</span><span class="s">'Got user data'</span><span class="p">,</span> <span class="n">userData</span><span class="p">);</span></code></pre></figure>

<h3 id="2-flutter-devtools">2. Flutter DevTools</h3>

<p>Flutter’s special debugging tool that lets you:</p>

<ul>
  <li>See how widgets are arranged</li>
  <li>Check app performance</li>
  <li>Watch network requests</li>
  <li>Fix layout problems</li>
</ul>

<p>Start it by running:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">flutter run</code></pre></figure>

<p>Then press ‘d’ in the terminal.</p>

<h3 id="3-breakpoints">3. Breakpoints</h3>

<p>Stop your code at certain points to check what’s happening:</p>

<ol>
  <li>Click next to a line of code in your editor</li>
  <li>Run in debug mode</li>
  <li>When the app stops at that point, check all variables</li>
  <li>Step through code line by line</li>
</ol>

<h2 id="best-practices">Best Practices</h2>

<h3 id="1-widget-tree-structure">1. Widget Tree Structure</h3>

<ul>
  <li>Break big screens into smaller parts (under 100 lines)</li>
  <li>Reuse widgets when you see the same UI more than once</li>
</ul>

<h3 id="2-performance-tips">2. Performance Tips</h3>

<ul>
  <li>Use <code class="language-plaintext highlighter-rouge">ListView.builder</code> for long lists</li>
  <li>Make widgets <code class="language-plaintext highlighter-rouge">const</code> when they don’t change</li>
  <li>Put <code class="language-plaintext highlighter-rouge">RepaintBoundary</code> around complex animations</li>
</ul>

<h3 id="3-code-organization">3. Code Organization</h3>

<ul>
  <li>Keep UI code separate from logic code</li>
  <li>Organize by feature, not by file type</li>
  <li>Name things clearly (camelCase for variables, PascalCase for classes)</li>
</ul>

<h2 id="common-mistakes-to-avoid">Common Mistakes To Avoid</h2>

<ol>
  <li><strong>Too Many Nested Widgets</strong>
    <ul>
      <li><strong>Bad</strong>: Putting 10+ widgets inside each other</li>
      <li><strong>Good</strong>: Break into separate widget classes</li>
      <li><strong>Example</strong>: Split a form into <code class="language-plaintext highlighter-rouge">FormHeader</code>, <code class="language-plaintext highlighter-rouge">FormFields</code>, and <code class="language-plaintext highlighter-rouge">FormButtons</code></li>
    </ul>
  </li>
  <li><strong>Not Using Keys</strong>
    <ul>
      <li><strong>Bad</strong>: Lists that rearrange items without keys</li>
      <li><strong>Good</strong>: Add keys to list items</li>
      <li><strong>Example</strong>: <code class="language-plaintext highlighter-rouge">ListView.builder(itemBuilder: (context, index) =&gt; MyWidget(key: ValueKey(index)))</code></li>
    </ul>
  </li>
  <li><strong>Slow Code in Main Thread</strong>
    <ul>
      <li><strong>Bad</strong>: Heavy work in build methods</li>
      <li><strong>Good</strong>: Do heavy work outside the UI</li>
      <li><strong>Example</strong>: Use <code class="language-plaintext highlighter-rouge">compute(parseJson, jsonString)</code> for parsing</li>
    </ul>
  </li>
  <li><strong>Rebuilding Too Much</strong>
    <ul>
      <li><strong>Bad</strong>: Uncessesary rebuild of the widget tree</li>
      <li><strong>Good</strong>: Only update the specific widgets that change</li>
      <li><strong>Example</strong>: Use <code class="language-plaintext highlighter-rouge">Provider.of(context, listen: false)</code> to avoid extra rebuilds</li>
    </ul>
  </li>
</ol>

<h2 id="resources-for-learning-more">Resources For Learning More</h2>

<ol>
  <li><a href="https://flutter.dev/docs">Flutter Docs</a></li>
  <li><a href="https://www.youtube.com/c/flutterdev">Flutter YouTube</a></li>
  <li><a href="https://discord.gg/flutter">Flutter Discord</a></li>
  <li><a href="https://pub.dev">Flutter Packages</a></li>
</ol>

<h2 id="what-to-try-next">What to Try Next</h2>

<p>Try building these simple apps:</p>

<ol>
  <li><strong>Todo App</strong>: Make a list of tasks (<a href="https://flutter.dev/docs/cookbook">Tutorial</a>)
    <ul>
      <li>Learn basic state management</li>
      <li>Practice forms</li>
      <li>Save data locally</li>
    </ul>
  </li>
  <li><strong>Weather App</strong>: Show weather forecasts
    <ul>
      <li>Learn to use APIs</li>
      <li>Handle loading states</li>
      <li>Make multiple screens</li>
    </ul>
  </li>
  <li><strong>Profile Page</strong>: Create a user profile
    <ul>
      <li>Build complex layouts</li>
      <li>Add animations</li>
      <li>Load and cache images</li>
    </ul>
  </li>
</ol>

<blockquote>
  <p>P.S. If you found this helpful, don’t forget to check out my other Flutter articles!</p>
</blockquote>]]></content><author><name>Md Didarul Islam</name></author><category term="Flutter Basics" /><category term="flutter" /><category term="beginner guide" /><category term="development setup" /><category term="cross-platform" /><category term="mobile app development" /><summary type="html"><![CDATA[A comprehensive beginner's guide to Flutter development, covering setup, essential concepts, debugging techniques, and best practices from an experienced developer.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Flutter State Management: Understanding the Basics</title><link href="https://didarul.codes/blog/state%20management/flutter%20basics/2025/02/01/flutter-state-management-basics.html" rel="alternate" type="text/html" title="Flutter State Management: Understanding the Basics" /><published>2025-02-01T14:30:00+08:00</published><updated>2025-02-01T14:30:00+08:00</updated><id>https://didarul.codes/blog/state%20management/flutter%20basics/2025/02/01/flutter-state-management-basics</id><content type="html" xml:base="https://didarul.codes/blog/state%20management/flutter%20basics/2025/02/01/flutter-state-management-basics.html"><![CDATA[<p>State management is a crucial concept in Flutter development. In this post, we’ll explore the basics of state management and look at different approaches to handle state in your Flutter applications.</p>

<h2 id="what-is-state">What is State?</h2>

<p>In Flutter, “state” refers to any data that can change during the lifetime of your application. This could be:</p>
<ul>
  <li>User data</li>
  <li>UI state (loading indicators, form inputs)</li>
  <li>Application configuration</li>
  <li>Navigation state</li>
</ul>

<h2 id="types-of-state">Types of State</h2>

<h3 id="1-ephemeral-local-state">1. Ephemeral (Local) State</h3>

<p>Ephemeral state is local to a single widget. It’s simple to manage using <code class="language-plaintext highlighter-rouge">setState()</code>. Here’s an example:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">CounterWidget</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="p">{</span>
  <span class="nd">@override</span>
  <span class="n">_CounterWidgetState</span> <span class="n">createState</span><span class="p">()</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">_CounterWidgetState</span><span class="p">();</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nc">_CounterWidgetState</span> <span class="kd">extends</span> <span class="n">State</span><span class="p">&lt;</span><span class="n">CounterWidget</span><span class="p">&gt;</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">_counter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="kt">void</span> <span class="n">_incrementCounter</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">setState</span><span class="p">(()</span> <span class="p">{</span>
      <span class="n">_counter</span><span class="o">++</span><span class="p">;</span>
    <span class="p">});</span>
  <span class="p">}</span>

  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
      <span class="nl">children:</span> <span class="p">[</span>
        <span class="n">Text</span><span class="p">(</span><span class="s">'Count: </span><span class="si">$_counter</span><span class="s">'</span><span class="p">),</span>
        <span class="n">ElevatedButton</span><span class="p">(</span>
          <span class="nl">onPressed:</span> <span class="n">_incrementCounter</span><span class="p">,</span>
          <span class="nl">child:</span> <span class="n">Text</span><span class="p">(</span><span class="s">'Increment'</span><span class="p">),</span>
        <span class="p">),</span>
      <span class="p">],</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h3 id="2-app-state-global-state">2. App State (Global State)</h3>

<p>App state needs to be accessed by multiple widgets and screens. Let’s look at a simple example using Provider:</p>

<p>First, create your state model:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">CartModel</span> <span class="kd">extends</span> <span class="n">ChangeNotifier</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">List</span><span class="p">&lt;</span><span class="kt">String</span><span class="p">&gt;</span> <span class="n">_items</span> <span class="o">=</span> <span class="p">[];</span>

  <span class="kt">List</span><span class="p">&lt;</span><span class="kt">String</span><span class="p">&gt;</span> <span class="kd">get</span> <span class="n">items</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">_items</span><span class="p">;</span>
  <span class="kt">int</span> <span class="kd">get</span> <span class="n">itemCount</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">_items</span><span class="o">.</span><span class="na">length</span><span class="p">;</span>

  <span class="kt">void</span> <span class="n">addItem</span><span class="p">(</span><span class="kt">String</span> <span class="n">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">_items</span><span class="o">.</span><span class="na">add</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
    <span class="n">notifyListeners</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="kt">void</span> <span class="n">removeItem</span><span class="p">(</span><span class="kt">String</span> <span class="n">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">_items</span><span class="o">.</span><span class="na">remove</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
    <span class="n">notifyListeners</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Then, set up the provider in your app:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kt">void</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">runApp</span><span class="p">(</span>
    <span class="n">ChangeNotifierProvider</span><span class="p">(</span>
      <span class="nl">create:</span> <span class="p">(</span><span class="n">context</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">CartModel</span><span class="p">(),</span>
      <span class="nl">child:</span> <span class="n">MyApp</span><span class="p">(),</span>
    <span class="p">),</span>
  <span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>Use the state in your widgets:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">CartWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">Consumer</span><span class="p">&lt;</span><span class="n">CartModel</span><span class="p">&gt;(</span>
      <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">cart</span><span class="p">,</span> <span class="n">child</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
          <span class="nl">children:</span> <span class="p">[</span>
            <span class="n">Text</span><span class="p">(</span><span class="s">'Cart Items: </span><span class="si">${cart.itemCount}</span><span class="s">'</span><span class="p">),</span>
            <span class="n">ListView</span><span class="o">.</span><span class="na">builder</span><span class="p">(</span>
              <span class="nl">itemCount:</span> <span class="n">cart</span><span class="o">.</span><span class="na">items</span><span class="o">.</span><span class="na">length</span><span class="p">,</span>
              <span class="nl">itemBuilder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">return</span> <span class="n">ListTile</span><span class="p">(</span>
                  <span class="nl">title:</span> <span class="n">Text</span><span class="p">(</span><span class="n">cart</span><span class="o">.</span><span class="na">items</span><span class="p">[</span><span class="n">index</span><span class="p">]),</span>
                  <span class="nl">trailing:</span> <span class="n">IconButton</span><span class="p">(</span>
                    <span class="nl">icon:</span> <span class="n">Icon</span><span class="p">(</span><span class="n">Icons</span><span class="o">.</span><span class="na">remove_circle</span><span class="p">),</span>
                    <span class="nl">onPressed:</span> <span class="p">()</span> <span class="p">{</span>
                      <span class="n">cart</span><span class="o">.</span><span class="na">removeItem</span><span class="p">(</span><span class="n">cart</span><span class="o">.</span><span class="na">items</span><span class="p">[</span><span class="n">index</span><span class="p">]);</span>
                    <span class="p">},</span>
                  <span class="p">),</span>
                <span class="p">);</span>
              <span class="p">},</span>
            <span class="p">),</span>
          <span class="p">],</span>
        <span class="p">);</span>
      <span class="p">},</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h2 id="best-practices-for-state-management">Best Practices for State Management</h2>

<ol>
  <li><strong>Choose the Right Approach</strong>
    <ul>
      <li>Use <code class="language-plaintext highlighter-rouge">setState()</code> for simple, local state</li>
      <li>Use state management solutions (Provider, Riverpod, Bloc) for complex app state</li>
    </ul>
  </li>
  <li><strong>Keep State Simple</strong></li>
</ol>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="c1">// Good: Simple and focused state</span>
<span class="kd">class</span> <span class="nc">UserState</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">name</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">email</span><span class="p">;</span>
  
  <span class="n">UserState</span><span class="p">({</span><span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">name</span><span class="p">,</span> <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">email</span><span class="p">});</span>
<span class="p">}</span>

<span class="c1">// Bad: Too much unrelated state</span>
<span class="kd">class</span> <span class="nc">AppState</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">userName</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">List</span><span class="p">&lt;</span><span class="n">Product</span><span class="p">&gt;</span> <span class="n">products</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">bool</span> <span class="n">isDarkMode</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">int</span> <span class="n">cartItemCount</span><span class="p">;</span>
  <span class="c1">// ... too many different concerns</span>
<span class="p">}</span></code></pre></figure>

<ol>
  <li><strong>Immutable State</strong></li>
</ol>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="c1">// Good: Immutable state</span>
<span class="nd">@immutable</span>
<span class="kd">class</span> <span class="nc">TodoItem</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">id</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="p">;</span>
  <span class="kd">final</span> <span class="kt">bool</span> <span class="n">isCompleted</span><span class="p">;</span>

  <span class="kd">const</span> <span class="n">TodoItem</span><span class="p">({</span>
    <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">id</span><span class="p">,</span>
    <span class="kd">required</span> <span class="k">this</span><span class="o">.</span><span class="na">title</span><span class="p">,</span>
    <span class="k">this</span><span class="o">.</span><span class="na">isCompleted</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
  <span class="p">});</span>

  <span class="n">TodoItem</span> <span class="n">copyWith</span><span class="p">({</span>
    <span class="kt">String</span><span class="o">?</span> <span class="n">id</span><span class="p">,</span>
    <span class="kt">String</span><span class="o">?</span> <span class="n">title</span><span class="p">,</span>
    <span class="kt">bool</span><span class="o">?</span> <span class="n">isCompleted</span><span class="p">,</span>
  <span class="p">})</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">TodoItem</span><span class="p">(</span>
      <span class="nl">id:</span> <span class="n">id</span> <span class="o">??</span> <span class="k">this</span><span class="o">.</span><span class="na">id</span><span class="p">,</span>
      <span class="nl">title:</span> <span class="n">title</span> <span class="o">??</span> <span class="k">this</span><span class="o">.</span><span class="na">title</span><span class="p">,</span>
      <span class="nl">isCompleted:</span> <span class="n">isCompleted</span> <span class="o">??</span> <span class="k">this</span><span class="o">.</span><span class="na">isCompleted</span><span class="p">,</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<ol>
  <li><strong>State Location</strong></li>
</ol>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="c1">// Good: State is close to where it's used</span>
<span class="kd">class</span> <span class="nc">ProductListScreen</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">ChangeNotifierProvider</span><span class="p">(</span>
      <span class="nl">create:</span> <span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">ProductListModel</span><span class="p">(),</span>
      <span class="nl">child:</span> <span class="n">ProductList</span><span class="p">(),</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Bad: State is too high in the widget tree</span>
<span class="kt">void</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">runApp</span><span class="p">(</span>
    <span class="n">MultiProvider</span><span class="p">(</span>
      <span class="nl">providers:</span> <span class="p">[</span>
        <span class="n">ChangeNotifierProvider</span><span class="p">(</span><span class="nl">create:</span> <span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">ProductListModel</span><span class="p">()),</span>
        <span class="n">ChangeNotifierProvider</span><span class="p">(</span><span class="nl">create:</span> <span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">CartModel</span><span class="p">()),</span>
        <span class="n">ChangeNotifierProvider</span><span class="p">(</span><span class="nl">create:</span> <span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">UserModel</span><span class="p">()),</span>
        <span class="c1">// ... too many providers at app root</span>
      <span class="p">],</span>
      <span class="nl">child:</span> <span class="n">MyApp</span><span class="p">(),</span>
    <span class="p">),</span>
  <span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<h2 id="debugging-state">Debugging State</h2>

<p>Flutter provides great tools for debugging state:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">DebuggableCartModel</span> <span class="kd">extends</span> <span class="n">ChangeNotifier</span> <span class="p">{</span>
  <span class="kd">final</span> <span class="kt">List</span><span class="p">&lt;</span><span class="kt">String</span><span class="p">&gt;</span> <span class="n">_items</span> <span class="o">=</span> <span class="p">[];</span>

  <span class="nd">@override</span>
  <span class="kt">void</span> <span class="n">notifyListeners</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">print</span><span class="p">(</span><span class="s">'Cart state changed: </span><span class="si">$_items</span><span class="s">'</span><span class="p">);</span> <span class="c1">// Debug print</span>
    <span class="k">super</span><span class="o">.</span><span class="na">notifyListeners</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="kt">void</span> <span class="n">addItem</span><span class="p">(</span><span class="kt">String</span> <span class="n">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">assert</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="na">isNotEmpty</span><span class="p">,</span> <span class="s">'Item cannot be empty'</span><span class="p">);</span>
    <span class="n">_items</span><span class="o">.</span><span class="na">add</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
    <span class="n">notifyListeners</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

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

<p>State management is a crucial skill in Flutter development. Start with simple solutions like <code class="language-plaintext highlighter-rouge">setState()</code> for local state, and gradually move to more complex solutions like Provider or Bloc as your app grows. Remember to keep your state organized, immutable, and close to where it’s used.</p>

<p>Stay tuned for more detailed posts about specific state management solutions in Flutter!</p>

<blockquote>
  <p>P.S. If you found this helpful, don’t forget to check out my other Flutter articles!</p>
</blockquote>]]></content><author><name>Md Didarul Islam</name></author><category term="State Management" /><category term="Flutter Basics" /><category term="flutter" /><category term="state management" /><category term="setState" /><category term="provider" /><category term="clean code" /><summary type="html"><![CDATA[Learn the fundamentals of state management in Flutter, including local state, global state, and best practices for managing app data.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">FutureBuilder in Flutter: A Complete Guide</title><link href="https://didarul.codes/blog/flutter%20basics/flutter%20widget/2023/01/20/futurebuilder-in-flutter.html" rel="alternate" type="text/html" title="FutureBuilder in Flutter: A Complete Guide" /><published>2023-01-20T12:00:00+08:00</published><updated>2023-01-20T12:00:00+08:00</updated><id>https://didarul.codes/blog/flutter%20basics/flutter%20widget/2023/01/20/futurebuilder-in-flutter</id><content type="html" xml:base="https://didarul.codes/blog/flutter%20basics/flutter%20widget/2023/01/20/futurebuilder-in-flutter.html"><![CDATA[<p>Hi there, fellow Flutter enthusiast!</p>

<p>Today I will share more about <code class="language-plaintext highlighter-rouge">FutureBuilder</code> in Flutter - an essential widget for handling asynchronous operations in your UI. Whether you’re fetching data from an API, reading files or performing complex calculations, <code class="language-plaintext highlighter-rouge">FutureBuilder</code> can come in handy.</p>

<h2 id="understanding-future">Understanding Future<T></T></h2>

<p>Before we dive into <code class="language-plaintext highlighter-rouge">FutureBuilder</code>, let’s quickly understand what <code class="language-plaintext highlighter-rouge">Future&lt;T&gt;</code> is. In Flutter/Dart, <code class="language-plaintext highlighter-rouge">Future&lt;T&gt;</code> represents a value that will be available at some point in the future. It’s the foundation for asynchronous programming, allowing you to perform async operations.</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">Future</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;</span> <span class="n">fetchUserData</span><span class="p">()</span> <span class="kd">async</span> <span class="p">{</span>
    <span class="c1">// Simulating API call</span>
    <span class="k">await</span> <span class="n">Future</span><span class="o">.</span><span class="na">delayed</span><span class="p">(</span><span class="kd">const</span> <span class="n">Duration</span><span class="p">(</span><span class="nl">seconds:</span> <span class="mi">2</span><span class="p">));</span>
    <span class="k">return</span> <span class="n">UserData</span><span class="p">(</span><span class="nl">name:</span> <span class="s">'John Doe'</span><span class="p">,</span> <span class="nl">age:</span> <span class="mi">30</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<h2 id="introduction-to-futurebuilder">Introduction to FutureBuilder</h2>

<p><code class="language-plaintext highlighter-rouge">FutureBuilder</code> is a widget that makes it easy to work with <code class="language-plaintext highlighter-rouge">Future&lt;T&gt;</code> in your UI. It helps to handle different states of the future:</p>
<ul>
  <li>Loading state (when the future is running)</li>
  <li>Success state (when the future completes successfully)</li>
  <li>Error state (when the future throws an error)</li>
</ul>

<p>Here’s a basic example:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">FutureBuilder</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;(</span>
    <span class="nl">future:</span> <span class="n">fetchUserData</span><span class="p">(),</span>
    <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">snapshot</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">connectionState</span> <span class="o">==</span> <span class="n">ConnectionState</span><span class="o">.</span><span class="na">waiting</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kd">const</span> <span class="n">Center</span><span class="p">(</span><span class="nl">child:</span> <span class="n">CircularProgressIndicator</span><span class="p">());</span>
        <span class="p">}</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">hasError</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">Center</span><span class="p">(</span>
                <span class="nl">child:</span> <span class="n">Text</span><span class="p">(</span><span class="s">'Error: </span><span class="si">${snapshot.error}</span><span class="s">'</span><span class="p">),</span>
            <span class="p">);</span>
        <span class="p">}</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">hasData</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">final</span> <span class="n">user</span> <span class="o">=</span> <span class="n">snapshot</span><span class="o">.</span><span class="na">data</span><span class="o">!</span><span class="p">;</span>
            <span class="k">return</span> <span class="n">UserProfileWidget</span><span class="p">(</span><span class="nl">user:</span> <span class="n">user</span><span class="p">);</span>
        <span class="p">}</span>
        
        <span class="k">return</span> <span class="kd">const</span> <span class="n">SizedBox</span><span class="p">();</span> <span class="c1">// Fallback for initial state</span>
    <span class="p">},</span>
<span class="p">)</span></code></pre></figure>

<h2 id="best-practices">Best Practices</h2>

<h3 id="1-avoid-creating-futures-in-build">1. Avoid Creating Futures in build()</h3>

<p>One common mistake is creating the future directly in the <code class="language-plaintext highlighter-rouge">build</code> method. This can cause unnecessary future executions and potential infinite loops.</p>

<p>❌ <strong>Don’t do this:</strong></p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">FutureBuilder</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;(</span>
    <span class="nl">future:</span> <span class="n">fetchUserData</span><span class="p">(),</span> <span class="c1">// Don't create future here!</span>
    <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">snapshot</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">},</span>
<span class="p">)</span></code></pre></figure>

<p>✅ <strong>Do this instead:</strong></p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">_MyWidgetState</span> <span class="kd">extends</span> <span class="n">State</span><span class="p">&lt;</span><span class="n">MyWidget</span><span class="p">&gt;</span> <span class="p">{</span>
    <span class="kd">late</span> <span class="kd">final</span> <span class="n">Future</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;</span> <span class="n">_userDataFuture</span><span class="p">;</span>

    <span class="nd">@override</span>
    <span class="kt">void</span> <span class="n">initState</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="p">();</span>
        <span class="n">_userDataFuture</span> <span class="o">=</span> <span class="n">fetchUserData</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="nd">@override</span>
    <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">FutureBuilder</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;(</span>
            <span class="nl">future:</span> <span class="n">_userDataFuture</span><span class="p">,</span>
            <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">snapshot</span><span class="p">)</span> <span class="p">{</span>
                <span class="c1">// ...</span>
            <span class="p">},</span>
        <span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h3 id="2-proper-error-handling">2. Proper Error Handling</h3>

<p>Always provide meaningful error feedback to your users:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">FutureBuilder</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;(</span>
    <span class="nl">future:</span> <span class="n">_userDataFuture</span><span class="p">,</span>
    <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">snapshot</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">connectionState</span> <span class="o">==</span> <span class="n">ConnectionState</span><span class="o">.</span><span class="na">waiting</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="kd">const</span> <span class="n">LoadingWidget</span><span class="p">();</span>
        <span class="p">}</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">hasError</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">ErrorWidget</span><span class="p">(</span>
                <span class="nl">error:</span> <span class="n">snapshot</span><span class="o">.</span><span class="na">error</span><span class="p">,</span>
                <span class="nl">onRetry:</span> <span class="p">()</span> <span class="p">{</span>
                    <span class="n">setState</span><span class="p">(()</span> <span class="p">{</span>
                        <span class="n">_userDataFuture</span> <span class="o">=</span> <span class="n">fetchUserData</span><span class="p">();</span>
                    <span class="p">});</span>
                <span class="p">},</span>
            <span class="p">);</span>
        <span class="p">}</span>
        
        <span class="kd">final</span> <span class="n">user</span> <span class="o">=</span> <span class="n">snapshot</span><span class="o">.</span><span class="na">data</span><span class="o">!</span><span class="p">;</span>
        <span class="k">return</span> <span class="n">UserProfileWidget</span><span class="p">(</span><span class="nl">user:</span> <span class="n">user</span><span class="p">);</span>
    <span class="p">},</span>
<span class="p">)</span></code></pre></figure>

<h3 id="3-handle-all-connection-states">3. Handle All Connection States</h3>

<p><code class="language-plaintext highlighter-rouge">FutureBuilder</code> provides different connection states that you should handle appropriately:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">FutureBuilder</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;(</span>
    <span class="nl">future:</span> <span class="n">_userDataFuture</span><span class="p">,</span>
    <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">snapshot</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">switch</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">connectionState</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">case</span> <span class="n">ConnectionState</span><span class="o">.</span><span class="na">none</span><span class="o">:</span>
                <span class="k">return</span> <span class="kd">const</span> <span class="n">Text</span><span class="p">(</span><span class="s">'No future to execute'</span><span class="p">);</span>
                
            <span class="k">case</span> <span class="n">ConnectionState</span><span class="o">.</span><span class="na">waiting</span><span class="o">:</span>
                <span class="k">return</span> <span class="kd">const</span> <span class="n">LoadingWidget</span><span class="p">();</span>
                
            <span class="k">case</span> <span class="n">ConnectionState</span><span class="o">.</span><span class="na">active</span><span class="o">:</span>
                <span class="c1">// This state occurs when the Future is still active but has yielded a value</span>
                <span class="c1">// It's more common with StreamBuilder, but can occur with certain Future implementations</span>
                <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
                  <span class="nl">children:</span> <span class="p">[</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">hasData</span><span class="p">)</span>
                      <span class="n">UserProfileWidget</span><span class="p">(</span><span class="nl">user:</span> <span class="n">snapshot</span><span class="o">.</span><span class="na">data</span><span class="o">!</span><span class="p">),</span>
                    <span class="kd">const</span> <span class="n">LinearProgressIndicator</span><span class="p">(),</span>
                  <span class="p">],</span>
                <span class="p">);</span>
                
            <span class="k">case</span> <span class="n">ConnectionState</span><span class="o">.</span><span class="na">done</span><span class="o">:</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">hasError</span><span class="p">)</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="n">ErrorWidget</span><span class="p">(</span>
                        <span class="nl">error:</span> <span class="n">snapshot</span><span class="o">.</span><span class="na">error</span><span class="p">,</span>
                        <span class="nl">onRetry:</span> <span class="n">_retryFetch</span><span class="p">,</span>
                    <span class="p">);</span>
                <span class="p">}</span>
                
                <span class="k">return</span> <span class="n">UserProfileWidget</span><span class="p">(</span><span class="nl">user:</span> <span class="n">snapshot</span><span class="o">.</span><span class="na">data</span><span class="o">!</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">},</span>
<span class="p">)</span></code></pre></figure>

<h2 id="advanced-usage">Advanced Usage</h2>

<h3 id="1-refreshable-future">1. Refreshable Future</h3>

<p>Here’s how to implement a pull-to-refresh functionality with <code class="language-plaintext highlighter-rouge">FutureBuilder</code>:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="n">RefreshIndicator</span><span class="p">(</span>
    <span class="nl">onRefresh:</span> <span class="p">()</span> <span class="kd">async</span> <span class="p">{</span>
        <span class="n">setState</span><span class="p">(()</span> <span class="p">{</span>
            <span class="n">_userDataFuture</span> <span class="o">=</span> <span class="n">fetchUserData</span><span class="p">();</span>
        <span class="p">});</span>
    <span class="p">},</span>
    <span class="nl">child:</span> <span class="n">FutureBuilder</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;(</span>
        <span class="nl">future:</span> <span class="n">_userDataFuture</span><span class="p">,</span>
        <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">snapshot</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// ... your builder logic</span>
        <span class="p">},</span>
    <span class="p">),</span>
<span class="p">)</span></code></pre></figure>

<h3 id="2-caching-results">2. Caching Results</h3>

<p>You can cache the future’s result to avoid unnecessary API calls:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">_MyWidgetState</span> <span class="kd">extends</span> <span class="n">State</span><span class="p">&lt;</span><span class="n">MyWidget</span><span class="p">&gt;</span> <span class="p">{</span>
    <span class="kd">late</span> <span class="n">Future</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;</span> <span class="n">_userDataFuture</span><span class="p">;</span>
    <span class="n">UserData</span><span class="o">?</span> <span class="n">_cachedData</span><span class="p">;</span>

    <span class="nd">@override</span>
    <span class="kt">void</span> <span class="n">initState</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="na">initState</span><span class="p">();</span>
        <span class="n">_userDataFuture</span> <span class="o">=</span> <span class="n">fetchUserData</span><span class="p">()</span><span class="o">.</span><span class="na">then</span><span class="p">((</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">_cachedData</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>
            <span class="k">return</span> <span class="n">data</span><span class="p">;</span>
        <span class="p">});</span>
    <span class="p">}</span>

    <span class="n">Future</span><span class="p">&lt;</span><span class="kt">void</span><span class="p">&gt;</span> <span class="n">_refreshData</span><span class="p">()</span> <span class="kd">async</span> <span class="p">{</span>
        <span class="n">setState</span><span class="p">(()</span> <span class="p">{</span>
            <span class="n">_userDataFuture</span> <span class="o">=</span> <span class="n">fetchUserData</span><span class="p">()</span><span class="o">.</span><span class="na">then</span><span class="p">((</span><span class="n">data</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">_cachedData</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>
                <span class="k">return</span> <span class="n">data</span><span class="p">;</span>
            <span class="p">});</span>
        <span class="p">});</span>
    <span class="p">}</span>

    <span class="nd">@override</span>
    <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">FutureBuilder</span><span class="p">&lt;</span><span class="n">UserData</span><span class="p">&gt;(</span>
            <span class="nl">future:</span> <span class="n">_userDataFuture</span><span class="p">,</span>
            <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">snapshot</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">snapshot</span><span class="o">.</span><span class="na">connectionState</span> <span class="o">==</span> <span class="n">ConnectionState</span><span class="o">.</span><span class="na">waiting</span> <span class="o">&amp;&amp;</span> <span class="n">_cachedData</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
                    <span class="c1">// Show cached data while refreshing</span>
                    <span class="k">return</span> <span class="n">UserProfileWidget</span><span class="p">(</span><span class="nl">user:</span> <span class="n">_cachedData</span><span class="o">!</span><span class="p">);</span>
                <span class="p">}</span>
                
                <span class="c1">// ... rest of your builder logic</span>
            <span class="p">},</span>
        <span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h3 id="remember">Remember</h3>
<ul>
  <li>Avoid creating new futures in the build method</li>
  <li>Initialize futures in <code class="language-plaintext highlighter-rouge">initState</code></li>
  <li>Cancel any ongoing operations in <code class="language-plaintext highlighter-rouge">dispose()</code></li>
  <li>Handle all possible states</li>
  <li>Provide meaningful error feedback</li>
  <li>Consider caching for better UX</li>
  <li>Use proper type parameters</li>
</ul>

<h3 id="example-code">Example Code</h3>
<p>Visit my <a href="https://github.com/islamdidarmd/flutter-playground/tree/master/lib/ui_example/pull_to_refresh">Flutter Playground App</a> repository for a complete example</p>

<h2 id="conclusion">Conclusion</h2>
<p>Choose FutureBuilder for simpler scenarios, and explore options like Provider, Bloc or Riverpod for more complex state management needs.</p>

<blockquote>
  <p><em>P.S. If you found this helpful, don’t forget to check out my other Flutter articles!</em></p>
</blockquote>]]></content><author><name>Md Didarul Islam</name></author><category term="Flutter Basics" /><category term="Flutter Widget" /><category term="flutter" /><category term="state management" /><category term="future" /><category term="async programming" /><category term="tips" /><summary type="html"><![CDATA[Learn how to effectively use FutureBuilder in Flutter to handle asynchronous operations and display data with proper error handling and loading states.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Handle Flutter Build Phase Errors Like a Pro</title><link href="https://didarul.codes/blog/flutter%20basics/2022/09/08/handle-flutter-errors-build-phase-error.html" rel="alternate" type="text/html" title="Handle Flutter Build Phase Errors Like a Pro" /><published>2022-09-08T14:00:00+08:00</published><updated>2022-09-08T14:00:00+08:00</updated><id>https://didarul.codes/blog/flutter%20basics/2022/09/08/handle-flutter-errors-build-phase-error</id><content type="html" xml:base="https://didarul.codes/blog/flutter%20basics/2022/09/08/handle-flutter-errors-build-phase-error.html"><![CDATA[<p>Build phase errors are an inevitable part of Flutter development. Whether it’s a null safety violation, layout constraint issue, or state management problem, these errors can disrupt the user experience. While Flutter provides default error handling, it’s not ideal for production apps. In this guide, I’ll show you how to transform these error scenarios into user-friendly experiences that maintain your app’s professional appearance.</p>

<h2 id="understanding-the-default-error-handling">Understanding the Default Error Handling</h2>

<p>Flutter handles build phase errors differently depending on the app’s mode:</p>

<ul>
  <li>In <strong>Debug Mode</strong>: Shows a red screen (the infamous “Red Screen of Death”) with detailed error information, stack traces, and debugging options</li>
  <li>In <strong>Release Mode</strong>: Displays a plain grey screen without any context, leaving users confused about what went wrong</li>
</ul>

<p>Here’s what users see when errors occur:</p>

<table>
  <thead>
    <tr>
      <th><img alt="Image" src="/assets/img/ui_exception_screen_in_debug.webp" width="200" /></th>
      <th><img alt="Image" src="/assets/img/ui_exception_screen_in_prod.webp" width="200" /></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Error In Debug Mode</td>
      <td>Error in Release Mode</td>
    </tr>
  </tbody>
</table>

<h2 id="the-problem">The Problem</h2>

<p>Default error screens present three major challenges:</p>

<ol>
  <li>
    <p><strong>Poor User Experience</strong></p>
  </li>
  <li>
    <p><strong>Developer Challenges</strong></p>
  </li>
  <li>
    <p><strong>Business Impact</strong></p>
  </li>
</ol>

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

<p>Let’s implement a better error handling system. We’ll explore two approaches:</p>

<h3 id="1-basic-error-widget-customization">1. Basic Error Widget Customization</h3>

<p>This approach is simple yet effective. We’ll override Flutter’s default error widget at the app’s entry point:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kt">void</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// Override the default error widget</span>
  <span class="n">ErrorWidget</span><span class="o">.</span><span class="na">builder</span> <span class="o">=</span> <span class="p">(</span><span class="n">FlutterErrorDetails</span> <span class="n">details</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Return a custom error widget that matches your brand</span>
    <span class="k">return</span> <span class="n">Container</span><span class="p">(</span>
      <span class="nl">padding:</span> <span class="kd">const</span> <span class="n">EdgeInsets</span><span class="o">.</span><span class="na">all</span><span class="p">(</span><span class="mf">16.0</span><span class="p">),</span>
      <span class="nl">alignment:</span> <span class="n">Alignment</span><span class="o">.</span><span class="na">center</span><span class="p">,</span>
      <span class="nl">child:</span> <span class="n">Text</span><span class="p">(</span>
        <span class="s">'Error</span><span class="se">\n</span><span class="si">${details.exception}</span><span class="s">'</span><span class="p">,</span>
        <span class="nl">style:</span> <span class="kd">const</span> <span class="n">TextStyle</span><span class="p">(</span><span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">red</span><span class="p">),</span>
        <span class="nl">textAlign:</span> <span class="n">TextAlign</span><span class="o">.</span><span class="na">center</span><span class="p">,</span>
      <span class="p">),</span>
    <span class="p">);</span>
  <span class="p">};</span>
  
  <span class="n">runApp</span><span class="p">(</span><span class="kd">const</span> <span class="n">MyApp</span><span class="p">());</span>
<span class="p">}</span></code></pre></figure>

<h3 id="2-advanced-error-handling">2. Advanced Error Handling</h3>

<p>For production apps, we can use <code class="language-plaintext highlighter-rouge">MaterialApp</code>’s builder parameter for more sophisticated error handling:</p>

<figure class="highlight"><pre><code class="language-dart" data-lang="dart"><span class="kd">class</span> <span class="nc">MyApp</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="n">MyApp</span><span class="p">({</span><span class="k">super</span><span class="o">.</span><span class="na">key</span><span class="p">});</span>

  <span class="nd">@override</span>
  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="n">MaterialApp</span><span class="p">(</span>
      <span class="nl">builder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">widget</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">Widget</span> <span class="n">errorWidget</span> <span class="o">=</span> <span class="kd">const</span> <span class="n">Text</span><span class="p">(</span>
          <span class="s">'Uh oh! Something is not right!'</span><span class="p">,</span>
          <span class="nl">style:</span> <span class="n">TextStyle</span><span class="p">(</span><span class="nl">color:</span> <span class="n">Colors</span><span class="o">.</span><span class="na">red</span><span class="p">),</span>
        <span class="p">);</span>

        <span class="c1">// Handle different widget types</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">widget</span> <span class="k">is</span> <span class="n">Scaffold</span> <span class="o">||</span> <span class="n">widget</span> <span class="k">is</span> <span class="n">Navigator</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">errorWidget</span> <span class="o">=</span> <span class="n">Scaffold</span><span class="p">(</span>
            <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="p">(</span><span class="nl">title:</span> <span class="kd">const</span> <span class="n">Text</span><span class="p">(</span><span class="s">'Error!'</span><span class="p">)),</span>
            <span class="nl">body:</span> <span class="n">Center</span><span class="p">(</span><span class="nl">child:</span> <span class="n">error</span><span class="p">),</span>
          <span class="p">);</span>
        <span class="p">}</span>

        <span class="n">ErrorWidget</span><span class="o">.</span><span class="na">builder</span> <span class="o">=</span> <span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="o">=</span><span class="p">&gt;</span> <span class="n">errorWidget</span><span class="p">;</span>

        <span class="c1">// Return the original widget if no errors</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">widget</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="k">return</span> <span class="n">widget</span><span class="p">;</span>
        <span class="k">throw</span> <span class="n">Exception</span><span class="p">(</span><span class="s">'widget is null'</span><span class="p">);</span>
      <span class="p">},</span>
      <span class="nl">home:</span> <span class="kd">const</span> <span class="n">MyHomeScreen</span><span class="p">(),</span>
    <span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h3 id="the-result">The Result</h3>

<p>After implementing custom error handling, users see this instead:</p>

<p><img alt="Image" src="/assets/img/ui_exception_handled.webp" width="200" /></p>

<blockquote>
  <p><strong>Pro Tip</strong>: In production apps, move the error handling logic to a dedicated widget or utility class for better maintainability and reusability across different screens.</p>
</blockquote>

<h2 id="key-benefits">Key Benefits</h2>

<ol>
  <li>
    <p><strong>Better User Experience</strong></p>
  </li>
  <li>
    <p><strong>Improved Development</strong></p>
  </li>
  <li>
    <p><strong>Business Advantages</strong></p>
  </li>
</ol>

<h2 id="implementation-tips">Implementation Tips</h2>

<ol>
  <li><strong>Error Messages</strong>
    <ul>
      <li>Keep language simple and clear</li>
      <li>Provide specific next steps</li>
      <li>Stay positive and helpful</li>
      <li>Avoid technical jargon</li>
    </ul>
  </li>
  <li><strong>Visual Design</strong>
    <ul>
      <li>Follow your app’s theme consistently</li>
      <li>Use appropriate icons and colors</li>
      <li>Maintain brand identity</li>
      <li>Ensure accessibility standards</li>
    </ul>
  </li>
  <li><strong>Error Handling</strong>
    <ul>
      <li>Implement comprehensive logging</li>
      <li>Add meaningful retry options</li>
      <li>Handle offline scenarios gracefully</li>
      <li>Consider various error types</li>
    </ul>
  </li>
  <li><strong>Testing</strong>
    <ul>
      <li>Verify all error scenarios</li>
      <li>Test error tracking systems</li>
      <li>Check different device sizes</li>
      <li>Validate accessibility features</li>
    </ul>
  </li>
</ol>

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

<p>Good error handling is about turning potential frustrations into opportunities to showcase your app’s professionalism. By implementing these strategies, you can create a more resilient app that handles errors gracefully and maintains user trust.</p>

<blockquote>
  <p><strong>Want to dive deeper?</strong> Check out my other articles about Flutter development and best practices.</p>
</blockquote>]]></content><author><name>Md Didarul Islam</name></author><category term="Flutter Basics" /><category term="flutter" /><category term="error handling" /><category term="debugging" /><category term="widget" /><category term="build phase" /><category term="user experience" /><summary type="html"><![CDATA[Learn how to handle build phase errors in Flutter and create beautiful custom error screens instead of the default error UI. A practical guide to improving user experience during error scenarios.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://didarul.codes/blog/assets/img/default-post.svg" /><media:content medium="image" url="https://didarul.codes/blog/assets/img/default-post.svg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>