<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ #shorebird - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ #shorebird - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:24:46 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/shorebird/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Push Silent Updates in Flutter Using Shorebird ]]>
                </title>
                <description>
                    <![CDATA[ Imagine you've just launched a major feature. Your app is climbing the charts, but then the first bug report arrives. It's a critical payment validation error. The fix is a single line of Dart code, but now you face the dreaded app store review queue... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-push-silent-updates-in-flutter-using-shorebird/</link>
                <guid isPermaLink="false">688ce407aa180d32049574ec</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ flutter-aware ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #shorebird ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Atuoha Anthony ]]>
                </dc:creator>
                <pubDate>Fri, 01 Aug 2025 15:57:59 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754063853545/725d429a-1bc1-40af-b089-3f742879a9bb.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Imagine you've just launched a major feature. Your app is climbing the charts, but then the first bug report arrives. It's a critical payment validation error. The fix is a single line of Dart code, but now you face the dreaded app store review queue, hours, maybe days of delay, all while your users and revenue are impacted.</p>
<p>What if you could bypass the store and deliver that fix directly to your users in minutes?</p>
<p>This is the power Shorebird brings to your Flutter workflow. It's not just about speed, it's about control, reliability, and delivering a superior user experience.</p>
<p>This guide will take you from a novice to a pro, covering everything from setting up Shorebird to implementing robust, production-grade update strategies with the <code>upgrader</code> package.</p>
<h3 id="heading-table-of-contents">Table of Contents:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-shorebird">What is Shorebird?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-1-the-why-when-to-patch-vs-when-to-release">1. The "Why": When to Patch vs. When to Release</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-getting-started-installation-amp-project-setup">2. Getting Started: Installation &amp; Project Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-the-core-workflow-releasing-and-patching">3. The Core Workflow: Releasing and Patching</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-taking-control-in-app-update-logic-in-flutter">4. Taking Control: In-App Update Logic in Flutter</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-the-nuclear-option-forcing-updates-with-upgrader">5. The Nuclear Option: Forcing Updates with upgrader</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-real-world-playbook-combining-shorebird-upgrader-and-remote-config">6. Real-World Playbook: Combining Shorebird, Upgrader, and Remote Config</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-7-production-grade-best-practices">7. Production-Grade Best Practices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-8-security-cost-and-limitations">8. Security, Cost, and Limitations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-references">References</a></p>
</li>
</ul>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>Before you dive in, make sure you have the following knowledge and tools set up. This will help you follow the guide smoothly and avoid common setup issues.</p>
<h4 id="heading-knowledge-and-skills">Knowledge and Skills</h4>
<ul>
<li><p><strong>Fundamental Flutter knowledge:</strong> You should be comfortable creating Flutter projects, managing packages with <code>pubspec.yaml</code>, and understanding the basic widget lifecycle. This guide assumes you have an existing Flutter application to work with.</p>
</li>
<li><p><strong>Command-line proficiency:</strong> You need to be comfortable using a terminal (like Terminal on macOS/Linux or PowerShell on Windows), as Shorebird is primarily a Command-Line Interface (CLI) tool. This includes navigating directories and executing commands.</p>
</li>
<li><p><strong>Basic Git and version control:</strong> Understanding concepts like commits, branches (<code>main</code>, <code>hotfix</code>), and checking out code is crucial for managing releases, creating patches from specific code states, and implementing rollback strategies.</p>
</li>
</ul>
<h4 id="heading-tools-and-environment">Tools and Environment</h4>
<ul>
<li><p><strong>A working Flutter development environment:</strong></p>
<ul>
<li><p>The latest stable version of the <strong>Flutter SDK</strong>.</p>
</li>
<li><p>An IDE configured for Flutter, such as <strong>VS Code</strong> or <strong>Android Studio</strong>.</p>
</li>
</ul>
</li>
<li><p><strong>A Shorebird account:</strong></p>
<ul>
<li>You’ll need to sign up for a free account on the <a target="_blank" href="https://shorebird.dev">Shorebird website</a>. The <code>shorebird login</code> command requires this.</li>
</ul>
</li>
<li><p><strong>Platform-Specific Build Tools:</strong></p>
<ul>
<li><p><strong>For Android:</strong></p>
<ul>
<li><p>An installation of <strong>Android Studio</strong> and the corresponding <strong>Android SDK</strong>.</p>
</li>
<li><p>A configured <strong>signing keystore</strong> (<code>key.jks</code>) and <code>key.properties</code> file for your project. Shorebird needs this to create signed, production-ready release builds (AABs).</p>
</li>
</ul>
</li>
<li><p><strong>For iOS:</strong></p>
<ul>
<li><p>A macOS computer with <strong>Xcode</strong> installed. Building for iOS is not possible on Windows or Linux.</p>
</li>
<li><p>An active <strong>Apple Developer Program membership</strong> is required to code sign and release iOS apps.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="heading-optional-but-recommended-for-the-full-strategy">Optional (but Recommended for the Full Strategy)</h4>
<ul>
<li><p><strong>A Firebase project:</strong> To implement the complete, real-world strategy involving forced updates and remote feature flags, you will need:</p>
<ul>
<li><p>A Firebase project linked to your Flutter app.</p>
</li>
<li><p>The <strong>Firebase Remote Config</strong> service enabled.</p>
</li>
<li><p>The <code>firebase_core</code> and <code>firebase_remote_config</code> packages added to your <code>pubspec.yaml</code>.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-what-is-shorebird">What is Shorebird?</h2>
<p>Shorebird is a code push service specifically designed for Flutter applications. It allows developers to deliver small, critical bug fixes and even new features directly to users' devices, bypassing the traditional app store review process. In essence, it enables "over-the-air" (OTA) updates for Flutter apps.</p>
<p>This means that instead of waiting hours or days for app store approval for a simple fix, you can deploy the change almost instantly, ensuring a much faster response to issues, reduced downtime for users, and minimal impact on revenue. It gives you greater control over your app's deployment lifecycle and helps maintain a smoother, more reliable user experience.</p>
<h2 id="heading-1-the-why-when-to-patch-vs-when-to-release">1. The "Why": When to Patch vs. When to Release</h2>
<p>Before diving into the "how," it's crucial to understand the "when/why." Not all updates are created equal. Using the right tool for the job is key to a stable and efficient development cycle.</p>
<h4 id="heading-use-shorebird-patch-for">Use <code>shorebird patch</code> for:</h4>
<ul>
<li><p><strong>Critical bug fixes:</strong> Logic errors in your Dart code (for example, calculation mistakes, null pointer exceptions, incorrect state management).</p>
</li>
<li><p><strong>Minor UI tweaks:</strong> Changing colors, text, or layout logic without adding new assets.</p>
</li>
<li><p><strong>Feature flag toggles:</strong> Pushing a code change that enables or disables a feature you've already built and deployed.</p>
</li>
<li><p><strong>Performance improvements:</strong> Optimizing algorithms or Dart-level rendering logic.</p>
</li>
</ul>
<h4 id="heading-use-a-full-store-release-for">Use a full store release for:</h4>
<ul>
<li><p><strong>Native code changes:</strong> Any modifications to the <code>android</code> or <code>ios</code> directories.</p>
</li>
<li><p><strong>Plugin/dependency updates:</strong> Adding a new package or upgrading an existing one in <code>pubspec.yaml</code>, as this often changes native code or includes pre-compiled binaries.</p>
</li>
<li><p><strong>Asset changes:</strong> Adding new images, fonts, JSON files, or any other assets.</p>
</li>
<li><p><strong>Major feature releases:</strong> When the app's functionality or UI changes significantly, it's best practice (and often required by store policies) to go through a full review.</p>
</li>
</ul>
<p>Think of it this way: if the change is <strong>only in your</strong> <code>.dart</code> files and doesn't touch <code>pubspec.yaml</code>, it's likely patchable.</p>
<h2 id="heading-2-getting-started-installation-amp-project-setup">2. Getting Started: Installation &amp; Project Setup</h2>
<p>Let's get your environment and project ready for Shorebird.</p>
<h3 id="heading-step-1-install-the-shorebird-cli">Step 1: Install the Shorebird CLI</h3>
<p>Open your terminal and run the official installation script:</p>
<pre><code class="lang-bash">curl https://raw.githubusercontent.com/shorebirdtech/shorebird/main/install.sh -sSf | bash
</code></pre>
<h3 id="heading-step-2-add-shorebird-to-your-path">Step 2: Add Shorebird to your PATH</h3>
<p>To use the <code>shorebird</code> command from anywhere, add it to your shell's configuration file (<code>.zshrc</code>, <code>.bashrc</code>, <code>.bash_profile</code>, and so on).</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.shorebird/bin:<span class="hljs-variable">$PATH</span>"</span>
</code></pre>
<p>Restart your terminal or run <code>source ~/.zshrc</code> (or your respective file) for the change to take effect.</p>
<h3 id="heading-step-3-verify-and-log-in">Step 3: Verify and Log In</h3>
<p>Confirm the installation was successful and log in to your Shorebird account.</p>
<pre><code class="lang-bash">shorebird --version
shorebird login
</code></pre>
<p>This will open a browser window to authenticate your CLI with the Shorebird service.</p>
<h3 id="heading-step-4-initialize-shorebird-in-your-flutter-project">Step 4: Initialize Shorebird in Your Flutter Project</h3>
<p>Navigate to the root directory of your Flutter project and run:</p>
<pre><code class="lang-bash">shorebird init
</code></pre>
<p>This command is your project's entry point into the Shorebird ecosystem. Here’s what it does under the hood:</p>
<ol>
<li><p>Creates <code>shorebird.yaml</code>: This file stores your unique app ID. Commit this to version control.</p>
</li>
<li><p>Modifies <code>pubspec.yaml</code>: It automatically adds the <code>shorebird_code_push</code> package, which is necessary for the app to communicate with Shorebird's servers.</p>
</li>
<li><p>Updates <code>.gitignore</code>: Adds the <code>.shorebird/</code> directory to prevent temporary build artifacts from being committed.</p>
</li>
</ol>
<h2 id="heading-3-the-core-workflow-releasing-and-patching">3. The Core Workflow: Releasing and Patching</h2>
<p>Shorebird's workflow is built on a simple concept: you create a <strong>base release</strong>, and then you apply <strong>patches</strong> to it.</p>
<h3 id="heading-step-1-create-a-full-release">Step 1: Create a Full Release</h3>
<p>Before you can patch anything, you need a full version of your app built with Shorebird. This release will be submitted to the App Store and Play Store.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># For Android</span>
shorebird release android

<span class="hljs-comment"># For iOS</span>
shorebird release ios
</code></pre>
<p>This command compiles your app, uploads the necessary symbols to Shorebird's servers so it can build patches later, and generates the release artifact (AAB/IPA) for you to upload to the stores.</p>
<h3 id="heading-step-2-create-and-publish-a-patch">Step 2: Create and Publish a Patch</h3>
<p>After fixing a bug or making a tweak in your Dart code, you can create a patch. Shorebird will compare your changes against the original release and create a small, efficient binary diff.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Patch the latest Android release</span>
shorebird patch android

<span class="hljs-comment"># Patch the latest iOS release</span>
shorebird patch ios
</code></pre>
<p>This patch is immediately available to users who have the corresponding base release installed.</p>
<h3 id="heading-step-3-the-user-experience">Step 3: The User Experience</h3>
<p>When you push a patch, the process is designed to be seamless:</p>
<ol>
<li><p><strong>Silent download:</strong> On the next app launch, the <code>shorebird_code_push</code> SDK checks for a new patch. If one is found, it downloads silently in the background.</p>
</li>
<li><p><strong>Applied on next relaunch:</strong> The downloaded patch is staged and automatically applied the <em>next time</em> the user closes and re-opens the app. This ensures their current session is not interrupted.</p>
</li>
</ol>
<h2 id="heading-4-taking-control-in-app-update-logic-in-flutter">4. Taking Control: In-App Update Logic in Flutter</h2>
<p>For a more controlled experience, you can use the <code>shorebird_code_push</code> package to manage the update process within your app.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:shorebird_code_push/shorebird_code_push.dart'</span>;

<span class="hljs-keyword">final</span> _shorebirdCodePush = ShorebirdCodePush();

<span class="hljs-comment">// Check for a new patch on app start.</span>
<span class="hljs-keyword">void</span> checkForUpdate() <span class="hljs-keyword">async</span> {
  <span class="hljs-comment">// Check if a new patch is available.</span>
  <span class="hljs-keyword">final</span> isUpdateAvailable = <span class="hljs-keyword">await</span> _shorebirdCodePush.isNewPatchAvailableForDownload();

  <span class="hljs-keyword">if</span> (isUpdateAvailable) {
    <span class="hljs-comment">// Optionally, you can show a dialog to the user here.</span>
    <span class="hljs-comment">// e.g., "A quick update is available. It will be applied on next restart."</span>

    <span class="hljs-comment">// Download the new patch.</span>
    <span class="hljs-keyword">await</span> _shorebirdCodePush.downloadUpdateIfAvailable();
  }
}

<span class="hljs-comment">// For critical updates, you can force an immediate restart.</span>
<span class="hljs-keyword">void</span> forceRestart() <span class="hljs-keyword">async</span> {
  <span class="hljs-comment">// Make sure a patch is downloaded and ready to be installed.</span>
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">await</span> _shorebirdCodePush.isNewPatchReadyToInstall()) {
    <span class="hljs-comment">// This will force the app to close and restart with the new patch.</span>
    <span class="hljs-comment">// Use with caution, as it's a disruptive user experience.</span>
    <span class="hljs-keyword">await</span> _shorebirdCodePush.installUpdateAndRestart();
  }
}
</code></pre>
<p>Call <code>checkForUpdate()</code> in your <code>main()</code> function or the <code>initState</code> of your primary screen.</p>
<h2 id="heading-5-the-nuclear-option-forcing-updates-with-upgrader">5. The Nuclear Option: Forcing Updates with <code>upgrader</code></h2>
<p>Sometimes, a Shorebird patch isn't enough. For critical updates that involve native code, new assets, or if you need to ensure every single user is on the latest version right now, you need a blocking update screen. The <code>upgrader</code> package is perfect for this.</p>
<h3 id="heading-step-1-add-the-dependency">Step 1: Add the Dependency</h3>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">upgrader:</span> <span class="hljs-string">^7.0.0</span>
</code></pre>
<h3 id="heading-step-2-implement-the-blocking-ui">Step 2: Implement the Blocking UI</h3>
<p>Wrap your <code>MaterialApp</code> with the <code>UpgradeAlert</code> widget to create a blocking dialog that cannot be dismissed.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:upgrader/upgrader.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-comment">// This configuration creates a blocking "must-update" screen.</span>
    <span class="hljs-keyword">final</span> upgrader = Upgrader(
      showIgnore: <span class="hljs-keyword">false</span>,
      showLater: <span class="hljs-keyword">false</span>,
      canDismissDialog: <span class="hljs-keyword">false</span>,
      dialogStyle: UpgradeDialogStyle.material,
      shouldPopScope: () =&gt; <span class="hljs-keyword">false</span>, <span class="hljs-comment">// Prevents back navigation</span>
    );

    <span class="hljs-keyword">return</span> MaterialApp(
      home: UpgradeAlert(
        upgrader: upgrader,
        child: HomeScreen(), <span class="hljs-comment">// Your app's main screen</span>
      ),
    );
  }
}
</code></pre>
<h2 id="heading-6-real-world-playbook-combining-shorebird-upgrader-and-remote-config">6. Real-World Playbook: Combining Shorebird, Upgrader, and Remote Config</h2>
<p>Here’s how to tie everything together for a bulletproof update strategy. Let's use Firebase Remote Config to act as our remote "kill switch."</p>
<p><strong>Scenario:</strong> Version <code>1.2.0</code> of your app has a critical payment bug.</p>
<h3 id="heading-the-playbook"><strong>The Playbook:</strong></h3>
<ol>
<li><p><strong>Fix the bug:</strong> Correct the error in your Dart code.</p>
</li>
<li><p><strong>Push a Shorebird patch:</strong></p>
<pre><code class="lang-bash"> shorebird patch android --release-version 1.2.0+10 
 <span class="hljs-comment"># Use --release-version to target a specific build</span>
</code></pre>
<p> This delivers the fix to active users quickly and silently.</p>
</li>
<li><p><strong>Set a remote flag:</strong> In your Firebase Remote Config console, create a parameter like <code>force_update_for_version</code> and set its value to <code>"1.2.0"</code>.</p>
</li>
<li><p><strong>Implement logic in your app:</strong> On app start, check this flag <em>before</em> showing your main UI.</p>
<pre><code class="lang-dart"> <span class="hljs-keyword">import</span> <span class="hljs-string">'package:firebase_remote_config/firebase_remote_config.dart'</span>;
 <span class="hljs-keyword">import</span> <span class="hljs-string">'package:package_info_plus/package_info_plus.dart'</span>;

 Future&lt;<span class="hljs-keyword">void</span>&gt; main() <span class="hljs-keyword">async</span> {
   <span class="hljs-comment">// ... initialization code ...</span>

   <span class="hljs-comment">// Get current app version</span>
   <span class="hljs-keyword">final</span> packageInfo = <span class="hljs-keyword">await</span> PackageInfo.fromPlatform();
   <span class="hljs-keyword">final</span> currentVersion = packageInfo.version;

   <span class="hljs-comment">// Fetch remote config</span>
   <span class="hljs-keyword">final</span> remoteConfig = FirebaseRemoteConfig.instance;
   <span class="hljs-keyword">await</span> remoteConfig.fetchAndActivate();
   <span class="hljs-keyword">final</span> forceUpdateVersion = remoteConfig.getString(<span class="hljs-string">'force_update_for_version'</span>);

   <span class="hljs-comment">// Check if this version is flagged for a forced update</span>
   <span class="hljs-keyword">if</span> (currentVersion == forceUpdateVersion) {
     <span class="hljs-comment">// This is a "bad" version. Show the blocking update screen.</span>
     runApp(ForcedUpdateApp());
   } <span class="hljs-keyword">else</span> {
     <span class="hljs-comment">// This version is fine. Run the normal app and check for a Shorebird patch.</span>
     checkForUpdate(); <span class="hljs-comment">// Your Shorebird check function</span>
     runApp(MyApp());
   }
 }

 <span class="hljs-comment">// A simple app that only shows the upgrader screen.</span>
 <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ForcedUpdateApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
   <span class="hljs-meta">@override</span>
   Widget build(BuildContext context) {
     <span class="hljs-keyword">return</span> MaterialApp(
       home: UpgradeAlert(
         child: Scaffold(body: Center(child: Text(<span class="hljs-string">'Checking for updates...'</span>))),
       ),
     );
   }
 }
</code></pre>
</li>
</ol>
<p>This dual strategy ensures that users who haven't received the Shorebird patch yet are still blocked from using the buggy version and are directed to the store.</p>
<h2 id="heading-7-production-grade-best-practices">7. Production-Grade Best Practices</h2>
<p>Before you hit “patch” in production, you should treat Shorebird like any other release pipeline: test the build, roll it out in waves, keep an eye on crash metrics, and automate the boring parts. Here are some best practices to follow:</p>
<p>First, always test patches. Use <code>shorebird preview</code> to test your patch on a local device or emulator before deploying it to users.</p>
<pre><code class="lang-bash">shorebird preview
</code></pre>
<p>Use staged rollouts with channels. Don't push a critical patch to 100% of users at once. Shorebird's "channels" feature lets you deploy to a <code>staging</code> audience first.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Push patch to internal testers</span>
shorebird patch android --channel=staging

<span class="hljs-comment"># After verification, promote it to all users</span>
shorebird promote --channel=staging --release-version 1.2.0+10
</code></pre>
<p>You should also have a rollback plan if a patch makes things worse. In this case, the fastest way to fix it is to <strong>patch forward</strong>. Check out the last known-good commit from Git and run <code>shorebird patch</code> again. This new patch will overwrite the bad one.</p>
<p>And make sure you monitor everything. To do this, you can use tools like Firebase Crashlytics or Sentry to monitor crash rates and app stability after a patch is released.</p>
<p>Finally, it’s helpful to automate with CI/CD, and you can integrate Shorebird right into your CI/CD pipeline (for example, GitHub Actions). Store your <code>SHOREBIRD_TOKEN</code> as a secret and use commands like <code>shorebird patch android --release-version &lt;version&gt; --force</code> to automate deployments.</p>
<h2 id="heading-8-security-cost-and-limitations">8. Security, Cost, and Limitations</h2>
<p>There are some other things you should keep in mind when using Shorebird.</p>
<p>First, all communication with Shorebird's servers is over TLS. Patches are cryptographically signed, and the <code>shorebird_code_push</code> SDK verifies this signature before applying an update, ensuring it hasn't been tampered with.</p>
<p>Second, Shorebird operates on a freemium model with a generous free tier. Paid plans are based on Monthly Active Users (MAU) and unlock team features. You can check the official pricing page for current details.</p>
<p>Also, keep in mind that there are some limitations that we’ve already touched on a bit: for example, Shorebird <strong>only patches Dart code</strong>. You can’t update native plugins, add/change assets, or modify native code (<code>android</code>/<code>ios</code> folders) with a patch. These changes always require a full store release.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Shorebird is a transformative tool for any serious Flutter developer. It moves the release process from a monolithic, slow cycle to a dynamic, responsive one.</p>
<p>By combining Shorebird's rapid patching with a robust forced-update strategy using <code>upgrader</code> and Remote Config, you gain unprecedented control over your production application. You can fix bugs in minutes, mitigate risks, and keep your users happy – all without waiting for the app store.</p>
<h3 id="heading-references">References</h3>
<p><strong>1. Shorebird</strong></p>
<ul>
<li><p><a target="_blank" href="https://shorebird.dev">Official Website</a></p>
</li>
<li><p><a target="_blank" href="https://docs.shorebird.dev/cli">CLI Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/shorebird_code_push">Code Push SDK</a></p>
</li>
</ul>
<p><strong>2. Upgrader Package</strong></p>
<ul>
<li><p><a target="_blank" href="http://Pub.dev">Pub.dev</a> <a target="_blank" href="https://pub.dev/packages/upgrader">Page</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/larryaasen/upgrader">GitHub Repository</a></p>
</li>
</ul>
<p><strong>3. Supporting Tools &amp; Concepts</strong></p>
<ul>
<li><p><a target="_blank" href="https://firebase.google.com/docs/remote-config">Firebase Remote Config</a></p>
</li>
<li><p><a target="_blank" href="https://semver.org">Semantic Versioning</a></p>
</li>
<li><p><a target="_blank" href="https://docs.flutter.dev/testing/cd">Flutter CI/CD Documentation</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
