<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Samuel Adekunle</title>
    <description>The latest articles on DEV Community by Samuel Adekunle (@techwithsam).</description>
    <link>https://dev.to/techwithsam</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg</url>
      <title>DEV Community: Samuel Adekunle</title>
      <link>https://dev.to/techwithsam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/techwithsam"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Mon, 28 Sep 2026 12:09:37 +0000</pubDate>
      <link>https://dev.to/techwithsam/-26do</link>
      <guid>https://dev.to/techwithsam/-26do</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/techwithsam/mcp-crash-course-model-context-protocol-explained-simply-266e" class="crayons-story__hidden-navigation-link"&gt;MCP Crash Course: Model Context Protocol Explained (Simply)&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/JJK_XtQpd1g" title="MCP Crash Course: Model Context Protocol Explained (Simply)"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/techwithsam" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" alt="techwithsam profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/techwithsam" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Samuel Adekunle
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Samuel Adekunle
                
                
              
              &lt;div id="story-author-preview-content-4759114" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/techwithsam" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Samuel Adekunle&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/techwithsam/mcp-crash-course-model-context-protocol-explained-simply-266e" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 28&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/techwithsam/mcp-crash-course-model-context-protocol-explained-simply-266e" id="article-link-4759114"&gt;
          MCP Crash Course: Model Context Protocol Explained (Simply)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mcp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mcp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/techwithsam"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;techwithsam&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/claude"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;claude&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/techwithsam/mcp-crash-course-model-context-protocol-explained-simply-266e" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/techwithsam/mcp-crash-course-model-context-protocol-explained-simply-266e#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>MCP Crash Course: Model Context Protocol Explained (Simply)</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Mon, 28 Sep 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/techwithsam/mcp-crash-course-model-context-protocol-explained-simply-266e</link>
      <guid>https://dev.to/techwithsam/mcp-crash-course-model-context-protocol-explained-simply-266e</guid>
      <description>&lt;p&gt;Having to manually copy-paste context between your code environments (like IDEs) and Claude Desktop, or build fragmented, one-off custom integrations for every single new data source can be very frustrating.&lt;/p&gt;

&lt;p&gt;That's why &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; was introduced and open-sourced by Anthropic in November 2024 to create a universal, standardized way for AI models to connect with external data sources, developer tools, and local systems.&lt;/p&gt;

&lt;p&gt;Today, we're creating a complete handbook on how this MCP solution works, how to use it, and how it became one of the most useful things in AI right now.&lt;/p&gt;

&lt;p&gt;By the end of this handbook, you'll understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What MCP actually is&lt;/li&gt;
&lt;li&gt;Why it matters&lt;/li&gt;
&lt;li&gt;How to use existing MCP servers&lt;/li&gt;
&lt;li&gt;How to create a simple one yourself&lt;/li&gt;
&lt;li&gt;And the key best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll keep it straightforward. Let's get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is&amp;nbsp;MCP?
&lt;/h2&gt;

&lt;p&gt;MCP stands for &lt;strong&gt;Model Context Protocol&lt;/strong&gt;. &lt;em&gt;&lt;a href="https://modelcontextprotocol.io/docs/2026-07-28/getting-started/intro" rel="noopener noreferrer"&gt;(Official documentation)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In simple terms, it's a standard way for AI tools (like Claude, Cursor, or other AI coding assistants) to connect to external tools and data.&lt;/p&gt;

&lt;p&gt;Think of it like USB-C for AI.&lt;/p&gt;

&lt;p&gt;Before USB-C, every device had its own cable. Before MCP, every AI tool needed custom code to communicate with each external service. That became messy fast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Filzdd5xx5xtww9cu2j9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Filzdd5xx5xtww9cu2j9t.png" alt="GIF of USB-C" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With MCP, you (or someone else) create an &lt;strong&gt;MCP server&lt;/strong&gt;. Once that server exists, any compatible AI can use it.&lt;/p&gt;

&lt;p&gt;An MCP server can offer three main things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt; → Actions the AI can perform (for example: "add two numbers", "create a GitHub issue", "read a file")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resources&lt;/strong&gt; → Data the AI can read&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts&lt;/strong&gt; → Ready-made instructions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of the time, you'll mainly work with &lt;strong&gt;Tools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the core idea. MCP gives AI applications a standard way to use external capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  See MCP in action&amp;nbsp;(Demos)
&lt;/h2&gt;

&lt;p&gt;Let me show you how this looks in practice.&lt;/p&gt;

&lt;p&gt;First, you can use existing MCP servers built by others. There are servers for the filesystem, GitHub, databases, browsers, and many other services.&lt;/p&gt;

&lt;p&gt;You connect them to your AI tool (Claude Desktop, Cursor, etc.), and suddenly the AI can use those tools when needed.&lt;/p&gt;

&lt;p&gt;For this handbook, I'll also show you a simple server I created so you can see exactly how it works.&lt;/p&gt;

&lt;p&gt;Here's a basic &lt;a href="https://py.sdk.modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Python MCP server&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.mcpserver&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MCPServer&lt;/span&gt;

&lt;span class="c1"&gt;# Create the MCP server
&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MCPServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Simple Demo Server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Add two numbers together.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return the current date and time.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%d %H:%M:%S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Greet someone by name.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;! Welcome to MCP.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This server exposes three simple tools: &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;get_current_time&lt;/code&gt;, and &lt;code&gt;greet&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once this server is running and connected to your AI, you can ask things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What is 95 plus 107?"&lt;/li&gt;
&lt;li&gt;"What's the current time?"&lt;/li&gt;
&lt;li&gt;"Greet Samuel"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI will automatically call the right tool and return the result.&lt;/p&gt;

&lt;p&gt;That's the power of MCP: the AI gains new abilities without you writing custom integration code every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a simple MCP&amp;nbsp;server
&lt;/h2&gt;

&lt;p&gt;Now let's create one ourselves.&lt;/p&gt;

&lt;p&gt;We'll use Python because it's simple and widely used, but you can pick from any of the available &lt;a href="https://modelcontextprotocol.io/docs/2026-07-28/sdk" rel="noopener noreferrer"&gt;MCP SDKs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install the package&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv add &lt;span class="s2"&gt;"mcp[cli]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Create a file called&lt;/strong&gt; &lt;code&gt;server.py&lt;/code&gt; and paste the code I showed earlier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Run it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can run it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the MCP development tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run mcp dev server.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Connect it to your AI tool&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Claude Desktop or Cursor, you add a configuration that points to this server. Once connected, the tools become available to the AI.&lt;/p&gt;

&lt;p&gt;That's it. You now have a working MCP server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb80irg0gp3tiuyghik2h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb80irg0gp3tiuyghik2h.png" alt="Clause Desktop Connectors" width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, you can expand it. Instead of simple math and greetings, you can add tools that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read or write files&lt;/li&gt;
&lt;li&gt;Call external APIs&lt;/li&gt;
&lt;li&gt;Interact with your project&lt;/li&gt;
&lt;li&gt;Query a database&lt;/li&gt;
&lt;li&gt;Or anything else useful for your workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern stays the same: create a tool, give it a clear description, and return a useful result.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use and share MCP&amp;nbsp;servers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Using existing servers&lt;/strong&gt;&lt;br&gt;
Most of the time you'll just install or connect servers that already exist. You add them to your AI tool's configuration and start using them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F98mrlvqwa4mirrslba9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F98mrlvqwa4mirrslba9q.png" alt="Discover Connectors on Clause Desktop" width="799" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharing your own server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you build something useful:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put the code on GitHub&lt;/li&gt;
&lt;li&gt;Write a clear README showing how to install and run it&lt;/li&gt;
&lt;li&gt;Include the configuration snippet people need to connect it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's usually enough for others (or your team) to use it.&lt;/p&gt;

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

&lt;p&gt;Here are the most important things to remember:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write clear tool descriptions&lt;/strong&gt;&lt;br&gt;
The AI decides when to use a tool based on the description. Make it specific and easy to understand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep tools focused&lt;/strong&gt;&lt;br&gt;
One tool should do one clear job.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Be careful with permissions&lt;/strong&gt;&lt;br&gt;
Especially when giving access to files, databases, or external APIs. Only give what's needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test your server&lt;/strong&gt;&lt;br&gt;
Use the MCP development tools to test before connecting it to your main AI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Only use servers you trust&lt;/strong&gt;&lt;br&gt;
Review what tools a server exposes before connecting it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Follow these, and you'll avoid most common problems.&lt;/p&gt;




&lt;p&gt;That's the complete simplified handbook on MCP.&lt;/p&gt;

&lt;p&gt;You now know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What MCP is&lt;/li&gt;
&lt;li&gt;How it gives AI tools new abilities&lt;/li&gt;
&lt;li&gt;How to use existing servers&lt;/li&gt;
&lt;li&gt;How to create a simple one yourself&lt;/li&gt;
&lt;li&gt;And the key best practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MCP is about giving &lt;strong&gt;AI access&lt;/strong&gt; to tools and data.&lt;/p&gt;

&lt;p&gt;In a future release, we'll look at how this pairs with &lt;strong&gt;Skills&lt;/strong&gt;, which give the AI expertise on how to use those tools well.&lt;/p&gt;

&lt;p&gt;If you build a simple MCP server after reading this, drop the GitHub link in the comments. I'd love to see it.&lt;/p&gt;

&lt;p&gt;If this was helpful, hit like, follow, and turn on notifications.&lt;/p&gt;

&lt;p&gt;I also offer 1-on-1 mentorship if you want help setting up AI workflows for your projects - &lt;a href="https://calendar.app.google/j4FfEbsehwtmjgXn6" rel="noopener noreferrer"&gt;click here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll see you in the next one. Peace.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>techwithsam</category>
      <category>claude</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Fri, 18 Sep 2026 12:26:43 +0000</pubDate>
      <link>https://dev.to/techwithsam/-3i4j</link>
      <guid>https://dev.to/techwithsam/-3i4j</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/techwithsam/ai-agent-skills-everything-you-need-to-know-44pi" class="crayons-story__hidden-navigation-link"&gt;AI Agent Skills: Everything You Need to Know&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/447Ec-yQxb4" title="AI Agent Skills: Everything You Need to Know"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/techwithsam" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" alt="techwithsam profile" class="crayons-avatar__image" width="800" height="1097"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/techwithsam" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Samuel Adekunle
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Samuel Adekunle
                
                
              
              &lt;div id="story-author-preview-content-4685456" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/techwithsam" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" class="crayons-avatar__image" alt="" width="800" height="1097"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Samuel Adekunle&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/techwithsam/ai-agent-skills-everything-you-need-to-know-44pi" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/techwithsam/ai-agent-skills-everything-you-need-to-know-44pi" id="article-link-4685456"&gt;
          AI Agent Skills: Everything You Need to Know
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/skills"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;skills&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/techwithsam"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;techwithsam&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/aiagents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;aiagents&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/techwithsam/ai-agent-skills-everything-you-need-to-know-44pi" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/techwithsam/ai-agent-skills-everything-you-need-to-know-44pi#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>AI Agent Skills: Everything You Need to Know</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Fri, 18 Sep 2026 12:25:34 +0000</pubDate>
      <link>https://dev.to/techwithsam/ai-agent-skills-everything-you-need-to-know-44pi</link>
      <guid>https://dev.to/techwithsam/ai-agent-skills-everything-you-need-to-know-44pi</guid>
      <description>&lt;p&gt;In 2024, AI coding agents (Claude, Cursor, Copilot, etc.) became capable enough to do real work. Teams started writing long instruction files and system prompts. But there was no standard way to package, share, or discover them. Every team reinvented the wheel.&lt;/p&gt;

&lt;p&gt;Today, we’re creating a complete handbook on how the solution to the issue above came about, how to use it, and how it became one of the most useful things in AI right now: &lt;strong&gt;Agent Skills&lt;/strong&gt; and the platform that makes them easy to use — &lt;strong&gt;skills.sh&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By the end of this workflow, you will understand:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What Agent Skills actually are&lt;/li&gt;
&lt;li&gt;How skills.sh works&lt;/li&gt;
&lt;li&gt;How to install and use great existing skills&lt;/li&gt;
&lt;li&gt;How to create your own skills from scratch&lt;/li&gt;
&lt;li&gt;How to share them with your team or the world&lt;/li&gt;
&lt;li&gt;And the best practices so your skills actually get used&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is not Flutter-only. The same system works whether you write Flutter, React, backend code, DevOps, or even content. I’ll use Flutter as one practical example, but everything here is flexible.&lt;/p&gt;

&lt;p&gt;Let’s get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Agent Skills and skills.sh?
&lt;/h2&gt;

&lt;p&gt;An Agent Skill is a lightweight, open format for giving AI agents specialized knowledge and workflows. At its core, a skill is just a folder that contains a file called SKILL.md.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;my-skill/
├── SKILL.md          ← Required
├── scripts/          ← Optional
├── references/       ← Optional
└── assets/           ← Optional
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SKILL.md&lt;/code&gt; file has two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;YAML frontmatter at the top (name + description)&lt;/li&gt;
&lt;li&gt;Markdown instructions below&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The description is extremely important. Agents only load the name and description of every skill at startup. When your request matches a skill’s description, the agent then loads the full instructions. This is called &lt;strong&gt;progressive disclosure&lt;/strong&gt; — it keeps the context window clean even if you have dozens of skills installed.&lt;/p&gt;

&lt;p&gt;Now, what is &lt;strong&gt;skills.sh&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.skills.sh/" rel="noopener noreferrer"&gt;skills.sh&lt;/a&gt; is the open directory and CLI for the Agent Skills ecosystem. Think of it as the npm for AI agent capabilities. It was built by Vercel Labs and works across many agents, including Claude Code, Cursor, Codex, GitHub Copilot, Windsurf, Gemini CLI, and more.&lt;/p&gt;

&lt;p&gt;You install skills with a single command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx skills add owner/repo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That’s it. The CLI detects which agents you have and installs the skill in the right place.&lt;/p&gt;

&lt;p&gt;You can browse popular skills on &lt;a href="https://www.skills.sh/" rel="noopener noreferrer"&gt;skills.sh&lt;/a&gt;, see a leaderboard, and discover what the community is building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discover on skills.sh
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Community leaderboard sorted by installs&lt;/li&gt;
&lt;li&gt;Browse by category, language, or use case&lt;/li&gt;
&lt;li&gt;Official skills from Anthropic, Vercel, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9xojab9d1qj77twnmmbc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9xojab9d1qj77twnmmbc.png" alt="skills.sh landing page" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo of some great skills
&lt;/h2&gt;

&lt;p&gt;Let me show you how this looks in practice.&lt;/p&gt;

&lt;p&gt;First, installing a skill is simple; run: &lt;code&gt;npx skills add vercel-labs/agent-skills&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: You need to have Node.js installed on your machine; &lt;a href="https://nodejs.org/en/download" rel="noopener noreferrer"&gt;download and install it&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can also install any public repo that contains skills.&lt;/p&gt;

&lt;p&gt;Once installed, the agent can automatically use them when relevant.&lt;/p&gt;

&lt;p&gt;Here are four strong examples of skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1 — PR Review (universal)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pr-review&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reviews&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pull&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;requests&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bugs,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;security&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;issues,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;quality,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;adherence&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;best&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;practices.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Use&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;when&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;asks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;review&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PR,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;check&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;diff,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;feedback&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;before&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;merging."&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one works on any codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2 — Flutter Widget Test (our case study)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter-widget-test&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generates high-quality Flutter widget tests following best practices. Use when the user asks to write tests for a Flutter widget, create widget tests, or improve test coverage for UI components.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows how you can encode framework-specific knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 3 — Generate Changelog
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;generate-changelog&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generates clean, professional changelog entries or release notes from git commits or PR descriptions. Use when the user asks for a changelog, release notes, or version summary.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Super practical for any project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 4 — Brand Voice (non-code example)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;brand-voice&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Writes content following a consistent professional brand voice. Use when creating LinkedIn posts, Twitter/X threads, blog intros, product announcements, or any marketing copy.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This proves skills are not only for code. You can package writing style, design systems, internal processes — anything repeatable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build your own skill
&lt;/h2&gt;

&lt;p&gt;This is the most important part of the workflow.&lt;/p&gt;

&lt;p&gt;Creating a skill is straightforward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Create a folder and the required file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-skill
&lt;span class="nb"&gt;touch &lt;/span&gt;my-skill/SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Write the frontmatter&lt;/p&gt;

&lt;p&gt;The two required fields are &lt;strong&gt;name&lt;/strong&gt; and &lt;strong&gt;description&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The description is what the agent uses to decide whether to activate the skill. Be specific. Include the kind of phrases a real user would say.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-skill-name&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;What it does and when to use it.&lt;/span&gt;
&lt;span class="s"&gt;Include real trigger phrases here.&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Write clear instructions&lt;/p&gt;

&lt;p&gt;Good structure usually includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When to use this skill&lt;/li&gt;
&lt;li&gt;When &lt;strong&gt;not&lt;/strong&gt; to use it&lt;/li&gt;
&lt;li&gt;Step-by-step process the agent should follow&lt;/li&gt;
&lt;li&gt;Expected output format&lt;/li&gt;
&lt;li&gt;Any edge cases or rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Write it like you’re explaining it to a smart new teammate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Test with real prompts&lt;/p&gt;

&lt;p&gt;Install locally with &lt;code&gt;npx skills add ./my-skill&lt;/code&gt; then trigger it with exact phrases. If it doesn't activate, the description needs to be more specific.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key tips while creating:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start small. A focused 15–30 line skill is better than a long vague one.&lt;/li&gt;
&lt;li&gt;Make the description do the heavy lifting for activation.&lt;/li&gt;
&lt;li&gt;Write instructions the way you would explain the task to a smart junior developer.&lt;/li&gt;
&lt;li&gt;Test it immediately after creating it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you’re happy with it, you can install it locally and start using it right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share your skills
&lt;/h2&gt;

&lt;p&gt;You have two main ways to share.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. With your team / private&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just commit the skills folder into your repository. Everyone who clones the project gets the skills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Publicly with skills.sh&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Push the skill to a public GitHub repository. You have two common structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dedicated repo → SKILL.md sits at the root&lt;/li&gt;
&lt;li&gt;Multi-skill repo → skills live in subfolders
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Single skill&lt;/span&gt;
my-repo/
└── SKILL.md

&lt;span class="gh"&gt;# Multi-skill&lt;/span&gt;
my-repo/
├── pr-review/
│ └── SKILL.md
└── changelog/
└── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then anyone can install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Public: single-skill repo&lt;/span&gt;
npx skills add your-username/my-skill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or target a specific skill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Public: multi-skill repo &lt;/span&gt;
npx skills add you/repo &lt;span class="nt"&gt;--skill&lt;/span&gt; name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also add the skills.sh badge to your README so people can see install counts.&lt;/p&gt;

&lt;p&gt;Clear name + excellent description = more people will find and use your skill.&lt;/p&gt;

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

&lt;p&gt;Here are the practices that separate good skills from great ones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write trigger-friendly descriptions&lt;/strong&gt; — this is the #1 factor for reliable activation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep skills focused&lt;/strong&gt; — one clear job per skill. Compose multiple skills instead of making one giant skill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use progressive disclosure properly&lt;/strong&gt; — put long references or examples in separate files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document when not to use the skill.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with real phrasing&lt;/strong&gt; — try the exact words your users (or you) would actually type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat skills like code&lt;/strong&gt; — version them, review them, improve them over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only install skills from sources you trust&lt;/strong&gt; — always skim the SKILL.md and any scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For teams&lt;/strong&gt; — store shared skills in the repo so consistency is automatic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Follow these, and your skills will actually get used instead of sitting unused.&lt;/p&gt;




&lt;p&gt;That’s the complete handbook.&lt;/p&gt;

&lt;p&gt;You now know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What Agent Skills are and how progressive disclosure works&lt;/li&gt;
&lt;li&gt;What skills.sh is and how to install skills&lt;/li&gt;
&lt;li&gt;How to demo and evaluate existing skills&lt;/li&gt;
&lt;li&gt;How to create your own&lt;/li&gt;
&lt;li&gt;How to share them&lt;/li&gt;
&lt;li&gt;And the best practices that make them effective&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest shift is this: instead of repeating the same instructions in every chat, you package the knowledge once and let any compatible agent use it.&lt;/p&gt;

&lt;p&gt;If you create a skill after reading this, drop the GitHub link in the comments. I’d love to see what you build.&lt;/p&gt;

&lt;p&gt;If you found this valuable, hit like, subscribe, and turn on notifications.&lt;/p&gt;

&lt;p&gt;I also offer 1-on-1 mentorship if you want help setting up skills or AI workflows for your team — &lt;a href="https://calendar.app.google/j4FfEbsehwtmjgXn6" rel="noopener noreferrer"&gt;click here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ll see you in the next one. Peace.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>skills</category>
      <category>techwithsam</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Thu, 10 Sep 2026 22:27:33 +0000</pubDate>
      <link>https://dev.to/techwithsam/-ake</link>
      <guid>https://dev.to/techwithsam/-ake</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/techwithsam/automate-flutter-releases-with-shorebird-github-actions-skip-app-store-review-2j1l" class="crayons-story__hidden-navigation-link"&gt;Automate Flutter Releases with Shorebird + GitHub Actions (Skip App Store Review)&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/mHek8Uu32ow" title="Automate Flutter Releases with Shorebird + GitHub Actions (Skip App Store Review)"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/techwithsam" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" alt="techwithsam profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/techwithsam" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Samuel Adekunle
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Samuel Adekunle
                
                
              
              &lt;div id="story-author-preview-content-4627026" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/techwithsam" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Samuel Adekunle&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/techwithsam/automate-flutter-releases-with-shorebird-github-actions-skip-app-store-review-2j1l" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 10&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/techwithsam/automate-flutter-releases-with-shorebird-github-actions-skip-app-store-review-2j1l" id="article-link-4627026"&gt;
          Automate Flutter Releases with Shorebird + GitHub Actions (Skip App Store Review)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/flutter"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;flutter&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mobile"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mobile&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/techwithsam"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;techwithsam&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/githubactions"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;githubactions&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/techwithsam/automate-flutter-releases-with-shorebird-github-actions-skip-app-store-review-2j1l" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/techwithsam/automate-flutter-releases-with-shorebird-github-actions-skip-app-store-review-2j1l#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Automate Flutter Releases with Shorebird + GitHub Actions (Skip App Store Review)</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Thu, 10 Sep 2026 22:27:12 +0000</pubDate>
      <link>https://dev.to/techwithsam/automate-flutter-releases-with-shorebird-github-actions-skip-app-store-review-2j1l</link>
      <guid>https://dev.to/techwithsam/automate-flutter-releases-with-shorebird-github-actions-skip-app-store-review-2j1l</guid>
      <description>&lt;p&gt;A real pain point for mobile apps is waiting days or weeks for App Store and Play Store reviews whenever you need to ship a fix or a small update to users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswg5gbczn3brmqu7jeor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswg5gbczn3brmqu7jeor.png" alt="Elon Musk complaining about iOS App Review delay on X" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That exact problem is what we are solving in this article. I’ll show you exactly how to automate your Flutter releases and patches using Shorebird and GitHub Actions, so you can push updates to users in minutes instead of waiting for store review.&lt;/p&gt;

&lt;p&gt;We are going to create a clean, production-ready CI pipeline for both full releases and instant code-push updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, here’s what you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://console.shorebird.dev/" rel="noopener noreferrer"&gt;Shorebird account&lt;/a&gt; and an app already set up with Shorebird&lt;/li&gt;
&lt;li&gt;Shorebird CLI installed and logged in on your machine&lt;/li&gt;
&lt;li&gt;A Flutter project that already has Shorebird initialized&lt;/li&gt;
&lt;li&gt;A GitHub repository&lt;/li&gt;
&lt;li&gt;Basic understanding of GitHub Actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t set up Shorebird in your Flutter app yet, check out &lt;a href="https://youtu.be/x7j3WnxfCc0" rel="noopener noreferrer"&gt;my video on YouTube on how to set up Shorebird&lt;/a&gt;. This article assumes that’s already done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication Setup
&lt;/h2&gt;

&lt;p&gt;The first thing we need is authentication so GitHub Actions can talk to Shorebird.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://console.shorebird.dev/account" rel="noopener noreferrer"&gt;Shorebird Console&lt;/a&gt; → Account → API Keys → Create API Key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv9iplxigrfum3t692fd2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv9iplxigrfum3t692fd2.png" alt="Shorebird API Key Interface" width="799" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give it a clear name, e.g “GitHub Actions”, choose an expiration, and set the required permissions.&lt;/p&gt;

&lt;p&gt;Copy the key immediately because you won’t see it again.&lt;/p&gt;

&lt;p&gt;Now go to your GitHub repository → Settings → Secrets and variables → Actions → New repository secret.&lt;/p&gt;

&lt;p&gt;Name it exactly: &lt;code&gt;SHOREBIRD_TOKEN&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Paste the key and save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9jctb6yd6tgrl0c8tuab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9jctb6yd6tgrl0c8tuab.png" alt="GitHub Repository Secret Interface" width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This token will be available in all your workflows as &lt;code&gt;${{ secrets.SHOREBIRD_TOKEN }}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s the only authentication step you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Official Shorebird GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Shorebird provides three official GitHub Actions that make life much easier:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;shorebirdtech/setup-shorebird@v1 — installs Shorebird on the runner&lt;/li&gt;
&lt;li&gt;shorebirdtech/shorebird-release@v1 — creates a release&lt;/li&gt;
&lt;li&gt;shorebirdtech/shorebird-patch@v1 — creates a patch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I strongly recommend using these instead of calling the CLI manually. They’re cleaner and maintained by the Shorebird team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release Workflow
&lt;/h2&gt;

&lt;p&gt;Let’s build the release workflow first.&lt;/p&gt;

&lt;p&gt;Create a new file in your project: &lt;code&gt;.github/workflows/shorebird-release.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here’s the structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trigger on version tags: v1.0.0, v1.2.3, etc. or manually trigger the workflow&lt;/li&gt;
&lt;li&gt;Set the SHOREBIRD_TOKEN as an environment variable&lt;/li&gt;
&lt;li&gt;Pin your Flutter version (very important)&lt;/li&gt;
&lt;li&gt;Checkout the code&lt;/li&gt;
&lt;li&gt;Set up Java for Android (or Xcode signing for iOS)&lt;/li&gt;
&lt;li&gt;Set up Shorebird with caching&lt;/li&gt;
&lt;li&gt;Decode your keystore/certificates from secrets&lt;/li&gt;
&lt;li&gt;Run the official shorebird-release action&lt;/li&gt;
&lt;li&gt;Upload the APK, AAB, or IPA as artifacts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ll show both Android and iOS versions.&lt;/p&gt;

&lt;p&gt;For Android, the key steps are decoding the keystore and creating the key.properties file from secrets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Secrets You Need to Add for Android&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;GitHub → repo → Settings → Secrets and variables → Actions → New repository secret&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Secret name - Value
ANDROID_KEY_ALIAS - your_key_alias
ANDROID_KEY_PASSWORD - your_key_password
ANDROID_STORE_PASSWORD - your_store_password
ANDROID_KEYSTORE_BASE64 - run &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; ~/path/to/upload.jks | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; | pbcopy&lt;span class="sb"&gt;`&lt;/span&gt;
The keystore &lt;span class="nb"&gt;base64 &lt;/span&gt;will be copied to your clipboard&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;paste &lt;/span&gt;it into GitHub Secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub Secrets You Need to Add for iOS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;GitHub → repo → Settings → Secrets and variables → Actions → New repository secret&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Secret name - Value
IOS_CERTIFICATE_BASE64 - &lt;span class="nb"&gt;base64 &lt;/span&gt;of your .p12
IOS_CERTIFICATE_PASSWORD - Password you &lt;span class="nb"&gt;set &lt;/span&gt;when exporting the .p12
IOS_PROVISIONING_PROFILE_BASE64 - &lt;span class="nb"&gt;base64 &lt;/span&gt;of com.example.app profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For iOS, you need to import the certificate and provisioning profile into a temporary keychain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# IOS_CERTIFICATE_BASE64 - export your .p12 from Keychain Access first, then:&lt;/span&gt;
&lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; ~/path/to/distribution.p12 | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; | pbcopy

&lt;span class="c"&gt;# IOS_PROVISIONING_PROFILE_BASE64 - download from Apple Developer Portal&lt;/span&gt;
&lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; ~/path/to/YourApp_AppStore.mobileprovision | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; | pbcopy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check out this document on how to create an &lt;a href="https://gist.github.com/techwithsam/e3ff26a6e12cde347e90158572e216c9#file-tutorial-md" rel="noopener noreferrer"&gt;Apple Distribution Certificate&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Once this workflow finishes, you get signed artifacts ready to upload to the stores.&lt;/p&gt;

&lt;p&gt;This is your normal full release path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Shorebird Release&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Target&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;platform'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;both'&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;choice&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;android&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ios&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;both&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release_android&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release Android&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.inputs.platform == 'android' || github.event.inputs.platform == 'both' }}&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Java&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-java@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;distribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temurin'&lt;/span&gt;
          &lt;span class="na"&gt;java-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;17'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Android Keystore&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode &amp;gt; android/app/keystore.jks&lt;/span&gt;
          &lt;span class="s"&gt;cat &amp;gt; android/key.properties &amp;lt;&amp;lt;EOF&lt;/span&gt;
          &lt;span class="s"&gt;storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}&lt;/span&gt;
          &lt;span class="s"&gt;keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}&lt;/span&gt;
          &lt;span class="s"&gt;keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}&lt;/span&gt;
          &lt;span class="s"&gt;storeFile=keystore.jks&lt;/span&gt;
          &lt;span class="s"&gt;EOF&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Shorebird&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shorebirdtech/setup-shorebird@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Shorebird Release (Android)&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shorebirdtech/shorebird-release@v1&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shorebird-release&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;flutter-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;
          &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;android&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SHOREBIRD_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SHOREBIRD_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print Release Version&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Released version ${{ steps.shorebird-release.outputs.release-version }}"&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full release on &lt;a href="https://gist.github.com/techwithsam/e3ff26a6e12cde347e90158572e216c9#file-shorebird-release-yml" rel="noopener noreferrer"&gt;GitHub Gist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Patch Workflow — The Real Power
&lt;/h2&gt;

&lt;p&gt;Now, the part that actually lets you skip App Store review.&lt;/p&gt;

&lt;p&gt;Create another file: &lt;code&gt;.github/workflows/shorebird-patch.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Shorebird Patch&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Target&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;platform'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;both'&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;choice&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;android&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ios&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;both&lt;/span&gt;
      &lt;span class="na"&gt;release-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;patch&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(e.g.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1.0.0+1).&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Defaults&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;latest.'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;patch_android&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Patch Android&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'push' || github.event.inputs.platform == 'android' || github.event.inputs.platform == 'both' }}&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Java&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-java@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;distribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temurin'&lt;/span&gt;
          &lt;span class="na"&gt;java-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;17'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Android Keystore&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode &amp;gt; android/app/keystore.jks&lt;/span&gt;
          &lt;span class="s"&gt;cat &amp;gt; android/key.properties &amp;lt;&amp;lt;EOF&lt;/span&gt;
          &lt;span class="s"&gt;storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}&lt;/span&gt;
          &lt;span class="s"&gt;keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}&lt;/span&gt;
          &lt;span class="s"&gt;keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}&lt;/span&gt;
          &lt;span class="s"&gt;storeFile=keystore.jks&lt;/span&gt;
          &lt;span class="s"&gt;EOF&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Shorebird&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shorebirdtech/setup-shorebird@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Shorebird Patch (Android)&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shorebirdtech/shorebird-patch@v1&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shorebird-patch&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;android&lt;/span&gt;
          &lt;span class="na"&gt;release-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.inputs.release-version || 'latest' }}&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--allow-asset-diffs --allow-native-diffs&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SHOREBIRD_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SHOREBIRD_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print Patch Number&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Patch number ${{ steps.shorebird-patch.outputs.patch-number }}"&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full release on &lt;a href="https://gist.github.com/techwithsam/e3ff26a6e12cde347e90158572e216c9#file-shorebird-patch-yml" rel="noopener noreferrer"&gt;GitHub Gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Trigger this one on hotfix tags, for example: v1.0.0-hotfix.1&lt;/p&gt;

&lt;p&gt;The structure is almost identical to the release workflow, but we use the shorebird-patch action instead.&lt;/p&gt;

&lt;p&gt;Important points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A patch can only be applied to an existing release&lt;/li&gt;
&lt;li&gt;Use the — staging flag first so you can preview the update&lt;/li&gt;
&lt;li&gt;After testing with shorebird preview — track=staging, promote it to production from the Shorebird Console.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the real magic. Users get the fix over-the-air (OTA) in minutes. No waiting for review. No forcing them to download a new version from the store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended Development Workflow
&lt;/h2&gt;

&lt;p&gt;Here’s the clean workflow I recommend (and the one Shorebird documents):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Work on features and fixes on feature branches&lt;/li&gt;
&lt;li&gt;Open a pull request into main&lt;/li&gt;
&lt;li&gt;CI runs formatting, analysis, and tests&lt;/li&gt;
&lt;li&gt;Squash and merge into main (main stays always releasable)&lt;/li&gt;
&lt;li&gt;When you’re ready for a full release → create a tag like v1.2.0 → the release workflow runs automatically&lt;/li&gt;
&lt;li&gt;If a critical bug appears → fix it on main → cherry-pick into the release branch → create a hotfix tag like v1.2.0-hotfix.1 → the patch workflow runs&lt;/li&gt;
&lt;li&gt;Preview on staging → promote to production&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This keeps everything automated, clean, and low-risk.&lt;/p&gt;

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

&lt;p&gt;A few important tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always pin the Flutter version on releases using — flutter-version&lt;/li&gt;
&lt;li&gt;Use — dry-run on pull requests so you catch build issues early without creating real releases or patches&lt;/li&gt;
&lt;li&gt;Keep your SHOREBIRD_TOKEN secure — never hardcode it&lt;/li&gt;
&lt;li&gt;For iOS environments without certificates, you can use — no-codesign (then sign later)&lt;/li&gt;
&lt;li&gt;Shorebird automatically runs in non-interactive mode in CI when the token is present&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow these, and your pipeline will be solid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;All of this is based on the official Shorebird documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.shorebird.dev/code-push/ci/github/" rel="noopener noreferrer"&gt;GitHub Integration guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.shorebird.dev/code-push/ci/generic/" rel="noopener noreferrer"&gt;Generic CI guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.shorebird.dev/code-push/guides/development-workflow/" rel="noopener noreferrer"&gt;Development Workflow guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m also a &lt;a href="https://shorebird.dev/blog/meet-our-first-shorebird-community-ambassadors#:~:text=Samuel%20Adekunle%20(Nigeria)" rel="noopener noreferrer"&gt;Shorebird Ambassador&lt;/a&gt;, so if you have questions, drop them in the comments.&lt;/p&gt;




&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;You now have a complete automated system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full releases go through the stores as usual&lt;/li&gt;
&lt;li&gt;Critical updates and bug fixes go live to users in minutes via Shorebird&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No more waiting for review every single time.&lt;/p&gt;

&lt;p&gt;If this was useful, hit the like button, subscribe, and turn on notifications so you don’t miss the next post.&lt;/p&gt;

&lt;p&gt;If you want personalized help setting this up for your project, you can send a DM.&lt;/p&gt;

&lt;p&gt;I’ll see you in the next one. Peace.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>techwithsam</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Wed, 02 Sep 2026 10:19:00 +0000</pubDate>
      <link>https://dev.to/techwithsam/-22fc</link>
      <guid>https://dev.to/techwithsam/-22fc</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-production-ai-engineering-in-flutter-120i" class="crayons-story__hidden-navigation-link"&gt;AI Engineering for Flutter Developers - Production AI Engineering in&amp;nbsp;Flutter&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/QAlmuHETFJ8" title="AI Engineering for Flutter Developers - Production AI Engineering in&amp;nbsp;Flutter"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/techwithsam" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" alt="techwithsam profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/techwithsam" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Samuel Adekunle
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Samuel Adekunle
                
                
              
              &lt;div id="story-author-preview-content-4554874" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/techwithsam" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Samuel Adekunle&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-production-ai-engineering-in-flutter-120i" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 2&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-production-ai-engineering-in-flutter-120i" id="article-link-4554874"&gt;
          AI Engineering for Flutter Developers - Production AI Engineering in&amp;nbsp;Flutter
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gemini"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gemini&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/flutter"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;flutter&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/techwithsam"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;techwithsam&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-production-ai-engineering-in-flutter-120i" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-production-ai-engineering-in-flutter-120i#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>AI Engineering for Flutter Developers - Production AI Engineering in Flutter</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Wed, 02 Sep 2026 10:18:03 +0000</pubDate>
      <link>https://dev.to/techwithsam/ai-engineering-for-flutter-developers-production-ai-engineering-in-flutter-120i</link>
      <guid>https://dev.to/techwithsam/ai-engineering-for-flutter-developers-production-ai-engineering-in-flutter-120i</guid>
      <description>&lt;p&gt;Over the past three articles, we've gone from fundamentals to building more reliable AI features to creating multi-agent workflows.&lt;/p&gt;

&lt;p&gt;But there's still one big gap most tutorials never cover:&lt;br&gt;
"How do you take an AI feature from '&lt;em&gt;it works on my machine&lt;/em&gt;' to something that's actually ready for production?"&lt;/p&gt;

&lt;p&gt;That's what this article is all about.&lt;/p&gt;

&lt;p&gt;We're focusing on &lt;strong&gt;Performance, Privacy, Security, and real-world Best Practices&lt;/strong&gt; - the things that separate demos from production-grade AI Engineering.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Production AI Engineering Really&amp;nbsp;Means
&lt;/h2&gt;

&lt;p&gt;Shipping a reliable, safe, and maintainable AI feature is a different skill.&lt;/p&gt;

&lt;p&gt;Production AI Engineering means thinking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How fast it feels to the user&lt;/li&gt;
&lt;li&gt;What data leaves the device&lt;/li&gt;
&lt;li&gt;How secure your implementation is&lt;/li&gt;
&lt;li&gt;How you handle failures&lt;/li&gt;
&lt;li&gt;How you monitor and improve the system over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll walk through the most important considerations every Flutter developer should know before shipping AI features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9feas9h2ou6mxrefydpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9feas9h2ou6mxrefydpg.png" alt="5 Pillars of Production AI" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Users are very sensitive to latency. A slow AI response can make even a smart feature feel broken.&lt;/p&gt;

&lt;p&gt;Key things to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On-device vs Cloud trade-offs&lt;/li&gt;
&lt;li&gt;When streaming actually helps&lt;/li&gt;
&lt;li&gt;Avoiding unnecessary AI calls&lt;/li&gt;
&lt;li&gt;Caching results when appropriate&lt;/li&gt;
&lt;li&gt;Choosing the right model size for the job&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4xqdbwuuszkm1p0df8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw4xqdbwuuszkm1p0df8z.png" alt="On-Device AI vs. Cloud AI" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Privacy Best Practices
&lt;/h2&gt;

&lt;p&gt;Not every piece of user data should be sent to an external AI model.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data Minimization:&lt;/strong&gt; Send only the minimal text snippet the prompt actually needs. Never dump an entire database record into an LLM call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local PII Sanitization:&lt;/strong&gt; Strip emails, phone numbers, credit-card numbers, and home addresses before sending prompts to external APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On-Device First for Sensitive Data:&lt;/strong&gt; Health logs, financial notes, and private messages should stay on-device via TFLite or local embedding models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit User Transparency:&lt;/strong&gt; Inform users whenever AI processes their input — and provide an opt-out toggle in Settings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is to make responsible decisions about what data leaves the user's phone.&lt;/p&gt;
&lt;h2&gt;
  
  
  Security Best Practices
&lt;/h2&gt;

&lt;p&gt;This is an area where many developers make critical mistakes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;1.&lt;/span&gt; Pitfall: Committing GEMINI_API_KEY = "AIzaSy…" into lib/api_config.dart
Fix: Pass keys at build time via  --dart-define=GEMINI_API_KEY=…
or store them in server-side environment variables. 
&lt;span class="p"&gt;
2.&lt;/span&gt; Pitfall: Treating user input as trusted when building the prompt.
Fix: Treat user input as untrusted data.
Delimiter: &lt;span class="nt"&gt;&amp;lt;user_input&amp;gt;&lt;/span&gt;…&lt;span class="nt"&gt;&amp;lt;/user_input&amp;gt;&lt;/span&gt;  +  enforce JSON Schema outputs.
&lt;span class="p"&gt;
3.&lt;/span&gt; Pitfall: No per-user rate-limiting on the AI endpoint.
Fix: Enforce per-user quota (e.g., 20 AI req / user / hr)
on the backend or Firebase Cloud Functions to stop wallet-depletion attacks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reliability, Monitoring &amp;amp; Evaluation
&lt;/h2&gt;

&lt;p&gt;Even with good performance, privacy, and security, your AI feature still needs to be reliable.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structured Exception Handling:&lt;/strong&gt; Differentiate rate-limits (HTTP 429), timeouts, safety refusals, and JSON-parse errors with a custom AIException class.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graceful Fallbacks:&lt;/strong&gt; If the cloud model fails after retries, drop to a cached result or an on-device score - never crash, never show a raw stack trace.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt Versioning:&lt;/strong&gt; Track system-prompt revisions in git (e.g., prompts/summarizer_v2.1.txt), evaluable and roll-back-able.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Latency &amp;amp; Cost Telemetry:&lt;/strong&gt; Track token usage and end-to-end response times in Firebase Analytics / Datadog / Sentry.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These practices help you understand how your AI features perform in the real world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Checklist &amp;amp; Architecture Recommendations
&lt;/h2&gt;

&lt;p&gt;Run through every box before shipping to the App Store / Play Store.&lt;/p&gt;

&lt;p&gt;These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API Security&lt;/li&gt;
&lt;li&gt;Key Restrictions&lt;/li&gt;
&lt;li&gt;Backend Validation&lt;/li&gt;
&lt;li&gt;Prompt Injection Defense&lt;/li&gt;
&lt;li&gt;Rate Limiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;… and many more&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways &amp;amp; Series&amp;nbsp;Summary
&lt;/h2&gt;

&lt;p&gt;As we close out this series, here are the biggest lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI Engineering is about much more than writing good prompts&lt;/li&gt;
&lt;li&gt;Reliability, privacy, and security are first-class concerns&lt;/li&gt;
&lt;li&gt;Good architecture makes AI features maintainable&lt;/li&gt;
&lt;li&gt;Start simple, but design with production in mind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've followed this series from the beginning, you now have a solid foundation for building thoughtful AI features in Flutter.&lt;/p&gt;

&lt;p&gt;-&lt;/p&gt;

&lt;p&gt;You can download the &lt;strong&gt;AI Engineering Starter Pack&lt;/strong&gt; with checklists, patterns, and guidance: &lt;a href="https://techwithsam.dev/production-ai-engineering-in-flutter" rel="noopener noreferrer"&gt;techwithsam.dev/production-ai-engineering-in-flutter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This brings the AI Engineering for Flutter Developers series to a close.&lt;/p&gt;

&lt;p&gt;Thank you for following along.&lt;/p&gt;

&lt;p&gt;If this series helped you, I'd really appreciate a like and a comment.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and I'll see you in the next one.&lt;/p&gt;

&lt;p&gt;Take care!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>flutter</category>
      <category>techwithsam</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Fri, 28 Aug 2026 11:33:18 +0000</pubDate>
      <link>https://dev.to/techwithsam/-58k3</link>
      <guid>https://dev.to/techwithsam/-58k3</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/techwithsam/clean-architecture-in-flutter-2026-practical-implementation-guide-1dfb" class="crayons-story__hidden-navigation-link"&gt;Clean Architecture in Flutter 2026 - Practical Implementation Guide&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/itShRNhyCN8" title="Clean Architecture in Flutter 2026 - Practical Implementation Guide"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/techwithsam" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" alt="techwithsam profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/techwithsam" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Samuel Adekunle
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Samuel Adekunle
                
                
              
              &lt;div id="story-author-preview-content-3633547" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/techwithsam" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Samuel Adekunle&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/techwithsam/clean-architecture-in-flutter-2026-practical-implementation-guide-1dfb" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 8&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/techwithsam/clean-architecture-in-flutter-2026-practical-implementation-guide-1dfb" id="article-link-3633547"&gt;
          Clean Architecture in Flutter 2026 - Practical Implementation Guide
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/flutter"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;flutter&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cleancode"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cleancode&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mobile"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mobile&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/techwithsam"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;techwithsam&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/techwithsam/clean-architecture-in-flutter-2026-practical-implementation-guide-1dfb" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;12&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/techwithsam/clean-architecture-in-flutter-2026-practical-implementation-guide-1dfb#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Thu, 27 Aug 2026 16:33:40 +0000</pubDate>
      <link>https://dev.to/techwithsam/-2p6i</link>
      <guid>https://dev.to/techwithsam/-2p6i</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-ai-agents-workflows-in-flutter-1cc7" class="crayons-story__hidden-navigation-link"&gt;AI Engineering for Flutter Developers - AI Agents &amp;amp; Workflows in&amp;nbsp;Flutter&lt;/a&gt;
    &lt;div class="crayons-article__cover crayons-article__cover__image__feed"&gt;
      &lt;iframe src="https://www.youtube.com/embed/D0s2X0_dvB0" title="AI Engineering for Flutter Developers - AI Agents &amp;amp; Workflows in&amp;nbsp;Flutter"&gt;&lt;/iframe&gt;
    &lt;/div&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/techwithsam" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" alt="techwithsam profile" class="crayons-avatar__image" width="800" height="1097"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/techwithsam" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Samuel Adekunle
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Samuel Adekunle
                
                
              
              &lt;div id="story-author-preview-content-4506461" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/techwithsam" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F283838%2Faf3610bc-683f-4e9d-8543-3f2117644325.jpg" class="crayons-avatar__image" alt="" width="800" height="1097"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Samuel Adekunle&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-ai-agents-workflows-in-flutter-1cc7" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 27&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-ai-agents-workflows-in-flutter-1cc7" id="article-link-4506461"&gt;
          AI Engineering for Flutter Developers - AI Agents &amp;amp; Workflows in&amp;nbsp;Flutter
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/flutter"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;flutter&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/techwithsamw"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;techwithsamw&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-ai-agents-workflows-in-flutter-1cc7" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/techwithsam/ai-engineering-for-flutter-developers-ai-agents-workflows-in-flutter-1cc7#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>AI Engineering for Flutter Developers - AI Agents &amp; Workflows in Flutter</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Thu, 27 Aug 2026 16:32:48 +0000</pubDate>
      <link>https://dev.to/techwithsam/ai-engineering-for-flutter-developers-ai-agents-workflows-in-flutter-1cc7</link>
      <guid>https://dev.to/techwithsam/ai-engineering-for-flutter-developers-ai-agents-workflows-in-flutter-1cc7</guid>
      <description>&lt;p&gt;In the first two articles, we covered the fundamentals and how to build more reliable AI features using structured output, streaming, and proper error handling.&lt;/p&gt;

&lt;p&gt;But there's a big limitation with single AI calls: they struggle with complex, multi-step tasks.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;AI Agents&lt;/strong&gt; come in.&lt;/p&gt;

&lt;p&gt;Today, we're going to build a simple but powerful multi-agent system in Flutter — specifically, a &lt;strong&gt;Planning Agent&lt;/strong&gt;, a &lt;strong&gt;Generation Agent&lt;/strong&gt;, and a &lt;strong&gt;Validation Agent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By the end of this video, you'll understand how to design agentic workflows and implement them in real Flutter applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are AI&amp;nbsp;Agents?
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;AI Agent&lt;/strong&gt; is more like giving someone a goal and letting them think, plan, and take multiple steps to achieve it, &lt;strong&gt;WHILE&lt;/strong&gt; a regular &lt;strong&gt;LLM&lt;/strong&gt; call is like asking a smart person one question and getting one answer.&lt;/p&gt;

&lt;p&gt;We'll focus on a simple and practical multi-agent pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Planning Agent&lt;/strong&gt; → Breaks the user request into clear steps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation Agent&lt;/strong&gt; → Executes the plan and creates the output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation Agent&lt;/strong&gt; → Reviews the result and checks for quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is extremely useful for real applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the&amp;nbsp;Workflow
&lt;/h2&gt;

&lt;p&gt;We'll build a simple Smart Feature Builder,&lt;/p&gt;

&lt;p&gt;The user gives a high level request like: 'Create a clean Flutter logins creen with email and password validation.'&lt;/p&gt;

&lt;p&gt;Then the system works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Planning Agent creates a structured plan&lt;/li&gt;
&lt;li&gt;The Generation Agent turns that plan into actual code or a UI description&lt;/li&gt;
&lt;li&gt;The Validation Agent reviews the output and suggests improvements&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Building the Planning&amp;nbsp;Agent
&lt;/h2&gt;

&lt;p&gt;The goal of this agent is to take a vague user request and turn it into a clear, structured plan.&lt;/p&gt;

&lt;p&gt; - we define the role and system prompt for the Planning Agent&lt;br&gt;
 - we use structured output so we can get a clean list of steps&lt;br&gt;
 - then we create a Dart model for the plan&lt;br&gt;
 - finally, call the agent and display the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:googleai_dart/googleai_dart.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'../models/feature_plan.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// Stage 1: Planning Agent&lt;/span&gt;
&lt;span class="c1"&gt;/// Takes raw user feature requests and converts them into a structured architectural plan via Gemini API.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlanningAgent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;PlanningAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'gemini-3.1-flash-lite'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_systemInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
You are a Senior Flutter Software Architect.
Your role is to take a high-level mobile/web feature request and generate a clean, structured architectural plan.

You must respond ONLY with a valid JSON object matching this exact schema:
{
  "title": "String - Concise title of the feature plan",
  "overview": "String - Executive summary of the architectural strategy",
  "targetPlatform": "String - Target platforms (e.g. Flutter Web, iOS &amp;amp; Android)",
  "componentsToBuild": ["List of String - Individual UI components, widgets, or services to build"],
  "implementationSteps": ["List of String - Sequential step-by-step engineering tasks"],
  "riskConsiderations": ["List of String - Potential state management, security, performance or edge case risks"]
}
'''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FeaturePlan&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;planFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;StateError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Gemini API Key is missing. Please configure your API key before running the pipeline.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;authProvider:&lt;/span&gt; &lt;span class="n"&gt;ApiKeyProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;config:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GenerateContentRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;contents:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Feature Request:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;$prompt&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
      &lt;span class="nl"&gt;systemInstruction:&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_systemInstruction&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;generationConfig:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;GenerationConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;responseMimeType:&lt;/span&gt; &lt;span class="s"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;temperature:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;model:&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Planning Agent returned empty response from Gemini API.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;jsonMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FeaturePlan&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the Generation Agent
&lt;/h2&gt;

&lt;p&gt;This agent takes the structured plan and actually produces the output, in our case, Flutter-related code/content suggestions.&lt;/p&gt;

&lt;p&gt; - first, we create the Generation Agent with a clear role&lt;br&gt;
 - then pass the plan from the previous agent&lt;br&gt;
 - generate the final output&lt;br&gt;
 - show how we keep the response structured and useful&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:googleai_dart/googleai_dart.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'../models/feature_plan.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'../models/generated_feature_code.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// Stage 2: Generation Agent&lt;/span&gt;
&lt;span class="c1"&gt;/// Takes the architectural FeaturePlan and user request, then generates complete, production-ready Flutter code via Gemini API.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenerationAgent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;GenerationAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'gemini-3.1-flash-lite'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_systemInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
You are an expert Lead Flutter Engineer.
Your task is to take an Architectural Feature Plan and write production-ready, clean, well-formatted Flutter Dart code.

You must respond ONLY with a valid JSON object matching this exact schema:
{
  "title": "String - Title of the generated feature code",
  "explanation": "String - Clear explanation of key implementation patterns and Dart features used",
  "flutterCode": "String - Complete, fully functioning, syntactically correct Flutter Widget Dart code",
  "dependencies": ["List of String - Required packages in pubspec.yaml (e.g. google_fonts, flutter_animate)"]
}
'''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GeneratedFeatureCode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;generateCode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;FeaturePlan&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;StateError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Gemini API Key is missing. Please configure your API key before running the pipeline.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;authProvider:&lt;/span&gt; &lt;span class="n"&gt;ApiKeyProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;config:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;promptInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
User Feature Request:
&lt;/span&gt;&lt;span class="si"&gt;$userPrompt&lt;/span&gt;&lt;span class="s"&gt;

Architectural Plan:
&lt;/span&gt;&lt;span class="si"&gt;${plan.toFormattedJson()}&lt;/span&gt;&lt;span class="s"&gt;
'''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GenerateContentRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;contents:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;promptInput&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
      &lt;span class="nl"&gt;systemInstruction:&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_systemInstruction&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;generationConfig:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;GenerationConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;responseMimeType:&lt;/span&gt; &lt;span class="s"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;temperature:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;model:&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Generation Agent returned empty response from Gemini API.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;jsonMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;GeneratedFeatureCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the Validation Agent
&lt;/h2&gt;

&lt;p&gt;This agent acts like a reviewer. It checks the output from the Generation Agent for issues, missing pieces, or areas for improvement.&lt;/p&gt;

&lt;p&gt; - again, we create the Validation Agent&lt;br&gt;
 - feed it the original request + the generated output&lt;br&gt;
 - get structured feedback&lt;br&gt;
 - optionally show a simple improvement loop&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:googleai_dart/googleai_dart.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'../models/feature_plan.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'../models/generated_feature_code.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'../models/validation_report.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// Stage 3: Validation Agent&lt;/span&gt;
&lt;span class="c1"&gt;/// Reviews generated code against the original user prompt and architectural plan via Gemini API.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ValidationAgent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;ValidationAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'gemini-3.1-flash-lite'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_systemInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
You are a Lead Code Auditor and Security Reviewer for Flutter applications.
Your job is to audit generated Flutter code against the user request and architectural plan.

You must evaluate code quality, completeness, error handling, accessibility, and performance.
You must respond ONLY with a valid JSON object matching this exact schema:
{
  "qualityScore": 92, // Integer between 0 and 100
  "isPassed": true, // Boolean - true if qualityScore &amp;gt;= 75
  "identifiedIssues": ["List of String - Specific flaws, missing validations, or anti-patterns"],
  "improvementSuggestions": ["List of String - Actionable code quality or performance recommendations"],
  "refinedCodeSnippet": "String or Null - Improved/corrected snippet fixing identified issues"
}
'''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ValidationReport&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;validateOutput&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;FeaturePlan&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;GeneratedFeatureCode&lt;/span&gt; &lt;span class="n"&gt;generatedCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;StateError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Gemini API Key is missing. Please configure your API key before running the pipeline.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;authProvider:&lt;/span&gt; &lt;span class="n"&gt;ApiKeyProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;config:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;promptInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
Original Request:
&lt;/span&gt;&lt;span class="si"&gt;$userPrompt&lt;/span&gt;&lt;span class="s"&gt;

Architectural Plan:
&lt;/span&gt;&lt;span class="si"&gt;${plan.toFormattedJson()}&lt;/span&gt;&lt;span class="s"&gt;

Generated Code to Audit:
&lt;/span&gt;&lt;span class="si"&gt;${generatedCode.toFormattedJson()}&lt;/span&gt;&lt;span class="s"&gt;
'''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GenerateContentRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;contents:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;promptInput&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
      &lt;span class="nl"&gt;systemInstruction:&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_systemInstruction&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;generationConfig:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;GenerationConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;responseMimeType:&lt;/span&gt; &lt;span class="s"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;temperature:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;model:&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Validation Agent returned empty response from Gemini API.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;jsonMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ValidationReport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting It All&amp;nbsp;Together
&lt;/h2&gt;

&lt;p&gt;We need to connect everything into one workflow.&lt;/p&gt;

&lt;p&gt;We will create a simple orchestrator that:&lt;br&gt;
&amp;nbsp;1. Calls the Planning Agent&lt;br&gt;
&amp;nbsp;2. Passes the plan to the Generation Agent&lt;br&gt;
&amp;nbsp;3. Sends the result to the Validation Agent&lt;br&gt;
&amp;nbsp;4. Displays the final output and feedback&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// Orchestrator Service&lt;/span&gt;
&lt;span class="c1"&gt;/// Coordinates the multi-agent pipeline: Planning -&amp;gt; Generation -&amp;gt; Validation&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentOrchestrationService&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ValueNotifier&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AgentWorkflowState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;runWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Orchestrator'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Workflow started for prompt: "&lt;/span&gt;&lt;span class="si"&gt;$prompt&lt;/span&gt;&lt;span class="s"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// -----------------------------------------------------------------------&lt;/span&gt;
      &lt;span class="c1"&gt;// STAGE 1: Planning Agent&lt;/span&gt;
      &lt;span class="c1"&gt;// -----------------------------------------------------------------------&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Planning Agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Analyzing request &amp;amp; composing architectural plan...'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;planningAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PlanningAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;apiKey:&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;planningAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;planFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;stage:&lt;/span&gt; &lt;span class="n"&gt;AgentStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;plan:&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Planning Agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Plan generated successfully: "&lt;/span&gt;&lt;span class="si"&gt;${plan.title}&lt;/span&gt;&lt;span class="s"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toFormattedJson&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="c1"&gt;// -----------------------------------------------------------------------&lt;/span&gt;
      &lt;span class="c1"&gt;// STAGE 2: Generation Agent&lt;/span&gt;
      &lt;span class="c1"&gt;// -----------------------------------------------------------------------&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Generation Agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Writing Flutter code &amp;amp; module dependencies...'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;generationAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GenerationAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;apiKey:&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;generatedCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;generationAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;userPrompt:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;plan:&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;stage:&lt;/span&gt; &lt;span class="n"&gt;AgentStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;generatedCode:&lt;/span&gt; &lt;span class="n"&gt;generatedCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Generation Agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Code generated successfully (&lt;/span&gt;&lt;span class="si"&gt;${generatedCode.flutterCode.length}&lt;/span&gt;&lt;span class="s"&gt; chars)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;generatedCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toFormattedJson&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="c1"&gt;// -----------------------------------------------------------------------&lt;/span&gt;
      &lt;span class="c1"&gt;// STAGE 3: Validation Agent&lt;/span&gt;
      &lt;span class="c1"&gt;// -----------------------------------------------------------------------&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Validation Agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Auditing code quality, security &amp;amp; accessibility...'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;validationAgent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ValidationAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;apiKey:&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;validationReport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;validationAgent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;validateOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;userPrompt:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;plan:&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;generatedCode:&lt;/span&gt; &lt;span class="n"&gt;generatedCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;_tickerTimer&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;stage:&lt;/span&gt; &lt;span class="n"&gt;AgentStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;validationReport:&lt;/span&gt; &lt;span class="n"&gt;validationReport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Validation Agent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Audit complete. Quality Score: &lt;/span&gt;&lt;span class="si"&gt;${validationReport.qualityScore}&lt;/span&gt;&lt;span class="s"&gt;/100 (Passed: &lt;/span&gt;&lt;span class="si"&gt;${validationReport.isPassed}&lt;/span&gt;&lt;span class="s"&gt;)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;payload:&lt;/span&gt; &lt;span class="n"&gt;validationReport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toFormattedJson&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Orchestrator'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Multi-Agent pipeline executed successfully!'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_tickerTimer&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;_addLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Orchestrator'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'Pipeline failed: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;stage:&lt;/span&gt; &lt;span class="n"&gt;AgentStage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;error:&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full source code is on GitHub: &lt;a href="https://github.com/techwithsam/ai_engineer_for_flutter_devs/tree/video-3" rel="noopener noreferrer"&gt;github.com/techwithsam/ai_engineer_for_flutter_devs/tree/video-3&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Quick summary of what we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI Agents allow us to handle multi-step tasks much better than single prompts&lt;/li&gt;
&lt;li&gt;The Planning → Generation → Validation pattern is simple but powerful&lt;/li&gt;
&lt;li&gt;Structured output is critical when agents need to communicate&lt;/li&gt;
&lt;li&gt;Keeping the architecture clean makes these systems maintainable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a foundational pattern you can expand in many directions.&lt;/p&gt;




&lt;p&gt;If you haven't already, download the free &lt;strong&gt;AI Engineering Starter Pack&lt;/strong&gt; - I've updated it with the code and patterns from this article.&lt;/p&gt;

&lt;p&gt;Here: &lt;a href="https://techwithsam.dev/ai-starter-kit-3" rel="noopener noreferrer"&gt;techwithsam.dev/ai-starter-kit-3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just enter your email, and it will be sent to you instantly.&lt;/p&gt;




&lt;p&gt;In the next and final video of this series, we'll focus on &lt;strong&gt;Production AI Engineering&lt;/strong&gt; - covering performance, privacy, security, evaluation, and best practices for shipping AI features in real Flutter apps.&lt;/p&gt;

&lt;p&gt;If you found this valuable, please like and drop a comment.&lt;/p&gt;

&lt;p&gt;I'd love to know: What kind of multi-agent workflow would you like to see next?&lt;/p&gt;

&lt;p&gt;Thank you for following. I'll see you in the final one.&lt;/p&gt;

&lt;p&gt;Take care!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>flutter</category>
      <category>agents</category>
      <category>techwithsamw</category>
    </item>
    <item>
      <title>AI Engineering for Flutter Developers — Building Reliable AI Features in Flutter</title>
      <dc:creator>Samuel Adekunle</dc:creator>
      <pubDate>Wed, 19 Aug 2026 16:13:10 +0000</pubDate>
      <link>https://dev.to/techwithsam/ai-engineering-for-flutter-developers-building-reliable-ai-features-in-flutter-54d8</link>
      <guid>https://dev.to/techwithsam/ai-engineering-for-flutter-developers-building-reliable-ai-features-in-flutter-54d8</guid>
      <description>&lt;p&gt;Building a reliable AI feature that works well in production is not so easy; it's a completely different skill.&lt;/p&gt;

&lt;p&gt;For this part 2, we're going to focus on three critical skills every AI Engineer needs, i.e&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mastering Structured AI Outputs&lt;/li&gt;
&lt;li&gt;Real-Time Streaming&lt;/li&gt;
&lt;li&gt;Building a Robust Error Handling System&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, we will know how to build AI features that are cleaner, more predictable, and much more enterprise-ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Learn in This&amp;nbsp;Part:&amp;nbsp;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why free-text AI responses are dangerous to production Flutter Apps&amp;nbsp;&lt;/li&gt;
&lt;li&gt;How to force structured, typed responses using Gemini&amp;nbsp;&lt;/li&gt;
&lt;li&gt;How to implement streaming for better user experience in Flutter&amp;nbsp;&lt;/li&gt;
&lt;li&gt;How to handle AI failures gracefully in Flutter&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Structured Output
&lt;/h2&gt;

&lt;p&gt;Let's start with Structured Output.&lt;/p&gt;

&lt;p&gt;When you ask an AI model a question, by default, it returns free text (plain text), which is flexible but dangerous and hard to work with in real applications.&lt;/p&gt;

&lt;p&gt;Structured Output allows the model to return clean, predictable data, usually in JSON format, just like when we are integrating an endpoint.&lt;/p&gt;

&lt;p&gt;Here is an example of how to do this properly with Gemini in Flutter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ArticleBlueprint&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;generateStructuredBlueprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;topicPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="kt"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;onRetry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RetryHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retryWithBackoff&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ArticleBlueprint&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;onRetry:&lt;/span&gt; &lt;span class="n"&gt;onRetry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;maxAttempts:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;action:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;GoogleAIClient&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// 1. Initialize client using googleai_dart&lt;/span&gt;
          &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;config:&lt;/span&gt; &lt;span class="n"&gt;GoogleAIConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;authProvider:&lt;/span&gt; &lt;span class="n"&gt;ApiKeyProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="c1"&gt;// 2. Build Structured JSON prompt enforcing strict schema&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;systemPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'''
You are a technical content architect. Generate a structured JSON blueprint for a technical article or feature guide.

STRICT JSON SCHEMA REQUIREMENT:
Return ONLY a valid JSON object with the following fields:
- "title": String (compelling article title)
- "overview": String (concise 2-3 sentence overview)
- "difficulty": String ("Beginner", "Intermediate", or "Advanced")
- "estimatedReadingMinutes": Integer
- "tags": Array of Strings
- "keyConcepts": Array of Strings (up to 4 bullet points)
- "implementationSteps": Array of Strings (step-by-step implementation guide)
- "codeSnippet": String (short code example)

Topic to generate blueprint for:
"&lt;/span&gt;&lt;span class="si"&gt;$topicPrompt&lt;/span&gt;&lt;span class="s"&gt;"
'''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="c1"&gt;// 3. Make API request using client.models.generateContent&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;model:&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;GenerateContentRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;contents:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;parts:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TextPart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
                  &lt;span class="nl"&gt;role:&lt;/span&gt; &lt;span class="s"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="c1"&gt;// 4. Extract generated text payload&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;candidates&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;firstOrNull&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;finishReason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;FinishReason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;safety&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
              &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;finishReason&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'safety'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SafetyRefusalAIException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s"&gt;'The requested topic was flagged by Gemini safety filters.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;rawText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;whereType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TextPart&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SchemaParsingAIException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s"&gt;'Received empty output from Gemini model.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;rawOutput:&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="c1"&gt;// 5. Clean markdown code blocks &amp;amp; parse JSON&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cleanJsonText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_cleanMarkdownJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;jsonMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleanJsonText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

          &lt;span class="c1"&gt;// 6. Deserialize into strongly-typed Dart model&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ArticleBlueprint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rawText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;_translateException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This alone will make the AI feature more reliable.&lt;/p&gt;
&lt;h2&gt;
  
  
  Streaming Responses
&lt;/h2&gt;

&lt;p&gt;Next up is Real-Time Streaming&lt;/p&gt;

&lt;p&gt;Nobody likes staring at a loading spinner while waiting for a long AI response. With streaming, we can let the text appear gradually, just like a typewriter, which feels much more natural and responsive.&lt;/p&gt;

&lt;p&gt;Let's implement streaming with Gemini in Flutter&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// Token Streaming (`streamGenerateContent`).&lt;/span&gt;
  &lt;span class="c1"&gt;///&lt;/span&gt;
  &lt;span class="c1"&gt;/// Yields incremental text tokens as they arrive from Gemini in real-time.&lt;/span&gt;
  &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;streamTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;GoogleAIClient&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;config:&lt;/span&gt; &lt;span class="n"&gt;GoogleAIConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;authProvider:&lt;/span&gt; &lt;span class="n"&gt;ApiKeyProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;streamGenerateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;model:&lt;/span&gt; &lt;span class="n"&gt;modelName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;GenerateContentRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;contents:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;parts:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TextPart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
              &lt;span class="nl"&gt;role:&lt;/span&gt; &lt;span class="s"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;candidates&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;firstOrNull&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;whereType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TextPart&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;_translateException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As simple as that…&lt;/p&gt;
&lt;h2&gt;
  
  
  Error Handling &amp;amp; Resilience
&lt;/h2&gt;

&lt;p&gt;This is the part most devs skip, and it's one of the reasons many AI features break in production.&lt;/p&gt;

&lt;p&gt;AI calls can fail for many reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network issues&lt;/li&gt;
&lt;li&gt;Rate limits&lt;/li&gt;
&lt;li&gt;Invalid responses&lt;/li&gt;
&lt;li&gt;Timeouts&lt;/li&gt;
&lt;li&gt;Model refusing the request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's create a simulation of a proper error handling system that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear error types&lt;/li&gt;
&lt;li&gt;Retry logic&lt;/li&gt;
&lt;li&gt;Meaningful feedback to the user&lt;/li&gt;
&lt;li&gt;Fallback behaviour
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// DEMO HELPER: Simulates intentional fault cases.&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ArticleBlueprint&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;simulateFault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;faultType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faultType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;'429_rate_limit'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;RateLimitAIException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s"&gt;'HTTP 429: Too Many Requests. Gemini rate limit reached.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;retryAfter:&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seconds:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;'network_timeout'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;NetworkAIException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s"&gt;'SocketException: Connection timed out while reaching api.generativeai.google'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;'schema_invalid'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SchemaParsingAIException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s"&gt;'FormatException: Missing mandatory "title" key in JSON payload.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;rawOutput:&lt;/span&gt; &lt;span class="s"&gt;'{"overview": "Broken JSON example without title"}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;'safety_refusal'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SafetyRefusalAIException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s"&gt;'Prompt blocked by Gemini Safety Classifier Policy.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;UnknownAIException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Simulated unknown exception.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we can combine everything we've learned into a more complete example.&lt;/p&gt;

&lt;p&gt;Source code on GitHub&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/techwithsam" rel="noopener noreferrer"&gt;
        techwithsam
      &lt;/a&gt; / &lt;a href="https://github.com/techwithsam/ai_engineer_for_flutter_devs" rel="noopener noreferrer"&gt;
        ai_engineer_for_flutter_devs
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      AI Engineering for Flutter Devs 2026 - TechWithSam
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;AI Engineering for Flutter Developers – Smart Text Analyzer&lt;/h1&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free resource from &lt;a href="https://techwithsam.dev" rel="nofollow noopener noreferrer"&gt;Tech With Sam&lt;/a&gt;&lt;/strong&gt; — companion repo for the &lt;a href="https://www.youtube.com/playlist?list=PLE9YRxI_vdvE" rel="nofollow noopener noreferrer"&gt;&lt;em&gt;AI Engineering for Flutter Developers&lt;/em&gt; YouTube series&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://flutter.dev" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0e9f29ad12595bd0d22a2c3451fa5a5bc01006f3059d4cc9c97c4ffd551f0a51/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f466c75747465722d332e782d3032353639423f6c6f676f3d666c7574746572" alt="Flutter"&gt;&lt;/a&gt;
&lt;a href="https://dart.dev" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c649545fa5c7257dae45f429fdb519a19f00a3c500619e425a2545c4e9418dc7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446172742d332e782d3031373543323f6c6f676f3d64617274" alt="Dart"&gt;&lt;/a&gt;
&lt;a href="https://pub.dev/packages/googleai_dart" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9d87a9978342ba7d9de931279f21da8f1c196536285713eb065841479ba7dbe8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f476f6f676c6525323041492d676f6f676c6561695f5f646172742d3432383546343f6c6f676f3d676f6f676c65" alt="Google AI"&gt;&lt;/a&gt;
&lt;a href="https://pub.dev/packages/tflite_flutter" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fa1492f3327b8feebdc2ea6f50a31f21f5df48628a78db6f7bdab9c23b14d8b8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f54464c6974652d74666c6974655f5f666c75747465722d4646364630303f6c6f676f3d74656e736f72666c6f77" alt="TensorFlow Lite"&gt;&lt;/a&gt;
&lt;a href="https://github.com/techwithsam/ai_engineer_for_flutter_devs/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://www.youtube.com/playlist?list=PLE9YRxI_vdvE" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ad0854a88d38c2ef192583c5daf8db08f86e62916bfb415e2a5faee6f2e9af74/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f596f75547562652d5365726965735f506c61796c6973742d4646303030303f6c6f676f3d796f7574756265" alt="YouTube Playlist"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📦 What's in This Repo&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Folder / File&lt;/th&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/lib/services&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cloud Gemini (&lt;code&gt;GeminiService&lt;/code&gt;), On-Device TFLite (&lt;code&gt;OnDeviceClassifierService&lt;/code&gt;), &amp;amp; Resilient AI Engine (&lt;code&gt;ResilientAIService&lt;/code&gt;, &lt;code&gt;RetryHelper&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/lib/models&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Data models (&lt;code&gt;TextAnalysisResult&lt;/code&gt;), Structured Output schemas (&lt;code&gt;ArticleBlueprint&lt;/code&gt;), &amp;amp; Custom AI Exceptions (&lt;code&gt;ai_exceptions.dart&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/lib/widgets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UI components: Gemini/On-Device cards, &lt;code&gt;StreamingOutputWidget&lt;/code&gt;, &lt;code&gt;StructuredBlueprintCard&lt;/code&gt;, &amp;amp; &lt;code&gt;ErrorResilienceBanner&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/lib/theme&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dark &amp;amp; light mode brand theme system (&lt;code&gt;AppTheme&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/lib/part_two_app.dart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Video 2 entry point &amp;amp; studio screen for Structured Output, Token Streaming, and Fault Injection testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/lib/main.dart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main root app launcher coexisting across all video series modules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Quick Setup&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 1. Clone the repo&lt;/span&gt;
git clone https://github.com/techwithsam/ai_engineer_for_flutter_devs.git
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 2. Navigate into the project&lt;/span&gt;
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; ai_engineer_for_flutter_devs

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 3. Get dependencies&lt;/span&gt;
flutter&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/techwithsam/ai_engineer_for_flutter_devs" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;Today we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always prefer Structured Output when you need predictable data&lt;/li&gt;
&lt;li&gt;Use streaming to improve perceived performance&lt;/li&gt;
&lt;li&gt;Treat error handling as a priority and first-class citizen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These three practices will immediately raise the quality of any AI feature you build in Flutter.&lt;/p&gt;




&lt;p&gt;If you haven't already, download the free AI Engineering Starter Pack — I've updated it with the code and patterns from this article.&lt;/p&gt;

&lt;p&gt;Here: &lt;a href="https://techwithsam.dev/ai-starter-kit-2" rel="noopener noreferrer"&gt;techwithsam.dev/ai-starter-kit-2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just enter your email, and it will be sent to you instantly.&lt;/p&gt;




&lt;p&gt;If you found this valuable, please hit the like clap, follow, and turn on notifications so you don't miss the rest of this series.&lt;/p&gt;

&lt;p&gt;In the next release, we'll go into AI Agents and Workflows&lt;/p&gt;

&lt;p&gt;Drop a comment and tell me: What's one AI feature you want to build in your Flutter app?&lt;/p&gt;

&lt;p&gt;Thank you for following. I'll see you in the next one.&lt;/p&gt;

&lt;p&gt;Take care!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>flutter</category>
      <category>mobile</category>
      <category>techwithsam</category>
    </item>
  </channel>
</rss>
