<?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: Terence Faid JABO </title>
    <description>The latest articles on DEV Community by Terence Faid JABO  (@faidterence).</description>
    <link>https://dev.to/faidterence</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1143188%2F4e545937-58c4-4c00-a13a-f83c290b21ca.jpeg</url>
      <title>DEV Community: Terence Faid JABO </title>
      <link>https://dev.to/faidterence</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faidterence"/>
    <language>en</language>
    <item>
      <title>🔓 Unlock Your App's Potential: Master A/B Testing in Flutter with Firebase Remote Config!</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Sat, 05 Jul 2025 12:52:21 +0000</pubDate>
      <link>https://dev.to/faidterence/unlock-your-apps-potential-master-ab-testing-in-flutter-with-firebase-remote-config-2ci6</link>
      <guid>https://dev.to/faidterence/unlock-your-apps-potential-master-ab-testing-in-flutter-with-firebase-remote-config-2ci6</guid>
      <description>&lt;p&gt;&lt;em&gt;Ever wondered why Instagram shows you one button color while your friend sees another? That's A/B testing in action – and you can add this superpower to your Flutter app too!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Exactly is A/B Testing?
&lt;/h2&gt;

&lt;p&gt;Think of A/B testing as running a friendly experiment with your app users. Instead of guessing what color button works best, you show half your users a blue button and half a green button. Then you measure which one gets more clicks. It's like having a crystal ball for app decisions!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real example&lt;/strong&gt;: Netflix famously tested their "Continue Watching" button placement and increased viewing time by 15% just by moving it to a different spot on the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should You Care About A/B Testing?
&lt;/h2&gt;

&lt;p&gt;Let's be honest – we've all made app design decisions based on our personal preferences. "I like purple, so the app should be purple!" But what if your users actually prefer orange? A/B testing takes the guesswork out of these decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The benefits are huge:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Better user experience&lt;/strong&gt;: Your users get features they actually want&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher engagement&lt;/strong&gt;: More people use your app when it's optimized for them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart decisions&lt;/strong&gt;: No more arguing about button colors in team meetings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real data&lt;/strong&gt;: You'll know exactly what works and what doesn't&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Magic Behind the Scenes: Firebase Remote Config
&lt;/h2&gt;

&lt;p&gt;Here's where it gets exciting. Firebase Remote Config is like having a remote control for your app. You can change colors, text, or even entire features without asking users to update their apps. &lt;/p&gt;

&lt;p&gt;Imagine you're running a food delivery app. With Remote Config, you could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the "Order Now" button from blue to red for half your users&lt;/li&gt;
&lt;li&gt;Test different promotional messages on the home screen&lt;/li&gt;
&lt;li&gt;Switch between different layouts instantly&lt;/li&gt;
&lt;li&gt;All without users downloading a new app version!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building Your First A/B Test: A Step-by-Step Story
&lt;/h2&gt;

&lt;p&gt;Let me walk you through creating your first A/B test with a simple example. We'll test different theme colors to see which one keeps users more engaged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Setting Up Your Testing Lab
&lt;/h3&gt;

&lt;p&gt;First, you'll need to connect your Flutter app to Firebase. Think of Firebase as your testing control center where you'll monitor everything that happens.&lt;/p&gt;

&lt;p&gt;Add these essential tools to your Flutter project:&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="c1"&gt;# pubspec.yaml&lt;/span&gt;
&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;firebase_core&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^3.6.0&lt;/span&gt;
  &lt;span class="na"&gt;firebase_remote_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^5.1.3&lt;/span&gt;
  &lt;span class="na"&gt;firebase_analytics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^11.3.3&lt;/span&gt;
  &lt;span class="na"&gt;flutter_riverpod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^2.6.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What each tool does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Remote Config&lt;/strong&gt;: Your remote control for app changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Analytics&lt;/strong&gt;: Your detective for tracking user behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Riverpod&lt;/strong&gt;: Your assistant for updating the app instantly when changes happen&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Creating the Smart App
&lt;/h3&gt;

&lt;p&gt;Here's where the magic happens. Your app will be smart enough to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check Firebase for the latest settings when it starts&lt;/li&gt;
&lt;li&gt;Update its appearance instantly when you change things remotely&lt;/li&gt;
&lt;li&gt;Track how users interact with different versions&lt;/li&gt;
&lt;li&gt;Keep working even if the internet is slow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;First, let's define our A/B test parameters using enums for better code organization:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define your Remote Config parameters as enums for type safety&lt;/span&gt;
&lt;span class="kt"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;primaryColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'primaryColor'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'buttonText'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;welcomeMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'welcomeMessage'&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;RemoteConfigKey&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;key&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;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Define possible values for better validation&lt;/span&gt;
&lt;span class="kt"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ThemeColor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'blue'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'green'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'red'&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;ThemeColor&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;value&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;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;color&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="k"&gt;this&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="n"&gt;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&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;&lt;strong&gt;Now here's the core code that makes this work:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set up your Remote Config with default values using enums&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDefaults&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;primaryColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buttonText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Click Me!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;welcomeMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Welcome to our app!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Get the latest configuration from Firebase&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fetchAndActivate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Use the values in your app with type safety&lt;/span&gt;
&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;buttonText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buttonText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&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;colorName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;primaryColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ThemeColor&lt;/span&gt; &lt;span class="n"&gt;themeColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;firstWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&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;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;colorName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;orElse:&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;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Safe fallback&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app will automatically check Firebase for updates and apply them instantly. If a user has the app open and you change the button text from "Click Me!" to "Tap Here!" in Firebase, they'll see the change immediately!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tracking user behavior is just as simple:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Track when users click buttons with enum safety&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;FirebaseAnalytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s"&gt;'button_clicked'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;parameters:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;'button_variant'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'color_variant'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;themeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Using enum value&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 beautiful part? Users never know they're part of an experiment. The app just works, and you get valuable insights about what they prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Setting Up Your First Experiment
&lt;/h3&gt;

&lt;p&gt;In your Firebase dashboard (think of it as mission control), you'll create your experiment. Here's how it looks in practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experiment Name&lt;/strong&gt;: "Theme Color Test"&lt;br&gt;
&lt;strong&gt;Question&lt;/strong&gt;: "Do users click more buttons when the app is blue or green?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting up targeting&lt;/strong&gt; (who sees your test):&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fwpp9o2feroslqhzs2toe.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.amazonaws.com%2Fuploads%2Farticles%2Fwpp9o2feroslqhzs2toe.png" alt="Firebase A/B Testing Targeting" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Defining your success goals&lt;/strong&gt; (what you want to measure):&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fycg63xi9wf43mn6dnzzi.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.amazonaws.com%2Fuploads%2Farticles%2Fycg63xi9wf43mn6dnzzi.png" alt="Firebase A/B Testing Goals" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating different variants&lt;/strong&gt; (the different versions users will see):&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2F7qcjx72u6vnsh6w8ae0z.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.amazonaws.com%2Fuploads%2Farticles%2F7qcjx72u6vnsh6w8ae0z.png" alt="Firebase A/B Testing Variants" width="800" height="792"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your test will use these Remote Config parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;primaryColor&lt;/strong&gt;: Controls the app's theme color (blue, green, red)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;buttonText&lt;/strong&gt;: Changes button labels ("Click Me!", "Tap Here!", "Press This!")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;welcomeMessage&lt;/strong&gt;: Tests different welcome messages for new users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Firebase will automatically split your users into these groups and track their behavior.&lt;/p&gt;

&lt;p&gt;Once you've configured your experiment with the right targeting, goals, and variants, you'll hit the &lt;strong&gt;Publish&lt;/strong&gt; button to make your A/B test go live. Then the magic begins – Firebase automatically starts showing different versions to different users while tracking their behavior.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4: Watching the Results Roll In
&lt;/h3&gt;

&lt;p&gt;This is the fun part! As users interact with your app, you'll see real-time data showing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many people clicked the button in each group&lt;/li&gt;
&lt;li&gt;How long people spent in the app&lt;/li&gt;
&lt;li&gt;Which version led to more purchases or sign-ups&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Real-World Success Stories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Spotify&lt;/strong&gt; used A/B testing to optimize their music discovery features. They found that showing album artwork 23% larger increased music exploration by 8%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Airbnb&lt;/strong&gt; tested different search result layouts and increased bookings by 12% simply by changing how photos were displayed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instagram&lt;/strong&gt; tested story viewer interfaces and found that subtle changes in swipe gestures improved user retention by 6%.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up Your Testing Dashboard
&lt;/h2&gt;

&lt;p&gt;Your Firebase console becomes your command center where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monitor live experiments&lt;/strong&gt;: See results as they happen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch between versions&lt;/strong&gt;: Turn features on or off instantly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track user behavior&lt;/strong&gt;: Understand what users really do (not what they say they do)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make data-driven decisions&lt;/strong&gt;: No more guessing games&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What Makes a Great A/B Test?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Smart Test Ideas:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Button colors and text&lt;/strong&gt;: "Click Me!" vs "Tap Here!" vs "Press This!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme colors&lt;/strong&gt;: Blue vs green vs red app themes (using our &lt;code&gt;ThemeColor&lt;/code&gt; enum)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Welcome messages&lt;/strong&gt;: Different greetings for new users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout changes&lt;/strong&gt;: Horizontal vs vertical menu layouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature placement&lt;/strong&gt;: Where should the search bar go?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Test Setup Best Practices:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start small&lt;/strong&gt;: Test one thing at a time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be patient&lt;/strong&gt;: Let tests run long enough to get meaningful results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Think about your users&lt;/strong&gt;: What actually matters to them?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use enums for consistency&lt;/strong&gt;: Define your test variants clearly to avoid typos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have a backup plan&lt;/strong&gt;: Always have a default version that works&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Smart Way to Deploy
&lt;/h2&gt;

&lt;p&gt;When you're ready to go live, start small:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test with 10% of users first&lt;/strong&gt;: Make sure everything works smoothly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor closely&lt;/strong&gt;: Watch for any unexpected behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale gradually&lt;/strong&gt;: Slowly increase to more users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep fallbacks ready&lt;/strong&gt;: Always have a way to revert changes quickly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's how to set up safe testing intervals:&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;// For testing - quick updates&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setConfigSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteConfigSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;fetchTimeout:&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;seconds:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;minimumFetchInterval:&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;seconds:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// For production - slower, more stable&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setConfigSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteConfigSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;fetchTimeout:&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;seconds:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;minimumFetchInterval:&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;hours:&lt;/span&gt; &lt;span class="mi"&gt;1&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;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Don't test too many things at once&lt;/strong&gt;: If you change the button color AND the text AND the layout, you won't know which change caused the improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't end tests too early&lt;/strong&gt;: Sometimes initial results are misleading. Give your test time to gather enough data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't ignore small improvements&lt;/strong&gt;: A 2% improvement might seem tiny, but across millions of users, that's huge!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't forget error handling&lt;/strong&gt;: Always have fallback values in case Firebase is unavailable:&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;// Safe way to get Remote Config values using enums&lt;/span&gt;
&lt;span class="n"&gt;ThemeColor&lt;/span&gt; &lt;span class="nf"&gt;getThemeColor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;primaryColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&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;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;firstWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&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;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;orElse:&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;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Safe fallback&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;return&lt;/span&gt; &lt;span class="n"&gt;ThemeColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Default fallback&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getButtonText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;remoteConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteConfigKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;buttonText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&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;return&lt;/span&gt; &lt;span class="s"&gt;'Click Me!'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Safe fallback&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;&lt;strong&gt;Don't test just for the sake of testing&lt;/strong&gt;: Make sure your test could actually lead to better user experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your A/B Testing Toolkit
&lt;/h2&gt;

&lt;p&gt;To get started, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flutter development environment&lt;/strong&gt;: Your app-building workspace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase account&lt;/strong&gt;: Your testing control center (free to start)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic analytics knowledge&lt;/strong&gt;: Understanding what metrics matter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patience&lt;/strong&gt;: Good experiments take time to show results&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Once you've mastered basic A/B testing, you can explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multivariate testing&lt;/strong&gt;: Testing multiple elements simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personalization&lt;/strong&gt;: Showing different content based on user behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced analytics&lt;/strong&gt;: Deeper insights into user journeys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt;: Automatically switching to winning variants&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;A/B testing isn't just for tech giants. With Flutter and Firebase, any developer can make data-driven decisions that improve user experiences. The best part? You don't need to be a statistics expert or have a huge budget.&lt;/p&gt;

&lt;p&gt;Start with simple tests – maybe just changing a button color or testing different welcome messages. As you get comfortable with the process, you'll find yourself making smarter decisions and building apps that users truly love.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to give it a try?&lt;/strong&gt; Your users are waiting to show you what they really want – you just need to ask them the right way!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you tried A/B testing in your apps? What surprising results did you discover? Share your experiences in the comments – I'd love to hear your success stories!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Resources and Source Code
&lt;/h2&gt;

&lt;p&gt;Ready to start building? Here are the resources you need:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔗 Complete Source Code&lt;/strong&gt;: &lt;a href="https://github.com/faid-terence/firebase_remote_config_ab_testing" rel="noopener noreferrer"&gt;Flutter A/B Testing with Firebase - GitHub Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;📖 Documentation Links&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/remote-config" rel="noopener noreferrer"&gt;Firebase Remote Config Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/ab-testing" rel="noopener noreferrer"&gt;Firebase A/B Testing Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://riverpod.dev/" rel="noopener noreferrer"&gt;Flutter Riverpod Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🎯 Firebase Console Setup&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://console.firebase.google.com/" rel="noopener noreferrer"&gt;Create Firebase Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.firebase.google.com/project/_/config" rel="noopener noreferrer"&gt;Remote Config Dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://console.firebase.google.com/project/_/abtesting" rel="noopener noreferrer"&gt;A/B Testing Dashboard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Flutter Deep Linking</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Thu, 03 Jul 2025 11:50:42 +0000</pubDate>
      <link>https://dev.to/faidterence/flutter-deep-linking-create-links-that-actually-work-3l2b</link>
      <guid>https://dev.to/faidterence/flutter-deep-linking-create-links-that-actually-work-3l2b</guid>
      <description>&lt;p&gt;Ever clicked a link and ended up exactly where you wanted to go? That's deep linking magic! &lt;/p&gt;

&lt;p&gt;Imagine your friend finds a cool event on your app and wants to share it. Instead of texting you "Hey, search for Flutter Conference 2025 in that app," they just send a link. You tap it, and boom – you're looking at that exact event. No searching, no scrolling, no frustration.&lt;/p&gt;

&lt;p&gt;That's what we're building today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Deep Links Matter
&lt;/h2&gt;

&lt;p&gt;Deep links are like shortcuts to specific parts of your app. They make your app feel connected to the rest of the digital world, not like a lonely island.&lt;/p&gt;

&lt;p&gt;Here's what users get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instant access&lt;/strong&gt; to shared content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No hunting&lt;/strong&gt; through menus&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth experience&lt;/strong&gt; from web to app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Better user experience&lt;/strong&gt; (happy users stick around)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More effective marketing&lt;/strong&gt; (links go straight to products)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher engagement&lt;/strong&gt; (less friction = more action)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How Deep Links Work
&lt;/h2&gt;

&lt;p&gt;Every deep link has parts, like a home address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://myapp.com/event/flutter-conference-2025
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://&lt;/code&gt; - How to deliver the link&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;myapp.com&lt;/code&gt; - Your app's address&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/event/flutter-conference-2025&lt;/code&gt; - The specific page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also add extras:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;?category=tech&amp;amp;date=2025&lt;/code&gt; - Filters and options&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#speakers&lt;/code&gt; - Jump to a specific section&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Two Ways to Make Deep Links
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Custom Schemes (Easy Start)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myapp://event/flutter-conference
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; Quick to set up, works without a website&lt;br&gt;
&lt;strong&gt;Bad:&lt;/strong&gt; Any app can steal your scheme, looks unprofessional&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 2: HTTPS Links (Professional)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://myapp.com/event/flutter-conference
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; Secure, falls back to website, looks professional&lt;br&gt;
&lt;strong&gt;Bad:&lt;/strong&gt; Requires owning a domain, bit more setup&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We recommend HTTPS links&lt;/strong&gt; for real apps.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up Android
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Tell Android About Your Links
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;android/app/src/main/AndroidManifest.xml&lt;/code&gt; and add this inside your &lt;code&gt;&amp;lt;activity&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;intent-filter&lt;/span&gt; &lt;span class="na"&gt;android:autoVerify=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.VIEW"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.DEFAULT"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.BROWSABLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;data&lt;/span&gt; &lt;span class="na"&gt;android:scheme=&lt;/span&gt;&lt;span class="s"&gt;"https"&lt;/span&gt;
          &lt;span class="na"&gt;android:host=&lt;/span&gt;&lt;span class="s"&gt;"myapp.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Prove You Own Your Domain
&lt;/h3&gt;

&lt;p&gt;Create a file called &lt;code&gt;assetlinks.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"relation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"delegate_permission/common.handle_all_urls"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"android_app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"package_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sha256_cert_fingerprints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_SHA256_FINGERPRINT"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upload it to: &lt;code&gt;https://myapp.com/.well-known/assetlinks.json&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Test It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell am start &lt;span class="nt"&gt;-a&lt;/span&gt; android.intent.action.VIEW &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; android.intent.category.BROWSABLE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"https://myapp.com/event/flutter-conference"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  com.example.myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up iOS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Add Associated Domains
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;ios/Runner.xcworkspace&lt;/code&gt; in Xcode&lt;/li&gt;
&lt;li&gt;Select Runner → Signing &amp;amp; Capabilities&lt;/li&gt;
&lt;li&gt;Add "Associated Domains" capability&lt;/li&gt;
&lt;li&gt;Add: &lt;code&gt;applinks:myapp.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Create Apple's Verification File
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;apple-app-site-association&lt;/code&gt; (no file extension):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"applinks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"appID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TEAM_ID.com.example.myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/event/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/events"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upload to: &lt;code&gt;https://myapp.com/.well-known/apple-app-site-association&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Test on iOS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcrun simctl openurl booted &lt;span class="s2"&gt;"https://myapp.com/event/flutter-conference"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Flutter Code: Making It Work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Up GoRouter
&lt;/h3&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;'package:go_router/go_router.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;routes:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;HomePage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/events'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;EventsPage&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/event/:eventId'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;eventId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'eventId'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EventDetailsPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;eventId:&lt;/span&gt; &lt;span class="n"&gt;eventId&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;h3&gt;
  
  
  Connect to Your App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'My App'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;routerConfig:&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handle Missing Content
&lt;/h3&gt;

&lt;p&gt;What if someone shares a link to an event that doesn't exist? Handle it gracefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventDetailsPage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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;eventId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;EventDetailsPage&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;eventId&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Try to find the event&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;findEventById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventId&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;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Oops!'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;mainAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;MainAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;event_busy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;size:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;16&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="s"&gt;'Event Not Found'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                   &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;fontWeight:&lt;/span&gt; &lt;span class="n"&gt;FontWeight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bold&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="s"&gt;'This event might have been moved or cancelled.'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;onPressed:&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;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/events'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Browse All Events'&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="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Show the event details&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="c1"&gt;// Event details here&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;h2&gt;
  
  
  Adding Login Protection
&lt;/h2&gt;

&lt;p&gt;Want to protect certain pages? Easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;redirect:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkIfUserIsLoggedIn&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;isLoginPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matchedLocation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'/login'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Not logged in? Send to login&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isLoginPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'/login?redirect=&lt;/span&gt;&lt;span class="si"&gt;${state.matchedLocation}&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="c1"&gt;// Already logged in? Skip login page&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;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;isLoginPage&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;redirectUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'redirect'&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;redirectUrl&lt;/span&gt; &lt;span class="o"&gt;??&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// No redirect needed&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nl"&gt;routes:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Your routes here&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;
  
  
  Testing Your Links
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simple Tests
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Android:&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;adb shell am start &lt;span class="nt"&gt;-a&lt;/span&gt; android.intent.action.VIEW &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; android.intent.category.BROWSABLE &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"https://myapp.com/event/flutter-conference"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  com.example.myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;iOS:&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;xcrun simctl openurl booted &lt;span class="s2"&gt;"https://myapp.com/event/flutter-conference"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real-World Tests
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Send the link via WhatsApp or Messages&lt;/li&gt;
&lt;li&gt;Create a simple webpage with your link&lt;/li&gt;
&lt;li&gt;Share on social media and test&lt;/li&gt;
&lt;li&gt;Test with and without your app installed&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Common Problems and Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem: App opens inside another app
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add &lt;code&gt;android:launchMode="singleTask"&lt;/code&gt; to your activity in AndroidManifest.xml&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem: Link opens in browser instead of app
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Check your domain verification files are uploaded correctly&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem: Multiple apps claim the same URL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Make sure &lt;code&gt;android:autoVerify="true"&lt;/code&gt; is set and your verification files are correct&lt;/p&gt;

&lt;h2&gt;
  
  
  Pro Tips
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep URLs simple&lt;/strong&gt;: &lt;code&gt;myapp.com/event/flutter-summit&lt;/code&gt; is better than &lt;code&gt;myapp.com/e/fs2025/details&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plan your structure&lt;/strong&gt;: Think about how users will share and remember links&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handle errors gracefully&lt;/strong&gt;: Always plan for missing content and network issues&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test everything&lt;/strong&gt;: Test on different devices, with and without your app installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Track performance&lt;/strong&gt;: Use analytics to see how users interact with your links&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Demo: See It in Action
&lt;/h2&gt;

&lt;p&gt;Let's walk through the complete deep linking experience step by step:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Our App's Home Screen
&lt;/h3&gt;

&lt;p&gt;First, here's what our EventSpot app looks like when users open it normally:&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.amazonaws.com%2Fuploads%2Farticles%2Fv697hkhvgzmjognkmlk9.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.amazonaws.com%2Fuploads%2Farticles%2Fv697hkhvgzmjognkmlk9.png" alt="EventSpot Home Screen" width="800" height="1739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Events List Screen
&lt;/h3&gt;

&lt;p&gt;Users can browse all available events from our events screen:&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.amazonaws.com%2Fuploads%2Farticles%2Ffg254ilto5ab4n4h6cli.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.amazonaws.com%2Fuploads%2Farticles%2Ffg254ilto5ab4n4h6cli.png" alt="All Events Screen" width="800" height="1739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Deep Link in Action - Browser Entry
&lt;/h3&gt;

&lt;p&gt;Now here's the magic! When someone shares a deep link, users can enter it directly in their browser:&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.amazonaws.com%2Fuploads%2Farticles%2Fj79bx529u0lfi6h0plx8.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.amazonaws.com%2Fuploads%2Farticles%2Fj79bx529u0lfi6h0plx8.png" alt="Browser entering URL" width="800" height="1739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. App Permission Request
&lt;/h3&gt;

&lt;p&gt;iOS asks for permission to open the link in our app instead of staying in the browser:&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.amazonaws.com%2Fuploads%2Farticles%2Fjxavtjtnvoi9g314oub8.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.amazonaws.com%2Fuploads%2Farticles%2Fjxavtjtnvoi9g314oub8.png" alt="Permission to open in app" width="800" height="1739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Direct Navigation Success!
&lt;/h3&gt;

&lt;p&gt;And voilà! The user lands directly on the specific event details screen, skipping all the navigation:&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.amazonaws.com%2Fuploads%2Farticles%2Fnrpxmvngmfasxnuz6f0g.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.amazonaws.com%2Fuploads%2Farticles%2Fnrpxmvngmfasxnuz6f0g.png" alt="Event Details Screen" width="800" height="1739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Demo Video!
&lt;/h3&gt;


  
  Your browser does not support the video tag.


&lt;p&gt;This seamless transition from web link to specific app content is what makes deep linking so powerful for user experience!&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code and Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Get the complete source code:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/faid-terence/deep_link_example" rel="noopener noreferrer"&gt;GitHub Repository - Flutter Deep Linking Example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Additional Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.flutter.dev/development/ui/navigation/deep-linking" rel="noopener noreferrer"&gt;Flutter Deep Linking Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/go_router" rel="noopener noreferrer"&gt;GoRouter Package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/training/app-links" rel="noopener noreferrer"&gt;Android App Links Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/ios/universal-links/" rel="noopener noreferrer"&gt;iOS Universal Links Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Deep linking transforms your Flutter app from an isolated island into a connected part of the digital world. Users can share content effortlessly, your marketing becomes more effective, and the overall experience gets much better.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions? Found a bug? Let me know in the comments below!&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Adding Flavors to Your Flutter App: From One Codebase to Multiple Experiences</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Mon, 30 Jun 2025 13:49:40 +0000</pubDate>
      <link>https://dev.to/faidterence/adding-flavors-to-your-flutter-app-from-one-codebase-to-multiple-experiences-1972</link>
      <guid>https://dev.to/faidterence/adding-flavors-to-your-flutter-app-from-one-codebase-to-multiple-experiences-1972</guid>
      <description>&lt;p&gt;You're building a Flutter app. Your client wants three versions: development for testing, staging for final checks, and production for real users. Each needs different settings—different API URLs, app names, even colors.&lt;/p&gt;

&lt;p&gt;Your first thought might be: "I'll create three separate projects." &lt;/p&gt;

&lt;p&gt;Don't. There's a better way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Flutter Flavors?
&lt;/h2&gt;

&lt;p&gt;Flutter flavors let you build multiple versions of your app from one codebase. Think of it like a restaurant kitchen making different dishes from the same ingredients.&lt;/p&gt;

&lt;p&gt;One codebase → Multiple app versions&lt;/p&gt;

&lt;p&gt;Each flavor can have its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App name and icon&lt;/li&gt;
&lt;li&gt;API endpoints
&lt;/li&gt;
&lt;li&gt;Colors and branding&lt;/li&gt;
&lt;li&gt;Feature toggles&lt;/li&gt;
&lt;li&gt;Configuration settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem Flavors Solve
&lt;/h2&gt;

&lt;p&gt;Without flavors, developers face these headaches:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment Mix-ups&lt;/strong&gt;: Accidentally testing against production data, breaking real user experiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual Switching&lt;/strong&gt;: Constantly changing API URLs and settings in code, leading to errors and messy commits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Duplication&lt;/strong&gt;: Maintaining separate codebases for different clients or environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment Confusion&lt;/strong&gt;: Forgetting which configuration is active before releasing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution in Action
&lt;/h2&gt;

&lt;p&gt;With flavors, switching environments becomes effortless:&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;# Launch development version&lt;/span&gt;
flutter run &lt;span class="nt"&gt;--flavor&lt;/span&gt; dev

&lt;span class="c"&gt;# staging version  &lt;/span&gt;
flutter run &lt;span class="nt"&gt;--flavor&lt;/span&gt; staging

&lt;span class="c"&gt;# production version&lt;/span&gt;
flutter run &lt;span class="nt"&gt;--flavor&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each command launches a completely different app configuration. Same code, different behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Benefits You'll See Immediately
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Faster Development&lt;/strong&gt;: No more manually changing configurations. Switch environments with one command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fewer Bugs&lt;/strong&gt;: Each environment is isolated. No more production accidents during development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Easier Testing&lt;/strong&gt;: QA team gets their own staging environment. Developers can experiment freely in dev.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client Customization&lt;/strong&gt;: Build white-label apps for multiple clients without duplicating code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Flavors: Choose Your Approach
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Manual Setup (Full Control)
&lt;/h3&gt;

&lt;p&gt;Perfect when you need complete customization or want to understand the underlying mechanics.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Configure Android Flavors
&lt;/h4&gt;

&lt;p&gt;Create &lt;code&gt;android/app/build.gradle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;flavorDimensions&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;

    &lt;span class="n"&gt;productFlavors&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
            &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s2"&gt;".dev"&lt;/span&gt;
            &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-dev"&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp Dev"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;staging&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
            &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s2"&gt;".staging"&lt;/span&gt;
            &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-staging"&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp Staging"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;prod&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2: Configure iOS Flavors
&lt;/h4&gt;

&lt;p&gt;Create build configurations in Xcode:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;ios/Runner.xcworkspace&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select Runner project → Info tab&lt;/li&gt;
&lt;li&gt;Duplicate existing configurations&lt;/li&gt;
&lt;li&gt;Create: Debug-dev, Debug-staging, Debug-prod, Release-dev, Release-staging, Release-prod&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Step 3: Create Dart Configuration Files
&lt;/h4&gt;

&lt;p&gt;Create &lt;code&gt;lib/config/app_config.dart&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppConfig&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;appName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'APP_NAME'&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;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'API_URL'&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;flavor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'FLAVOR'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;isDevelopment&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;flavor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'dev'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;isStaging&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;flavor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'staging'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;isProduction&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;flavor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'prod'&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;Create flavor-specific entry points:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lib/main_dev.dart&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'main.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option 2: Very Good CLI (Quick Setup)
&lt;/h3&gt;

&lt;p&gt;Great for rapid prototyping and standard configurations.&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;# Install the CLI&lt;/span&gt;
dart pub global activate very_good_cli

&lt;span class="c"&gt;# Create project with flavors pre-configured&lt;/span&gt;
very_good create flutter_app my_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get development, staging, and production flavors ready to use with proper native configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3: Flavorizr Package (Automated Setup)
&lt;/h3&gt;

&lt;p&gt;Ideal for existing projects that need flavors added quickly.&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;# Add to pubspec.yaml&lt;/span&gt;
dev_dependencies:
  flavorizr: ^2.2.1

&lt;span class="c"&gt;# Create flavorizr.yaml configuration&lt;/span&gt;
flutter packages pub run flavorizr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding Custom Flavors
&lt;/h2&gt;

&lt;p&gt;Need more than dev/staging/prod? Here's how to add custom flavors:&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a "Demo" Flavor
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Update Android Configuration
&lt;/h4&gt;

&lt;p&gt;Add to &lt;code&gt;android/app/build.gradle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;productFlavors&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// existing flavors...&lt;/span&gt;

    &lt;span class="n"&gt;demo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
        &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s2"&gt;".demo"&lt;/span&gt;
        &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-demo"&lt;/span&gt;
        &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp Demo"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Update iOS Configuration
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;In Xcode, duplicate existing configurations&lt;/li&gt;
&lt;li&gt;Create Debug-demo and Release-demo&lt;/li&gt;
&lt;li&gt;Update build settings for demo-specific values&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Create Dart Entry Point
&lt;/h4&gt;

&lt;p&gt;Create &lt;code&gt;lib/main_demo.dart&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.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;'main.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'FLAVOR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;defaultValue:&lt;/span&gt; &lt;span class="s"&gt;'demo'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;main&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;h4&gt;
  
  
  Update Launch Configuration
&lt;/h4&gt;

&lt;p&gt;Create &lt;code&gt;.vscode/launch.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/main_demo.dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"--flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--dart-define"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FLAVOR=demo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Flavor Configurations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment-Specific Features
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FeatureFlags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;debugMode&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;AppConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isDevelopment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;analyticsEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isDevelopment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;crashReporting&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;AppConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isProduction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Different Themes Per Flavor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FlavorTheme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;theme&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;AppConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flavor&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;'dev'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;primarySwatch:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;red&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;'staging'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;primarySwatch:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orange&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;'demo'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;primarySwatch:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;purple&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;return&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;primarySwatch:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Client-Specific Configurations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientConfig&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;clientName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'CLIENT_NAME'&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;brandColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'BRAND_COLOR'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;primaryColor&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;clientName&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;'client_a'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&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;'client_b'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&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;return&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&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;h2&gt;
  
  
  Running Your Flavored App
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Command Line
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Development&lt;/span&gt;
flutter run &lt;span class="nt"&gt;--flavor&lt;/span&gt; dev &lt;span class="nt"&gt;--dart-define&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;FLAVOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="c"&gt;# Staging&lt;/span&gt;
flutter run &lt;span class="nt"&gt;--flavor&lt;/span&gt; staging &lt;span class="nt"&gt;--dart-define&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;FLAVOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;staging

&lt;span class="c"&gt;# Custom demo flavor&lt;/span&gt;
flutter run &lt;span class="nt"&gt;--flavor&lt;/span&gt; demo &lt;span class="nt"&gt;--dart-define&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;FLAVOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building for Release
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build production APK&lt;/span&gt;
flutter build apk &lt;span class="nt"&gt;--flavor&lt;/span&gt; prod &lt;span class="nt"&gt;--dart-define&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;FLAVOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod

&lt;span class="c"&gt;# Build staging iOS&lt;/span&gt;
flutter build ios &lt;span class="nt"&gt;--flavor&lt;/span&gt; staging &lt;span class="nt"&gt;--dart-define&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;FLAVOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Keep Configurations Organized
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lib/
├── config/
│   ├── app_config.dart
│   ├── dev_config.dart
│   ├── staging_config.dart
│   └── prod_config.dart
├── main.dart
├── main_dev.dart
├── main_staging.dart
└── main_prod.dart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Build-Time Constants
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppConstants&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;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'API_URL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;defaultValue:&lt;/span&gt; &lt;span class="s"&gt;'https://api.example.com'&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;bool&lt;/span&gt; &lt;span class="n"&gt;enableLogging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;'ENABLE_LOGGING'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;defaultValue:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Version Control Considerations
&lt;/h3&gt;

&lt;p&gt;Never commit sensitive data like API keys directly. Use environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Secrets&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;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'API_KEY'&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;databaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'DATABASE_URL'&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;
  
  
  Your Development Workflow Transformed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before flavors:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change API URL in code&lt;/li&gt;
&lt;li&gt;Update app name manually&lt;/li&gt;
&lt;li&gt;Switch configuration files&lt;/li&gt;
&lt;li&gt;Hope you didn't miss anything&lt;/li&gt;
&lt;li&gt;Commit changes (cluttering git history)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;With flavors:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;flutter run --flavor dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Done.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Troubleshooting Common Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  iOS Build Errors
&lt;/h3&gt;

&lt;p&gt;Make sure all configurations exist in Xcode and schemes are properly configured.&lt;/p&gt;

&lt;h3&gt;
  
  
  Android Namespace Conflicts
&lt;/h3&gt;

&lt;p&gt;Ensure each flavor has a unique &lt;code&gt;applicationIdSuffix&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing Dependencies
&lt;/h3&gt;

&lt;p&gt;Some flavors might need specific dependencies. Use flavor-specific dependency injection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Using Flavors Today
&lt;/h2&gt;

&lt;p&gt;If you're managing multiple environments or building for different clients, flavors aren't optional—they're essential.&lt;/p&gt;

&lt;p&gt;Set them up once, save time forever.&lt;/p&gt;

&lt;p&gt;Your codebase stays clean. Your deployments become predictable. Your team stays focused on building features, not managing configurations.&lt;/p&gt;

&lt;p&gt;Choose the setup method that works best for your project and team—whether that's manual configuration for maximum control, Very Good CLI for quick setup, or Flavorizr for existing projects.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Flutter Dos and Don'ts: Writing Clean and Efficient Code</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Mon, 30 Jun 2025 13:27:25 +0000</pubDate>
      <link>https://dev.to/faidterence/flutter-dos-and-donts-writing-clean-and-efficient-code-4a6h</link>
      <guid>https://dev.to/faidterence/flutter-dos-and-donts-writing-clean-and-efficient-code-4a6h</guid>
      <description>&lt;p&gt;So, you're building apps with Flutter? That's awesome! Flutter is a fantastic tool for creating beautiful apps for phones, computers, and the web using just one code.&lt;/p&gt;

&lt;p&gt;But how do you go from just making an app to making a great app? It's like cooking: anyone can follow a recipe, but a great chef knows the little secrets that make the food amazing. This guide will share those secrets for Flutter. We'll look at some simple "Dos" and "Don'ts" to help you write code that is clean, fast, and easy to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Do: Break Down Your Screen into Small Pieces
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Don't&lt;/strong&gt;: Don't put the code for your entire screen in one giant piece. It gets messy, hard to read, and if you change one tiny thing, the whole screen has to reload, which is slow.&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;// Don't do this! It's one giant piece of code.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileScreen&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="n"&gt;CircleAvatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;radius:&lt;/span&gt; &lt;span class="mi"&gt;50&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="s"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;24&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="s"&gt;'About Me: I love Flutter!'&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="s"&gt;'Location: Kigali, Rwanda'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Edit'&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Do&lt;/strong&gt;: Break your screen into smaller, reusable widgets. This keeps your code clean and helps your app run faster because Flutter only reloads the small piece that changes.&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;// Do this! It's organized into small pieces.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileScreen&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="n"&gt;ProfileHeader&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="n"&gt;UserDetails&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="n"&gt;ActionButtons&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Each piece is separate and reusable&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProfileHeader&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;CircleAvatar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;radius:&lt;/span&gt; &lt;span class="mi"&gt;50&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="s"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;24&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;h2&gt;
  
  
  2. Do: Handle Your App's Memory with Care
&lt;/h2&gt;

&lt;p&gt;Imagine your app has a "memory" or "state," like the score in a game or a list of items in a shopping cart. When that memory changes, your screen needs to update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Don't&lt;/strong&gt;: Don't just call &lt;code&gt;setState()&lt;/code&gt; every time something changes. setState() is like a panic button that tells your entire screen, "Everyone, update now!" Even parts of the screen that didn't change have to do work, which is inefficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Do&lt;/strong&gt;: Use a proper state management tool (like Provider or Riverpod). Think of it as a smart manager. Instead of yelling at everyone, the manager just taps the specific worker (widget) on the shoulder that needs to update. This is much more efficient.&lt;/p&gt;

&lt;p&gt;Here's a simple idea of how Provider works for a counter app:&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;// This class holds the count data.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ChangeNotifier&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;_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;value&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;_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;increment&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="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;notifyListeners&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// This tells the smart manager to update only what's needed.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// In the UI, you just "watch" for changes.&lt;/span&gt;
&lt;span class="c1"&gt;// Only this Text widget will update when the count changes!&lt;/span&gt;
&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'The count is: &lt;/span&gt;&lt;span class="si"&gt;${context.watch&amp;lt;Counter&amp;gt;().value}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Do: Use const to Give Your App a Break
&lt;/h2&gt;

&lt;p&gt;Some parts of your app never, ever change. For example, a color, a title text, or the padding around a button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Don't&lt;/strong&gt;: Don't let Flutter rebuild things that are never going to change. It's a waste of your phone's battery and processing power.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Do&lt;/strong&gt;: Add the word &lt;code&gt;const&lt;/code&gt; in front of any widget that is constant. This is a signal to Flutter that says, "Hey, this piece is finished. You never have to build it again." It's one of the easiest ways to make your app faster.&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;// Don't do this:&lt;/span&gt;
&lt;span class="n"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="n"&gt;EdgeInsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;16.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Hello, Kigali!'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Do this! Add `const`.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nf"&gt;Padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="n"&gt;EdgeInsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;16.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Hello, Kigali!'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a small change that makes a big difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Do: Make Your App Look Good on All Screens
&lt;/h2&gt;

&lt;p&gt;Your app will be used on tiny phones, huge tablets, and everything in between. You need to make sure it looks good on all of them!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Don't&lt;/strong&gt;: Don't hardcode sizes using fixed numbers. When you write &lt;code&gt;SizedBox(height: 20)&lt;/code&gt;, that 20 pixels might look perfect on your phone, but it could be too tiny on a tablet or too big on a small phone.&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;// Don't: Fixed sizes don't adapt&lt;/span&gt;
&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&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="s"&gt;'Welcome!'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Always 20 pixels&lt;/span&gt;
    &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Always 40 pixels&lt;/span&gt;
    &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Click'&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;&lt;strong&gt;The Do&lt;/strong&gt;: Use &lt;code&gt;MediaQuery&lt;/code&gt; to make your sizes responsive. Think of MediaQuery as a measuring tape that tells you how big the screen is, so you can adjust accordingly.&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;// Do: Responsive sizes&lt;/span&gt;
&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&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="s"&gt;'Welcome!'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="n"&gt;MediaQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 2% of screen&lt;/span&gt;
    &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="n"&gt;MediaQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 5% of screen&lt;/span&gt;
    &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Click'&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;// Pro tip: Create a helper&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;screenHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MediaQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Then use: SizedBox(height: screenHeight(context) * 0.02)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Do: Keep Your Styles Consistent with Themes
&lt;/h2&gt;

&lt;p&gt;Imagine if every page in a book used a different font, size, and color. It would be chaos! The same goes for your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Don't&lt;/strong&gt;: Don't write the same style code over and over again throughout your app. This is like copying and pasting – if you want to change the color later, you'll have to hunt down every single place you used it.&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;// Don't: Inline styles everywhere&lt;/span&gt;
&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&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="s"&gt;'Welcome!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&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="s"&gt;'Profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&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="s"&gt;'Settings'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&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="s"&gt;'About'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&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;&lt;strong&gt;The Do&lt;/strong&gt;: Define your styles once in your app's theme, then use them everywhere. It's like having a style guide that your whole app follows. Want to change all your headings from blue to green? Just change it in one place!&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;// Do: Define theme once&lt;/span&gt;
&lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;theme:&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;textTheme:&lt;/span&gt; &lt;span class="n"&gt;TextTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;headlineMedium:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;bodyLarge:&lt;/span&gt; &lt;span class="n"&gt;TextStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;fontSize:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;grey&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;600&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;// Use theme everywhere&lt;/span&gt;
&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;children:&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="s"&gt;'Welcome!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headlineMedium&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="s"&gt;'Profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headlineMedium&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="s"&gt;'Settings'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bodyLarge&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="s"&gt;'About'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bodyLarge&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;
  
  
  6. Do: Navigate Like a Pro with Named Routes
&lt;/h2&gt;

&lt;p&gt;Navigation is like the roads in your app – users need clear paths to get where they're going. Let's make sure they don't get lost!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Don't&lt;/strong&gt;: Don't use direct navigation with anonymous routes everywhere. It's like giving directions by saying "turn left, then right, then left again" instead of using street names.&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;// Don't: Anonymous routes everywhere&lt;/span&gt;
&lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MaterialPageRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProfileScreen&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Later in another file...&lt;/span&gt;
&lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;MaterialPageRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProfileScreen&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;&lt;strong&gt;The Do&lt;/strong&gt;: Use named routes or even better, a routing package like go_router. It's like having a GPS for your app!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Named Routes (Built-in)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define routes once in your app&lt;/span&gt;
&lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;routes:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HomeScreen&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s"&gt;'/profile'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProfileScreen&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s"&gt;'/settings'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SettingsScreen&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;// Navigate using names&lt;/span&gt;
&lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pushNamed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'/profile'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: go_router (Recommended for larger apps)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define your router&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;routes:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;HomeScreen&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/profile/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;ProfileScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;userId:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/settings'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;SettingsScreen&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;// Use it in your app&lt;/span&gt;
&lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;routerConfig:&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Navigate with parameters&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/profile/123'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/settings'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why go_router is awesome:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URL-based navigation (great for web apps)&lt;/li&gt;
&lt;li&gt;Easy parameter passing&lt;/li&gt;
&lt;li&gt;Deep linking support&lt;/li&gt;
&lt;li&gt;Navigation guards for authentication
&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;// Pro tip: Protect routes with guards&lt;/span&gt;
&lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/admin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;redirect:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Auth check&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;isLoggedIn&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="s"&gt;'/login'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;AdminScreen&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;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;These simple "Dos" and "Don'ts" will help you write Flutter code that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clean&lt;/strong&gt;: Easy to read and maintain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast&lt;/strong&gt;: Efficient and responsive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable&lt;/strong&gt;: Works on all screen sizes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent&lt;/strong&gt;: Looks professional and polished&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember, great apps aren't just about what users see – they're also about the quality of the code behind the scenes.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>#flutter #firebase</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Mon, 03 Mar 2025 13:33:32 +0000</pubDate>
      <link>https://dev.to/faidterence/flutter-firebase-1ea7</link>
      <guid>https://dev.to/faidterence/flutter-firebase-1ea7</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/faidterence" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2F1143188%2F4e545937-58c4-4c00-a13a-f83c290b21ca.jpeg" alt="faidterence"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/faidterence/crud-operation-flutter-firebase-auth-firestore-57p2" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;CRUD Operation - Flutter , Firebase Auth &amp;amp; Firestore&lt;/h2&gt;
      &lt;h3&gt;Terence Faid JABO  ・ Mar 3&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>CRUD Operation - Flutter , Firebase Auth &amp; Firestore</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Mon, 03 Mar 2025 13:25:21 +0000</pubDate>
      <link>https://dev.to/faidterence/crud-operation-flutter-firebase-auth-firestore-57p2</link>
      <guid>https://dev.to/faidterence/crud-operation-flutter-firebase-auth-firestore-57p2</guid>
      <description>&lt;p&gt;This guide walks you through building a basic note-taking app. Users can sign up or log in with their email and password and then add/view their own notes. The app uses Flutter for the UI, Firebase Authentication for user sign in/up, and Firestore to store notes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Flutter is an open-source UI software development kit created by Google. It can be used to develop cross platform applications from a single codebase for the web, Android, iOS, Linux, macOS, and Windows.&lt;/li&gt;
&lt;li&gt;Firebase is a set of backend clouding computing services and application development platforms provided by Google.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Flutter over other options?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It doesn't rely on web browser technology nor the set of widgets that ship with each device. Instead, Flutter uses its own high-performance rendering engine to draw widgets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to use Firebase ?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Small to medium-sized projects&lt;/li&gt;
&lt;li&gt;Real-time Operations&lt;/li&gt;
&lt;li&gt;Authentication and User Management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When not use Firebase ?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Complex Backend Logic&lt;/li&gt;
&lt;li&gt;Large-Scale Enterprise Applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flutter:&lt;/strong&gt; Install Flutter by following the &lt;a href="https://docs.flutter.dev/get-started/install?gad_source=1&amp;amp;gclid=CjwKCAiAt4C-BhBcEiwA8Kp0CfkB_InMh_mUKS7AeXVVFv6xaItv8l5-Jtc3CmB2qiG4ZrWHUqhVpxoCgnEQAvD_BwE&amp;amp;gclsrc=aw.ds" rel="noopener noreferrer"&gt;Flutter Installation Guide.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Account:&lt;/strong&gt; Sign up at the Firebase Console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Editor:&lt;/strong&gt; Use VS Code, Android Studio, or any editor of your choice.&lt;/li&gt;
&lt;li&gt;Node: Make sure node is installed globally on your device.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Create a New Flutter Project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open your terminal.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Run the command:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter create simple_note_app

&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Navigate into your project folder:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;simple_note_app

&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Open the project in your code editor.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Set Up Firebase
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Go to the Firebase Console.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Click "Add Project" and follow the instructions.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a new app to your project:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;For mobile apps, select the Android/iOS option as needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable Authentication:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In the Firebase Console, go to &lt;strong&gt;Authentication&lt;/strong&gt; and enable the &lt;strong&gt;Email/Password&lt;/strong&gt; sign-in method.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Set Up Firebase &amp;amp; FlutterFire CLI Tools
&lt;/h2&gt;

&lt;p&gt;These commands help you manage Firebase projects and configure your Flutter project to use Firebase.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Install Firebase CLI Globally&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;npm install -g firebase-tools&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;​&lt;br&gt;
This command installs the Firebase CLI globally using Node Package Manager (npm)&lt;br&gt;
Log in to Firebase&lt;br&gt;
&lt;code&gt;firebase login&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Activate FlutterFire CLI&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dart pub global activate flutterfire_cli&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;​&lt;br&gt;
This command activates the FlutterFire CLI globally using Dart's package manager.&lt;br&gt;
Configure FlutterFire in Your Project&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flutterfire configure&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
​&lt;br&gt;
This command configures your Flutter project with your Firebase project. It generates the necessary configuration files and links your app to Firebase.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4: Add Firebase Dependencies
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open &lt;code&gt;terminal&lt;/code&gt; in your Flutter project.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add these dependencies&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub add firebase_core firebase_auth cloud_firestore

&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;firebase_core:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the main package for connecting your app to Firebase. It initializes Firebase services and is required by other Firebase plugins.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;firebase_auth:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This package allows you to add user authentication to your app.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;cloud_firestore:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This package provides access to Cloud Firestore, a flexible, real-time NoSQL database from Firebase. It lets you store, sync, and retrieve user notes easily.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5 : Create Re-usable compose
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd lib &amp;amp;&amp;amp; mkdir components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Text Input
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';

class Customtextfield extends StatelessWidget {
  final String hintText;
  final bool obscureText;
  final TextEditingController textEditingController;
  const Customtextfield({
    super.key,
    required this.hintText,
    required this.obscureText,
    required this.textEditingController,
  });

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: textEditingController,
      obscureText: obscureText,
      decoration: InputDecoration(
        hintText: hintText,
        border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;TextEditingController&lt;/code&gt; in Flutter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages the text within a &lt;code&gt;TextField&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Allows you to get, set, and listen to changes in that text.&lt;/li&gt;
&lt;li&gt;It is the tool that allows you to control the text field.&lt;/li&gt;
&lt;li&gt;Button
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';

class CustomButton extends StatelessWidget {
  final String text;
  final void Function() onPressed;
  const CustomButton({super.key, required this.text, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onPressed,
      child: Container(
        decoration: BoxDecoration(
          color: Colors.black54,
          borderRadius: BorderRadius.circular(10),
        ),
        padding: const EdgeInsets.all(25),
        child: Center(
          child: Text(
            text,
            style: const TextStyle(
              color: Colors.white,
              fontSize: 20,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Message Util
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';

void displayMessageToUser(BuildContext context, String message, Color color) {
  final snackBar = SnackBar(content: Text(message), backgroundColor: color);
  ScaffoldMessenger.of(context).showSnackBar(snackBar);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;StatelessWidget:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use cases:&lt;/strong&gt; Displaying static content, simple UI elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;StatefulWidget:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use cases:&lt;/strong&gt; Handling user interactions, displaying dynamic content, managing data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6: Initialize Firebase in Your App
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;lib/main.dart&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Update the main function to:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:firebase_core/firebase_core.dart';
import 'package:project_name/firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(const MyApp());
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7 : Create the Login and Register Screen
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Login
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_simple_auth/components/custom_button.dart';
import 'package:flutter_simple_auth/components/custom_text_field.dart';
import 'package:flutter_simple_auth/utils/display_message_user_util.dart';

class LoginScreen extends StatefulWidget {
  final Function()? onTap;
  const LoginScreen({super.key, this.onTap});

  @override
  State&amp;lt;LoginScreen&amp;gt; createState() =&amp;gt; _LoginScreenState();
}

class _LoginScreenState extends State&amp;lt;LoginScreen&amp;gt; {
  final TextEditingController emailController = TextEditingController();

  final TextEditingController passwordController = TextEditingController();

  void loginUser() async {
    // show a loader
    showDialog(
      context: context,
      builder: (context) =&amp;gt; Center(child: CircularProgressIndicator()),
    );

    try {
      if (passwordController.text.isEmpty || emailController.text.isEmpty) {
        // pop loader
        if (mounted) {
          Navigator.pop(context);
        }

        // show error message
        displayMessageToUser(context, "Please fill all fields", Colors.red);
        return;
      }

      await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: emailController.text,
        password: passwordController.text,
      );

      // pop loader
      if (!mounted) return;
      Navigator.pop(context);
    } on FirebaseAuthException catch (e) {
      // pop loader
      if (!mounted) return;
      Navigator.pop(context);

      // show error message
      if (mounted) {
        displayMessageToUser(
          context,
          e.message ?? "An error occurred",
          Colors.red,
        );
      }
    } catch (e) {
      // pop loader
      if (!mounted) return;
      Navigator.pop(context);

      // show error message
      if (mounted) {
        displayMessageToUser(context, e.toString(), Colors.red);
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(25),
          child: Column(
            spacing: 25,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.person, size: 80),
              // app name
              Text("C O D E X", style: TextStyle(fontSize: 20)),

              // email field
              Customtextfield(
                hintText: "Email",
                obscureText: false,
                textEditingController: emailController,
              ),

              // Password
              Customtextfield(
                hintText: "Password",
                obscureText: true,
                textEditingController: passwordController,
              ),

              CustomButton(text: "Login", onPressed: loginUser),

              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                spacing: 8,
                children: [
                  Text("Don't have an account?"),
                  GestureDetector(
                    onTap: widget.onTap,
                    child: Text(
                      "Sign Up",
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Register Screen
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_simple_auth/components/custom_button.dart';
import 'package:flutter_simple_auth/components/custom_text_field.dart';
import 'package:flutter_simple_auth/utils/display_message_user_util.dart';

class RegisterScreen extends StatefulWidget {
  final Function()? onTap;
  const RegisterScreen({super.key, this.onTap});

  @override
  State&amp;lt;RegisterScreen&amp;gt; createState() =&amp;gt; _RegisterScreenState();
}

class _RegisterScreenState extends State&amp;lt;RegisterScreen&amp;gt; {
  final TextEditingController emailController = TextEditingController();

  final TextEditingController usernameController = TextEditingController();

  final TextEditingController passwordController = TextEditingController();

  final TextEditingController confirmPasswordController =
      TextEditingController();

  // function to register a user
  void registerUser() async {
    // show a loader
    showDialog(
      context: context,
      builder: (context) =&amp;gt; Center(child: CircularProgressIndicator()),
    );

    // check password match
    if (passwordController.text != confirmPasswordController.text) {
      // show error message
      displayMessageToUser(context, "Passwords do not match", Colors.red);
    } else {
      // create user
      try {
        await FirebaseAuth.instance.createUserWithEmailAndPassword(
          email: emailController.text,
          password: passwordController.text,
        );

        // pop loader
        Navigator.pop(context);
      } on FirebaseAuthException catch (e) {
        // pop loader
        Navigator.pop(context);

        // show error message
        displayMessageToUser(
          context,
          e.message ?? "An error occurred",
          Colors.red,
        );
      } catch (e) {
        // pop loader
        Navigator.pop(context);

        // show error message
        displayMessageToUser(
          context,
          "An unexpected error occurred",
          Colors.red,
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(25),
          child: Column(
            spacing: 25,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.person, size: 80),
              // app name
              Text("C O D E X", style: TextStyle(fontSize: 20)),

              // email field
              Customtextfield(
                hintText: "Email",
                obscureText: false,
                textEditingController: emailController,
              ),

              // username
              Customtextfield(
                hintText: "Username",
                obscureText: false,
                textEditingController: usernameController,
              ),
              // Password
              Customtextfield(
                hintText: "Password",
                obscureText: true,
                textEditingController: passwordController,
              ),

              // Confirm Password field
              Customtextfield(
                hintText: "Confirm Password",
                obscureText: true,
                textEditingController: confirmPasswordController,
              ),

              CustomButton(text: "Register", onPressed: registerUser),

              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                spacing: 8,
                children: [
                  Text("""Already have an account?"""),
                  GestureDetector(
                    onTap: widget.onTap,
                    child: Text(
                      "Login",
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8 :
&lt;/h3&gt;

&lt;p&gt;The AuthHelper helps to toggle between Login and Register pages.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd lib &amp;amp;&amp;amp; mkdir auth &amp;amp;&amp;amp; touch/auth_helper.dart&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
class AuthHelper extends StatefulWidget {
  const AuthHelper({super.key});

  @override
  State&amp;lt;AuthHelper&amp;gt; createState() =&amp;gt; _AuthHelperState();
}

class _AuthHelperState extends State&amp;lt;AuthHelper&amp;gt; {
  // initially show login page

  bool showLogin = true;

  // function to toggle between login and register page

  void toggleView() {
    setState(() {
      showLogin = !showLogin;
    });
  }

  @override
  Widget build(BuildContext context) {
    return showLogin
        ? LoginScreen(onTap: toggleView)
        : RegisterScreen(onTap: toggleView);
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 9 :
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cd lib/auth &amp;amp;&amp;amp; touch/firebase_navigator_helper.dart&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
class FirebaseNavigatorHelper extends StatelessWidget {
  const FirebaseNavigatorHelper({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder(
        stream: FirebaseAuth.instance.authStateChanges(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasData) {
            return WelcomeScreen();
          } else if (snapshot.hasError) {
            return const Center(child: Text("An error occurred"));
          } else {
            return AuthHelper();
          }
        },
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PART TWO :
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Notes CRUD Operation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1 :&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd lib &amp;amp;&amp;amp; mkdir services &amp;amp;&amp;amp; touch firestore_notes_service.dart&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class FirestoreNotesServices {
  // get collection for notes

  final CollectionReference notesCollection = FirebaseFirestore.instance
      .collection('notes');

  final currentUser = FirebaseAuth.instance.currentUser;

  // CREATE with logged in user
  Future&amp;lt;void&amp;gt; createNote(String note) {
    return notesCollection.add({
      'note': note,
      'ownerId': currentUser?.uid,
      'createdAt': Timestamp.now(),
    });
  }

  // retrieve notes for logged in user
  Stream&amp;lt;QuerySnapshot&amp;gt; getNotes() {
    return notesCollection
        .where('ownerId', isEqualTo: currentUser?.uid)
        .snapshots();
  }

  // Update note

  Future&amp;lt;void&amp;gt; updateNote(String note, String id) {
    return notesCollection.doc(id).update({'note': note});
  }

  // Delete note

  Future&amp;lt;void&amp;gt; deleteNotes(String id) {
    return notesCollection.doc(id).delete();
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 : Integration&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;cd lib/components &amp;amp;&amp;amp; touch note_list.dart&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
class NotesList extends StatelessWidget {
  final List notes;
  final FirestoreNotesServices firestoreNotesServices;
  final Function(String noteId, String currentNote) onUpdateNote;

  const NotesList({
    super.key,
    required this.notes,
    required this.firestoreNotesServices,
    required this.onUpdateNote,
  });

  @override
  Widget build(BuildContext context) {
    if (notes.isEmpty) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.note_add, size: 64, color: Colors.grey[400]),
            const SizedBox(height: 16),
            Text(
              "No notes found",
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[600],
              ),
            ),
            const SizedBox(height: 8),
            Text(
              "Tap the + button to add your first note!",
              style: TextStyle(fontSize: 14, color: Colors.grey[500]),
            ),
          ],
        ),
      );
    }

    return ListView.builder(
      itemCount: notes.length,
      itemBuilder: (context, index) {
        return NoteCard(
          note: notes[index]['note'],
          noteId: notes[index].id,
          firestoreNotesServices: firestoreNotesServices,
          onUpdateNote: onUpdateNote,
        );
      },
    );
  }
}

class NoteCard extends StatelessWidget {
  final String note;
  final String noteId;
  final FirestoreNotesServices firestoreNotesServices;
  final Function(String noteId, String currentNote) onUpdateNote;

  const NoteCard({
    super.key,
    required this.note,
    required this.noteId,
    required this.firestoreNotesServices,
    required this.onUpdateNote,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8.0),
      child: Card(
        elevation: 2,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        child: ListTile(
          contentPadding: const EdgeInsets.symmetric(
            horizontal: 16,
            vertical: 8,
          ),
          title: Text(note, style: const TextStyle(fontSize: 16)),
          trailing: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              IconButton(
                icon: const Icon(Icons.edit, color: Colors.blue),
                onPressed: () =&amp;gt; onUpdateNote(noteId, note),
              ),
              IconButton(
                icon: const Icon(Icons.delete, color: Colors.red),
                onPressed: () {
                  _showDeleteConfirmation(context);
                },
              ),
            ],
          ),
          onTap: () =&amp;gt; onUpdateNote(noteId, note),
        ),
      ),
    );
  }

  void _showDeleteConfirmation(BuildContext context) {
    showDialog(
      context: context,
      builder:
          (context) =&amp;gt; AlertDialog(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(16),
            ),
            title: const Text("Delete Note"),
            content: const Text("Are you sure you want to delete this note?"),
            actions: [
              TextButton(
                onPressed: () =&amp;gt; Navigator.pop(context),
                child: const Text(
                  "Cancel",
                  style: TextStyle(color: Colors.grey),
                ),
              ),
              ElevatedButton(
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.red,
                  foregroundColor: Colors.white,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                ),
                onPressed: () {
                  firestoreNotesServices.deleteNotes(noteId);
                  Navigator.pop(context);
                },
                child: const Text("Delete"),
              ),
            ],
          ),
    );
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly , create a home Screen &lt;br&gt;
to display logged in user notes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State&amp;lt;HomeScreen&amp;gt; createState() =&amp;gt; _HomeScreenState();
}

class _HomeScreenState extends State&amp;lt;HomeScreen&amp;gt; {
  // note controller
  final TextEditingController noteController = TextEditingController();

  // firebase instance
  final currentUser = FirebaseAuth.instance.currentUser;
  final FirestoreNotesServices firestoreNotesServices =
      FirestoreNotesServices();

  void logout() {
    FirebaseAuth.instance.signOut();
  }

  // open a dialog to add note
  void addNoteDialog() {
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16),
          ),
          title: const Text(
            "Add Note",
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
          content: Customtextfield(
            hintText: "Enter Note",
            obscureText: false,
            textEditingController: noteController,
          ),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text("Cancel", style: TextStyle(color: Colors.grey)),
            ),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.black87,
                foregroundColor: Colors.white,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
              ),
              onPressed: () {
                firestoreNotesServices.createNote(noteController.text);
                noteController.clear();
                Navigator.pop(context);
              },
              child: const Text("Add"),
            ),
          ],
        );
      },
    );
  }

  // Dialog for updating notes
  void updateNoteDialog(String noteId, String currentNote) {
    noteController.text = currentNote;
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16),
          ),
          title: const Text(
            "Update Note",
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
          content: Customtextfield(
            hintText: "Enter Note",
            obscureText: false,
            textEditingController: noteController,
          ),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text("Cancel", style: TextStyle(color: Colors.grey)),
            ),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.black87,
                foregroundColor: Colors.white,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
              ),
              onPressed: () {
                firestoreNotesServices.updateNote(noteController.text, noteId);
                noteController.clear();
                Navigator.pop(context);
              },
              child: const Text("Update"),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[100],
      appBar: AppBar(
        backgroundColor: Colors.black87,
        foregroundColor: Colors.white,
        elevation: 0,
        centerTitle: true,
        title: const Text(
          "Notes App",
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        actions: [
          IconButton(
            onPressed: () {
              logout();
            },
            icon: const Icon(Icons.logout),
            tooltip: "Logout",
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          addNoteDialog();
        },
        backgroundColor: Colors.black87,
        child: const Icon(Icons.add, color: Colors.white),
      ),
      body: Padding(
        padding: const EdgeInsets.all(12.0),
        child: StreamBuilder(
          stream: firestoreNotesServices.getNotes(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(
                child: CircularProgressIndicator(color: Colors.black87),
              );
            }

            if (snapshot.hasError) {
              return Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    const Icon(
                      Icons.error_outline,
                      size: 48,
                      color: Colors.red,
                    ),
                    const SizedBox(height: 16),
                    Text(
                      "Error: ${snapshot.error}",
                      style: const TextStyle(fontSize: 16),
                    ),
                  ],
                ),
              );
            }

            // Check if we have data
            if (snapshot.hasData) {
              List notes = snapshot.data!.docs;

              return NotesList(
                notes: notes,
                firestoreNotesServices: firestoreNotesServices,
                onUpdateNote: updateNoteDialog,
              );
            } else {
              return const Center(child: Text("No notes available"));
            }
          },
        ),
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Best Practices for Branch Naming Conventions and Effective Commit Messages</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Thu, 29 Feb 2024 20:44:48 +0000</pubDate>
      <link>https://dev.to/faidterence/best-practices-for-branch-naming-conventions-and-effective-commit-messages-ma8</link>
      <guid>https://dev.to/faidterence/best-practices-for-branch-naming-conventions-and-effective-commit-messages-ma8</guid>
      <description>&lt;p&gt;In the world of software development, maintaining clear and organized workflows is essential for effective collaboration and code management. Two key aspects that greatly contribute to this are branch naming conventions and writing good commit messages. Let's explore some best practices for both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Branch Naming Conventions:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Be Descriptive&lt;/strong&gt;: Branch names should be descriptive and indicative of the task or feature being worked on. A clear and concise name can save time and reduce confusion for team members. For example, feature/user-authentication or bugfix/issue-123-fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prefixes for Categorization:&lt;/strong&gt;  Prefixes such as feature/, bugfix/, hotfix/, release/, refactor/, and docs/ can categorize branches based on their purpose. This standardizes naming conventions and makes it easier to identify the type of work associated with each branch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inclusion of Ticket or Issue Numbers:&lt;/strong&gt; Including ticket or issue numbers in branch names helps establish traceability between code changes and specific tasks or bug reports. This practice fosters transparency and facilitates collaboration among team members. For example, feature/PROJ-123-add-user-registration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistency is Key:&lt;/strong&gt; Consistency in naming conventions is paramount for effective communication and collaboration. Establishing a team-wide standard ensures that everyone understands and adheres to the same naming conventions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep it Concise:&lt;/strong&gt; While descriptive, branch names should also be kept concise to avoid unnecessary complexity. Long branch names can be unwieldy and may get truncated in certain interfaces or tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good Commit Messages:
&lt;/h2&gt;

&lt;p&gt;Commit messages provide valuable context and insight into the changes made to a codebase. They serve as documentation for future reference and help developers understand the reasoning behind specific code alterations. Here are some guidelines for crafting effective commit messages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with a Prefix:&lt;/strong&gt; Some teams use prefixes like "feat" for new features or "chore" for maintenance tasks and other non-feature changes. This helps quickly identify the nature of the commit at a glance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow with a Verb:&lt;/strong&gt; After the prefix, include a verb that succinctly describes the action taken in the commit. For example, use "Add," "Fix," "Update," "Remove," or "Refactor."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be Specific:&lt;/strong&gt; Provide clear and specific details in your commit message to explain what was changed and why it was changed. This clarity aids in understanding the purpose and impact of each commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limit Line Length:&lt;/strong&gt; Keep commit messages concise and limit line length to around 50-72 characters. This ensures they are easily readable in various contexts, such as terminal outputs or version control tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the Imperative Mood:&lt;/strong&gt; Write commit messages in the imperative mood, as if you are giving a command. For example, "Fix bug" rather than "Fixed bug" or "Fixes bug."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reference Relevant Issues:&lt;/strong&gt; If your commit resolves a specific issue or relates to a particular task, reference the corresponding issue or ticket number in the commit message. This helps establish a clear connection between code changes and project issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good Branch Names Examples:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;feature/user-authentication:&lt;/strong&gt; This branch focuses on implementing user authentication functionality.&lt;br&gt;
&lt;strong&gt;bugfix/issue-123-fix:&lt;/strong&gt; Addressing a specific bug identified in issue 123.&lt;br&gt;
refactor/database-schema-update: Updating the database schema to improve performance.&lt;br&gt;
&lt;strong&gt;chore/update-dependencies:&lt;/strong&gt; Routine maintenance task to update project dependencies.&lt;br&gt;
release/version-1.2.0: Branch for preparing the release of version 1.2.0 of the software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good Commit Messages Examples:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;feat: Implement user registration functionality&lt;/strong&gt;&lt;br&gt;
Description: Adds user registration feature with email verification.&lt;br&gt;
&lt;strong&gt;chore: Update package dependencies&lt;/strong&gt;&lt;br&gt;
Description: Updates project dependencies to latest versions for security and performance improvements.&lt;br&gt;
&lt;strong&gt;fix: Resolve issue with form validation&lt;/strong&gt;&lt;br&gt;
Description: Fixes bug where form validation was not handling edge cases correctly.&lt;br&gt;
&lt;strong&gt;refactor: Optimize database query performance&lt;/strong&gt;&lt;br&gt;
Description: Rewrites database queries to improve response times and reduce server load.&lt;br&gt;
&lt;strong&gt;docs: Update README with installation instructions&lt;/strong&gt;&lt;br&gt;
Description: Adds detailed instructions for setting up and running the application in the README file.&lt;br&gt;
These examples demonstrate how to create informative branch names and commit messages that provide clarity about the purpose and content of each branch and commit.&lt;/p&gt;

&lt;p&gt;By following these best practices for branch naming and commit messages, you can enhance clarity, transparency, and collaboration within your development team. Clear communication through well-defined branches and informative commit messages ultimately leads to smoother workflows and better-maintained codebases.&lt;/p&gt;

&lt;p&gt;~ &lt;strong&gt;Terence Faid J!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>coding</category>
    </item>
    <item>
      <title>Implementing an Auth Guard with JWT tokens in Nest.js</title>
      <dc:creator>Terence Faid JABO </dc:creator>
      <pubDate>Sat, 24 Feb 2024 19:28:39 +0000</pubDate>
      <link>https://dev.to/faidterence/implementing-an-auth-guard-with-jwt-tokens-in-nestjs-3o95</link>
      <guid>https://dev.to/faidterence/implementing-an-auth-guard-with-jwt-tokens-in-nestjs-3o95</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2Fomdp2fzgoursm3ujglxs.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fomdp2fzgoursm3ujglxs.jpg" alt="Image description" width="720" height="405"&gt;&lt;/a&gt;&lt;br&gt;
Authentication guards allow you to control access to routes and controllers in a NestJS application based on user authentication. An Auth Guard is very similar to middlewares in Express.js&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.amazonaws.com%2Fuploads%2Farticles%2F8y0nnczvco8nyuhr3bzq.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2F8y0nnczvco8nyuhr3bzq.jpg" alt="Image description" width="720" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Auth Guard which is applied to an endpoint/api or a controller in Nest.js only allows authenticated users to access those routes. A token is attached in the incoming request headers, Auth Guard extracts and validates the token, and uses the extracted information to determine whether to process the request further or not.&lt;/p&gt;

&lt;p&gt;In this blog, we will look at how to implement basic authentication guards using JWT tokens to protect routes in a NestJS app.&lt;/p&gt;

&lt;p&gt;I have created a basic Nest.js CRUD application for demonstration which looks likes this:&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.amazonaws.com%2Fuploads%2Farticles%2Fzlseolju1h0zga9gw5qw.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fzlseolju1h0zga9gw5qw.jpg" alt="Image description" width="333" height="825"&gt;&lt;/a&gt;&lt;br&gt;
There is student controller which has basic read/write operations (GET, POST requests) on which we shall apply our auth guard.&lt;/p&gt;

&lt;p&gt;Create an auth.guard.ts file and let’s start creating an auth guard. First, lets have the necessary imports&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auth.guard.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, 
ForbiddenException,} from '@nestjs/common';

import { AuthService } from './auth.service';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;auth.guard.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Injectable()
export class AuthGuard implements CanActivate {
    async canActivate(context: ExecutionContext): Promise&amp;lt;boolean&amp;gt; {
      try {
        const request = context.switchToHttp().getRequest();
        return true;
      } catch (error) {
        console.log('auth error - ', error.message);
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authentication guards are classes that implement the CanActivate interface from @nestjs/common. This interface requires a canActivate method/function that returns a boolean. Here we access the incoming request using context.switchToHttp().getRequest() method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { authorization }: any = request.headers;
if (!authorization || authorization.trim() === '') {
    throw new UnauthorizedException('Please provide token');
}
const authToken = authorization.replace(/bearer/gim, '').trim();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We extract the token from Authorization headers in the request. If there is no Authorization header or no token in the Authorization Header, it throws and error to provide token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const resp = await this.authService.validateToken(authToken);
request.decodedData = resp;
return true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created a function in AuthService to validate our jwt token which if valid then attaches the decodedData i.e payload(user data by which we create our jwt token) to the request and returns true to further process the request. And if token isnt’t valid, then throws an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constructor(private readonly authService: AuthService) {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we are using AuthService, lets import it as a dependency injection in our AuthGuard’s class constructor.&lt;/p&gt;

&lt;p&gt;Let’s have a look at validateToken() function in AuthService.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
    constructor(private readonly jwtServ: JwtService) {}

    validateToken(token: string) {
        return this.jwtServ.verify(token, {
            secret : process.env.JWT_SECRET_KEY
        });
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are using JwtService from Nest.js and also using it as a dependency injection in our class’s constructor to use its function to validate our jwt token. We use verify() function which takes token and secret key to verify the token. This would be the same secret key to create our jwt token in the sign-in &amp;amp; sign-up API’s. Use a .env file to store our JWT_SECRET_KEY.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auth.guard.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, 
ForbiddenException,} from '@nestjs/common';
import { AuthService } from './auth.service';

  @Injectable()
  export class AuthGuard implements CanActivate {
    constructor(private readonly authService: AuthService) {}

    async canActivate(context: ExecutionContext): Promise&amp;lt;boolean&amp;gt; {
      try {
        const request = context.switchToHttp().getRequest();
        const { authorization }: any = request.headers;
        if (!authorization || authorization.trim() === '') {
          throw new UnauthorizedException('Please provide token');
        }
        const authToken = authorization.replace(/bearer/gim, '').trim();
        const resp = await this.authService.validateToken(authToken);
        request.decodedData = resp;
        return true;
      } catch (error) {
        console.log('auth error - ', error.message);
        throw new ForbiddenException(error.message || 'session expired! Please sign In');
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets apply our Auth guard to our Api routes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;student.controller.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { AuthGuard } from 'src/auth/auth.guard';

@Controller('student')
export class StudentController {
    constructor(private readonly studentService: StudentService) { }

    @Get()
    async getStudents(@Res() response) {
        try {
            const studentData = await this.studentService.getAllStudents();
            return response.status(HttpStatus.OK).json({
                message: 'All students data found successfully', studentData,
            });
        } catch (err) {
            return response.status(err.status).json(err.response);
        }
    }

    @UseGuards(AuthGuard)
    @Get('/:id')
    async getStudentById(@Res() response, @Param('id') studentId: string) {
        try {
            const existingStudent = await this.studentService.getStudent(studentId);
            return response.status(HttpStatus.OK).json({
                message: 'Student found successfully', existingStudent,
            });
        } catch (err) {
            return response.status(err.status).json(err.response);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have 2 routes getStudents() to get all students which is an open route and getStudentById() to get a specific student details which is a protected route. We use @UseGuards(AuthGuard) decorator and pass our AuthGuard to it which protects the getStudentById() route.&lt;/p&gt;

&lt;p&gt;We can also attach the AuthGuard to an entire controller rather than specific routes which makes the entire Controller as Protected Controller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@UseGuards(AuthGuard)
@Controller('student')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now test your endpoints using postman or thunderclient&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
