<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[fz3hra's blog]]></title><description><![CDATA[Join me on an adventure into the world of tech, where we explore cutting-edge innovations and connect with thriving communities, all in the pursuit of becoming something greater.✨]]></description><link>https://zaahramujore.com/</link><image><url>https://zaahramujore.com/favicon.png</url><title>fz3hra&apos;s blog</title><link>https://zaahramujore.com/</link></image><generator>Ghost 5.68</generator><lastBuildDate>Thu, 16 Apr 2026 19:34:30 GMT</lastBuildDate><atom:link href="https://zaahramujore.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[The Evolution of My IT Career: 5 Years In]]></title><description><![CDATA[<p>I thought it was time for a check-in. </p><p>Who would have imagined that 5 years later, I would be writing this saying that becoming a Software Engineer was absolutely worth it?</p><p>The ups and downs? I embrace all of it. They are part of the experience for a reason. Growth</p>]]></description><link>https://zaahramujore.com/the-evolution-of-my-it-career-5-years-in/</link><guid isPermaLink="false">69a1fc50fb0aa004d48ba4be</guid><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Fri, 27 Feb 2026 20:50:47 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2026/02/5.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2026/02/5.png" alt="The Evolution of My IT Career: 5 Years In"><p>I thought it was time for a check-in. </p><p>Who would have imagined that 5 years later, I would be writing this saying that becoming a Software Engineer was absolutely worth it?</p><p>The ups and downs? I embrace all of it. They are part of the experience for a reason. Growth is definitely not linear. It is built through challenges, new connections, new lessons, and by allowing yourself to evolve with change.</p><p>There is something special about working with like-minded people. When you give yourself the permission to explore, to let your soul wander, and to discover your true potential; it creates a thrilling effect, almost like a trance you don&apos;t ever want to leave. And yes... it can be addictive. But it is the kind that builds you.</p><p>Of course, with growth comes self doubt. It always shows up. But is it necessarily a bad thing? I don&apos;t think so.</p><p>We are human. We are meant to feel. Sometimes self-doubt is not a sign that you are failing but rather a sign that you are being projected towards the right direction. </p><p>My experience has taught me this; allow yourself to be guided by the process.</p><p>Feel it. Breath through it. Dance within it.</p><p>And that was 5 years in. and I am still evolving. =D</p><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[From Idea to Code - Building Rummy with Flutter - Part 2]]></title><description><![CDATA[<p>In part 1, we built the foundation of our Rummy game with the UI and the core game engine. </p><p>Traditionally, creating a computer opponent means writing algorithms. You might implement an complex decision trees or write hundreds of lines of heuristic code. This algorithmic approach is robust but rigid. It</p>]]></description><link>https://zaahramujore.com/from-idea-to-code-building-rummy-with-flutter-part-2/</link><guid isPermaLink="false">6962a6373b998104b437f20e</guid><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Sat, 10 Jan 2026 20:42:05 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1712002641088-9d76f9080889?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDl8fEFJJTIwQWdlbnR8ZW58MHx8fHwxNzY4MDc3NjI0fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1712002641088-9d76f9080889?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDl8fEFJJTIwQWdlbnR8ZW58MHx8fHwxNzY4MDc3NjI0fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="From Idea to Code - Building Rummy with Flutter - Part 2"><p>In part 1, we built the foundation of our Rummy game with the UI and the core game engine. </p><p>Traditionally, creating a computer opponent means writing algorithms. You might implement an complex decision trees or write hundreds of lines of heuristic code. This algorithmic approach is robust but rigid. It requires you, the developer, to encode the strategy explicitly.</p><h3 id="but-what-if-we-could-trade-algorithms-for-intelligence">But what if we could trade algorithms for intelligence?</h3><p>What if, instead of hard-coding how to play, we build an AI agent powered by Gemini, that plays Rummy against us. We won&apos;t teach it strategy, we will basically just hand it the cards, and say your turn.</p><p>The packages required:</p><pre><code class="language-yaml">dependencies:
  # The core SDK for accessing Gemini models via Firebase
  firebase_ai: ^3.5.0
  
  firebase_core: ^4.2.1</code></pre><p><strong>Note</strong>: Make sure you have initialised Firebase in your project.</p><h2 id="what-is-an-ai-agent">What is an &apos;AI Agent&apos;?</h2><p>In the context of software development, an&#xA0;<strong>Agent</strong>&#xA0;is more than just a chatbot. It&apos;s a system capable of:</p><ol><li><strong>Perception</strong>: Reading the environment (in our case, looking at the cards on the table).</li><li><strong>Reasoning</strong>: Deciding on a course of action based on a goal (winning the game).</li><li><strong>Action</strong>: Executing code to change the environment (drawing or discarding).</li></ol><p>When we use a Large Language Model (LLM) like Gemini as an agent, we aren&apos;t just asking it to write a poem. We are treating it as a reasoning engine. We give it the current state of the game as a JSON object, and it &quot;replies&quot; by choosing which function in our code to call.</p><h2 id="the-implementation">The Implementation</h2><p>To make the AI play the game, we need three key components working in harmony. Let&apos;s look at them in order:</p><ol><li><strong>The Engine</strong>&#xA0;(EngineController): The game state.</li><li><strong>The Brain</strong>&#xA0;(AppAgent): The AI service.</li><li><strong>The Coordinator</strong>&#xA0;(GameScreen): The UI layer.</li></ol><h3 id="1-the-engine-enginecontroller">1. The Engine:&#xA0;EngineController</h3><p>The&#xA0;EngineController&#xA0;is the &quot;rule enforcer.&quot; It holds the deck, the hands, and manages the phases (Drawing -&gt; Discarding). It doesn&apos;t know&#xA0;<em>who</em>&#xA0;is playing, only&#xA0;<em>what</em>&#xA0;determines a valid move.</p><p>We expose methods for the AI (and the player) to interact with the game:</p><ul><li><strong>setup():</strong> Deals the cards.</li><li><strong>handOf(playerId)</strong>: Returns the cards for a specific player (needed for the AI to &quot;see&quot; its hand).</li><li><strong>playerDrawFromStock()&#xA0;</strong>/&#xA0;<strong>playerDrawFromDiscard()</strong>: The drawing actions.</li><li><strong>playerDiscard(card)</strong>: The ending action.</li></ul><pre><code class="language-dart">class EngineController {
  // ... state variables ...
  
  // The AI needs to see the top of the discard pile to decide if it wants it
  Card? get discardTop =&gt; discardCard.isEmpty ? null : discardCard.last;
  // The AI needs to know if moves are compliant with game phases
  bool get canDrawFromStock =&gt; phase == GamePlayState.playing &amp;&amp; !_hasDrawnThisTurn;
  // Key Action: Drawing
  void playerDrawFromStock() {
    Card card = stock.draw();
    hands.putIfAbsent(player, () =&gt; []).add(card);
    _hasDrawnThisTurn = true;
  }
  // Key Action: Discarding
  void playerDiscard(Card card) {
    final hand = hands[player]!;
    hand.remove(card);
    discardCard.add(card);
    _endTurn();
  }
}</code></pre><h3 id="2-the-brain-appagent">2. The Brain:&#xA0;AppAgent</h3><p>This is where the magic happens. The&#xA0;AppAgent&#xA0;translates the&#xA0;EngineController&apos;s state into a prompt for Gemini, and then translates Gemini&apos;s response back into&#xA0;EngineController&#xA0;function calls.</p><p>We configure Gemini with&#xA0;<strong>Function Calling</strong>&#xA0;definitions so it knows exactly what moves are legal.</p><pre><code class="language-dart">class AppAgent {
  late final ChatSession _chat;
  AppAgent() {
    // 1. Configure the Model
    final model = FirebaseAI.googleAI().generativeModel(
      model: &apos;gemini-2.5-flash&apos;,
      // ... config ...
      systemInstruction: Content.text(
        &apos;You are a Rummy opponent. In ONE response emit EXACTLY two tool calls: &apos;
        &apos;(1) draw_from_stock OR draw_from_discard, (2) discard_card. &apos;
        &apos;RULE: Only use draw_from_discard if the top card completes or improves a run (same suit consecutive) &apos;
        &apos;or a set (same rank, different suits). Otherwise draw_from_stock. &apos;
        &apos;Use ONLY tool calls. No text. Stop after the discard.&apos;,
      ),
      // 2. Define the Tools (The &quot;Language&quot; of our game)
      tools: [
        Tool.functionDeclarations([
          FunctionDeclaration(&apos;draw_from_stock&apos;, &apos;Draw from stock.&apos;, {}),
          FunctionDeclaration(&apos;draw_from_discard&apos;, &apos;Draw from discard.&apos;, {}),
          FunctionDeclaration(&apos;discard_card&apos;, &apos;Discard a card.&apos;, {
             &apos;rank&apos;: Schema.integer(...),
             &apos;suit&apos;: Schema.string(...)
          }),
        ]),
      ],
    );
    _chat = model.startChat();
  }
  // 3. The Play Loop
  Future&lt;String&gt; playTurn({required EngineController engine, required int botPlayerId}) async {
    // A. Perception: Serialize the game state
    final state = {
      &apos;hand&apos;: engine.handOf(botPlayerId).map((c) =&gt; ...).toList(),
      &apos;discardTop&apos;: engine.discardTop?.toJson(),
      &apos;canDrawFromDiscard&apos;: engine.canDrawFromDiscard,
    };
    // B. Decision: Send state to Gemini
    final response = await _chat.sendMessage(
      Content(&apos;user&apos;, [TextPart(jsonEncode(state))])
    );
    // C. Action: Execute the tool calls
    final calls = response.functionCalls;
    for (final call in calls) {
      if (call.name == &apos;draw_from_stock&apos;) {
        engine.playerDrawFromStock();
      } else if (call.name == &apos;discard_card&apos;) {
        // ... parse arguments and find the card object ...
        engine.playerDiscard(card);
      }
    }
    
    return &quot;Opponent played.&quot;;
  }
}</code></pre><h3 id="3-the-coordinator-gamescreen">3. The Coordinator:&#xA0;GameScreen</h3><p>Finally, the&#xA0;GameScreen&#xA0;ties it all together. It watches the game state; when the player finishes their turn, it triggers the AI.</p><pre><code class="language-dart">class _GameScreenState extends State&lt;GameScreen&gt; {
  // ...
  
  // Triggered after the player discards
  void _discardCard() {
    // ... human player logic ...
    
    // Pass control to the bot
    _runBotTurn();
  }
  Future&lt;void&gt; _runBotTurn() async {
    // 1. Validation
    if (_engineController!.currentPlayerId != _opponentId) return;
    setState(() =&gt; _gameMessage = &quot;Opponent is thinking&#x2026;&quot;);
    // 2. Execution
    // We await the result so the UI doesn&apos;t update until the bot is done
    final summary = await _agent!.playTurn(
      engine: _engineController!, 
      botPlayerId: _opponentId
    );
    // 3. Feedback
    setState(() =&gt; _gameMessage = summary);
    
    // 4. Check Win Condition
    if (_engineController!.phase == GamePlayState.finished) {
       _showGameResultModal(...);
    }
  }
}</code></pre><p>And that&apos;s about it! This approach shows how AI Agents can replace rigid, rule based systems with adaptive reasoning.</p><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[Optimistic Locking]]></title><description><![CDATA[<p>While researching about ways to make my application more production-ready, especially in situations where multiple users may interact with the same data, I came across a concept called Optimistic Locking.</p><blockquote>Optimistic Locking is a concurrency control strategy commonly used in transactional systems to prevent data inconsistencies when several users attempt</blockquote>]]></description><link>https://zaahramujore.com/optimistic-locking/</link><guid isPermaLink="false">695259083b998104b437f0c4</guid><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Wed, 31 Dec 2025 12:25:16 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1571171637578-41bc2dd41cd2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHNvZnR3YXJlfGVufDB8fHx8MTc2ODA3MzYyNHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1571171637578-41bc2dd41cd2?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDJ8fHNvZnR3YXJlfGVufDB8fHx8MTc2ODA3MzYyNHww&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Optimistic Locking"><p>While researching about ways to make my application more production-ready, especially in situations where multiple users may interact with the same data, I came across a concept called Optimistic Locking.</p><blockquote>Optimistic Locking is a concurrency control strategy commonly used in transactional systems to prevent data inconsistencies when several users attempt to update the same resource.</blockquote><p>For now, this post will be more theoretical than practical. The goal is to explain what problem exists and how it can be handled conceptually.</p><p>I will get into the technical implementation details in a later post.</p><h3 id="scenerio">Scenerio</h3><p>Let&#x2019;s say you have two users managing product entries on a website.</p><p>Here&#x2019;s the sequence of events:</p><ul><li><strong>User 1</strong> opens a product and starts editing it.</li><li>They step away for a break without saving their changes.</li><li><strong>User 2</strong> opens the same product five minutes later.<ul><li>From their perspective, no changes have been made yet.</li></ul></li><li>User 2 edits the product but gets interrupted and forgets to click <strong>Save</strong>.</li><li><strong>User 1 comes back</strong>, completes their edits, and saves the product.</li></ul><p>So far, everything looks normal.</p><p>Now imagine User 2 later clicks Save.</p><h3 id="what-really-happens-now">What really happens now?</h3><ul><li>Without any concurrency control in place:</li><li>User 1 successfully saves their changes</li></ul><p>The problem here is that:</p><ul><li>both users made valid changes</li><li>neither user is aware of the conflict</li><li>there is no clear trace of where the error originated</li><li>User 2 also saves their version afterward</li><li>User 1&#x2019;s changes are overwritten, even though they were correct</li><li>The final product entry contains incorrect or incomplete information</li></ul><h3 id="why-this-is-a-problem-in-production-systems">Why this is a problem in Production Systems?</h3><p>In a production ready system, we want to</p><ul><li>protect data integrity</li><li>avoid silent overwrites</li><li>clearly detect and handle conflicts </li></ul><p>And this is where optimistic locking becomes useful.</p><p>Optimistic locking is a strategy that assumes conflicts are possible but relatively rare.</p><p>Instead of locking a record as soon as a user starts editing it, the system allows multiple users to work concurrency, and checks for conflicts only when a save operation occurs.</p><h3 id="how-it-works">How it works?</h3><p>A common way to implement Optimistic Locking is by using the <code>updatedAt</code>, specially while developing backend solutions. </p><p>The idea is simple: </p><ul><li>a user opens a product - the system sends the product data along with its current <code>updatedAt</code> value</li><li>the user make changes</li><li>when the user clicks on Save, the backend compares the <code>updatedAt</code> value of the user, and the current <code>updatedAt</code> value stored in the db.</li><li>If a match is found, it means no one else modified the data, henceforth, saves successfully. But if no match is found, a conflict is detected.</li></ul><p><strong>When a conflict is detected</strong></p><ul><li>the update is rejected</li><li>a collision conflict error is returned</li><li>the user is informed, so that they can review and decide on how to proceed.</li></ul><p>And this makes managing data and conflict more practical.</p><p>And yeah... that&apos;s about it ;)</p><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[Community Contributions Summary (2024–2025)]]></title><description><![CDATA[<p><strong>Summary:</strong><br>Throughout 2024&#x2013;2025, I actively contributed to the developer community through blog posts, workshops, and public talks focused on Flutter, AI, .NET, and emerging technologies.</p><p><strong>Impact:<br>13 technical blogs</strong>, <strong>4 public sessions</strong> (3 in-person, 3 online).<br>I consistently create content to empower developers&#x2014;especially early-career and women</p>]]></description><link>https://zaahramujore.com/what-i-have-done-in-2024-and-2025/</link><guid isPermaLink="false">68efcecb7d9b2604e07866fb</guid><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Tue, 30 Dec 2025 17:10:00 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/community-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/community-1.jpg" alt="Community Contributions Summary (2024&#x2013;2025)"><p><strong>Summary:</strong><br>Throughout 2024&#x2013;2025, I actively contributed to the developer community through blog posts, workshops, and public talks focused on Flutter, AI, .NET, and emerging technologies.</p><p><strong>Impact:<br>13 technical blogs</strong>, <strong>4 public sessions</strong> (3 in-person, 3 online).<br>I consistently create content to empower developers&#x2014;especially early-career and women in tech&#x2014;by sharing practical insights, tutorials, and problem-solving experiences.</p><p><strong><em>This article is divided into blog post and events and speaking engagements for the year 2024, and 2025.</em></strong></p><h2 id="blog-posts">Blog posts:</h2><h3 id="2024">2024</h3><p><strong>21st Jan 2024</strong> - <a href="https://zaahramujore.com/python-virtual-environment/" rel="nofollow">[Python] My Whirlwind Adventure with Django and Virtual Environments&#xA0;</a></p><p><strong>24th Jan 2024 </strong>- <a href="https://zaahramujore.com/ios-running-in-circle/" rel="nofollow">[IOS] Debugging a production nightmare ;)</a></p><p><strong>25th Jan 2024 </strong>- <a href="https://zaahramujore.com/ios-yet-another-issue-with-cocoapods/" rel="nofollow">[IOS] yet another issue with CocoaPods?</a></p><p>I found myself wrestling with yet another CocoaPods issue. The error message I encountered seemed quite daunting at first: Warning: CocoaPods not installed. Skipping pod install. CocoaPods is used to retrieve the iOS and macOS platform side&apos;s plugin code that responds to your plugin usage on the Dart side. Without CocoaPods, plugins will not work on iOS or macOS. For more info, see https://flutter.dev/platform-plugins To install see https://guides.cocoapods.org/using/getting-started.html#installation for instructions. CocoaPods not installed or not in valid state. Therefore in my blog, I explain how I solve the issue.</p><p><strong>1st March 2024</strong> - <a href="https://zaahramujore.com/feeling-rusty-lets-align-those-containers/" rel="nofollow">[Flutter Edition] Feeling Rusty? Let&apos;s Align Those Containers!</a></p><p>What we&apos;re aiming for is a clean and symmetrical design, with two distinct containers. In my blog I explain how to align 2 containers.</p><p><strong>24th March 2024</strong> - <a href="https://zaahramujore.com/the-dunning-kruger-effect-in-action/" rel="nofollow">The Dunning-Kruger Effect in Action&#xA0;</a></p><p>I believe we have all been through a situation where someone expected to be of knowledgeable authority, displays actions and attitude that shows otherwise. They lack the basic of knowledge, and skills required for a role. They bark orders, make unrealistic demands and shut down any attempts at constructive feedbacks or collaboration. And worst of all, they genuinely believe that they are experts when the evidence suggests otherwise. This englobe the paradoxical situation of the Dunning-Kruger effect.</p><p><strong>1st May 2024</strong> - <a href="https://zaahramujore.com/react-native-navigating-viewproptypes-deprecation-a-step-by-step-guide/" rel="nofollow">[React Native] Navigating ViewPropTypes Deprecation: A Step-by-Step Guide&#xA0;</a></p><p>Solving the &quot;ERROR ViewPropTypes will be removed from React Native, along with all other PropTypes. We recommend that you migrate away from PropTypes and switch to a type system like TypeScript. If you need to continue using ViewPropTypes, migrate to the &apos;deprecated-react-native-prop-types&apos; package&quot; error</p><p><strong>6th May 2024</strong> - <a href="https://zaahramujore.com/flutter-didchangedependencies/" rel="nofollow">[Flutter] initState v/s didChangeDependencies methods&#xA0;</a></p><p>In an attempt to solve a bug in my code, I stumbled onto the didChangeDependencies method. While it did not solve my problem, it significantly expanded my understanding on the flutter lifecycle methods. In this article i therefore share my insights.</p><p><strong>15th June</strong> <strong>2024 </strong>- <a href="https://zaahramujore.com/flutter-private-packages/" rel="nofollow">[Flutter] Create Private Packages&#xA0;</a></p><p>My journey with Flutter packages began when I had to build one from scratch. Today, I am excited to share my experiences and insights on this topic. I even discussed it at Google I/O&apos;23, but here we are for those who truly want to understand the concepts and build their own packages.</p><h3 id="2025">2025</h3><p><strong>16 July 2025</strong> - <a href="https://zaahramujore.com/function-apps-and-their-purpose/" rel="nofollow">Azure Function and Its purpose&#xA0;</a></p><p>I just published a new article titled &#x201C;Azure Functions and its Purpose.&#x201D; This is a brief introduction to Azure Functions, aimed at helping you understand what they are, how they work, and where they fit into enterprise solutions. I wrote this based on my experience working with Azure Functions and while preparing for the AZ-900 and AZ-204 certifications. Read it here: https://lnkd.in/dV3Fu3n7 I hope you find it helpful! If you have questions, insights, or tips from your own experience with Azure or serverless technologies, feel free to share them in the comments &#x2014; let&#x2019;s learn and grow together. Thank you! &#x1F601;</p><p><strong>15th September 2025 </strong>-  <a href="https://zaahramujore.com/lets-create-a-cli/" rel="nofollow">[.NET]: Let&#x2019;s Create a CLI with System.CommandLine</a></p><p>Hey There! I just built a small CLI task tracker in .NET! It&#x2019;s a simple tool where you can: - Add, update, delete tasks - List tasks - Mark status: ToDo, InProgress, or Done I wrote up the full walkthrough here: https://lnkd.in/dFE9kcTE Thank you for checking it out</p><p><strong>18th September 2025 </strong>- <a href="https://zaahramujore.com/dotnet-creating-an-expense-tracker-cli/" rel="nofollow">[.NET]: Creating an Expense Tracker CLI</a></p><p>Hey there! I&#x2019;ve created something fun again &#x2014; an Expense Tracker CLI in .NET. It&#x2019;s small but shows how you can apply programming best practices even in CLI tools. It includes features such as: - Add, update, delete, and read expenses - Get summarized expenses by month - Export data to CSV Here are a few example commands: expense-tracker add --name &quot;groceries&quot; --amount &quot;10&quot; expense-tracker summary --month 9 expense-tracker export-csv --out ~/Documents/expenses.csv Full post can be read here: https://lnkd.in/d69NqNnB Would love to know &#x2014; have you built a CLI tool before? What did it do?</p><p><strong>11 October 2025</strong> - <a href="https://zaahramujore.com/dart-isolates/" rel="nofollow">[Dart]: Understanding Isolates</a></p><p>Hey everyone! I just published a new article on Dart isolates &#x2014; a little guide to how Dart handles concurrency under the hood. Check it out here: https://zaahramujore.com/dart-isolates/</p><p><strong>13th October 2025 </strong>- <a href="https://zaahramujore.com/flutter-understanding-stateful-widget-2/" rel="nofollow">[Flutter - part 2] Understanding Stateful Widget&#xA0;</a></p><p>After previously exploring how Stateless Widgets work in Flutter, I wanted to continue the journey by sharing about Stateful Widgets, something Flutter devs use almost every day. I&#x2019;ve crafted this article carefully to help you not only understand how to update your UI but also what happens under the hood when you do. Read the full article here: https://zaahramujore.com/flutter-understanding-stateful-widget-2/</p><p><strong>15th October 2025 : <a href="https://zaahramujore.com/flutter-inherited-widget/" rel="noreferrer">[Flutter - part 3] Passing Data Efficiently Across Widgets</a></strong></p><p>Today, we&#x2019;ll be exploring how to pass data efficiently across widgets in Flutter!In my article, I dive into how data sharing really works under the hood, and walk you through both a bad example and a clean solution using InheritedWidget.</p><p><strong>15th October 2025</strong>: <a href="https://zaahramujore.com/dart-wildcard-variables/" rel="noreferrer">[Dart] WildCard Variables</a></p><p>Did you know?With the release of Dart 3.7, developers can now use wildcards (_) to ignore values they don&#x2019;t need &#x2014; making code cleaner, safer, and easier to read.</p><p><strong>16th October 2025</strong>: <a href="https://zaahramujore.com/dart-formatter-issue/" rel="noreferrer">When Dart Stops Formatting Your Code the Way You Want</a></p><p>Have you been encountering Dart formatting issue?</p><p><strong>28th October 2025</strong>: <a href="https://zaahramujore.com/turning-design-to-flutter-project-part-one/" rel="noreferrer">From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]</a></p><p>In this article, I share how I transformed my BMI Calculator design from Figma to a mobile app using Flutter. Today, I&#x2019;m excited to share Part 1 of the journey where I walk you through building the Home Screen step by step.</p><p><strong>28th October 2025</strong>: <a href="https://zaahramujore.com/flutter-merge-threads/" rel="noreferrer">Flutter Merge Threads #BreakingChanges</a></p><p>Flutter Breaking Changes Just Got Real. Merge Threads is now something every Flutter dev should keep an eye on.</p><p><strong>1st November 2025: <a href="https://zaahramujore.com/turning-design-to-flutter-project-part-two/" rel="noreferrer">From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]</a></strong></p><p>In this article, we will be completing the Calculate and Result Screen, implement the BMI Logic and the Unit testing.</p><p><strong>8th November 2025: <a href="https://zaahramujore.com/turning-design-to-flutter-project-part-three/" rel="noreferrer">From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3] </a></strong></p><p>A few days ago, I shared about Building Agentic Applications using Flutter and Gemini at Women in Tech Africa, Mauritius Chapter. &#x1F499; </p><p>Today, I&#x2019;ve written an article diving deeper into its capabilities, showing how an agentic AI can act as a wellness coach, giving personalised tips, setting weekly goals, and even scheduling reminders! </p><p><strong>21st December 2025:</strong> <a href="https://zaahramujore.com/from-idea-to-code/" rel="noreferrer">From Idea to Code - Building Rummy with Flutter - Part 1</a></p><p><strong>31st December 2025</strong>: <a href="https://zaahramujore.com/optimistic-locking/" rel="noreferrer">Optimistic Locking</a></p><p>In this post, I walk through scenarios where data conflicts can occur and explain how we can detect and resolve them using Optimistic Locking.</p><h3 id="events-and-speaking-engagements">Events and Speaking Engagements</h3><h3 id="2024-1">2024</h3><p><strong>4th May 2024 - Data Analytics</strong></p><p>In this comprehensive session, you&apos;ll delve into the fundamentals of data analytics and Power BI, learning essential functionalities and techniques to harness the power of data in a professional setting. From understanding the basics to integrating advanced analytics into your workflow, this workshop covers it all.</p><p>PPT Link: <a href="https://docs.google.com/presentation/d/1EQVY8fSjxmsl6XnUI169vZwXN4R0fWB8/edit?slide=id.p1&amp;ref=zaahramujore.com#slide=id.p1">https://docs.google.com/presentation/d/1EQVY8fSjxmsl6XnUI169vZwXN4R0fWB8/edit?slide=id.p1#slide=id.p1</a></p><p><strong>18th July 2024</strong> - Leveraging Generative AI in Mobile Applications</p><p>Join me as I delve into the cutting-edge world of Generative AI and its application in mobile development. This session will guide you through the practical aspects of incorporating AI technologies into mobile apps, with a special focus on Flutter. Learn how to design and implement AI-driven systems and enhance user interactions in real-world scenarios. Whether you&#x2019;re looking to stay ahead in the industry or simply interested in the potential of AI in mobile apps, this talk will provide valuable insights and actionable strategies.</p><p>Event Link: <a href="https://gdg.community.dev/events/details/google-gdg-mauritius-presents-google-io-extended-2024/?ref=zaahramujore.com">https://gdg.community.dev/events/details/google-gdg-mauritius-presents-google-io-extended-2024/</a></p><p><strong>28th</strong> <strong>September 2024</strong> (<strong>ONLINE</strong>) - Exploiter l&apos;Intelligence Artificielle G&#xE9;n&#xE9;rative dans les Applications Mobiles</p><p>Rejoignez-moi pour plonger dans le monde de pointe de l&apos;IA g&#xE9;n&#xE9;rative et son application dans le d&#xE9;veloppement mobile. Cette session vous guidera &#xE0; travers les aspects pratiques de l&apos;int&#xE9;gration des technologies d&apos;IA dans les applications mobiles, avec un accent particulier sur Flutter. Apprenez &#xE0; concevoir et &#xE0; mettre en &#x153;uvre des syst&#xE8;mes pilot&#xE9;s par l&apos;IA et &#xE0; am&#xE9;liorer les interactions utilisateur dans des sc&#xE9;narios r&#xE9;els. Que vous cherchiez &#xE0; rester en t&#xEA;te dans l&apos;industrie ou que vous soyez simplement int&#xE9;ress&#xE9; par le potentiel de l&apos;IA dans les applications mobiles, cette pr&#xE9;sentation vous fournira des informations pr&#xE9;cieuses et des strat&#xE9;gies concr&#xE8;tes.</p><p>Link: <a href="https://www.youtube.com/watch?v=pc4VpVAsMqY&amp;ref=zaahramujore.com">https://www.youtube.com/watch?v=pc4VpVAsMqY</a></p><p><strong>19th October 2024</strong> - Getting our hands dirty with Flutter GPU </p><p>Link: <a href="https://www.youtube.com/watch?v=ZvPRHV4iCiw&amp;t=30s&amp;ref=zaahramujore.com">https://www.youtube.com/watch?v=ZvPRHV4iCiw&amp;t=30s</a></p><h3 id="2025-1">2025</h3><p><strong>30th October 2025 - <a href="https://www.youtube.com/watch?v=hEN0AKmTuws&amp;t=6s&amp;ref=zaahramujore.com" rel="noreferrer"><strong>Building Agentic Apps with Flutter and Gemini</strong></a></strong></p><p><strong><em>At Women in Tech Africa, Mauritius Chapter event.</em></strong></p><p>Link: <a href="https://www.youtube.com/watch?v=hEN0AKmTuws&amp;t=6s&amp;ref=zaahramujore.com">https://www.youtube.com/watch?v=hEN0AKmTuws&amp;t=6s</a></p><p>In this session, we explored the foundational concepts of <strong>AI Agents</strong> &#x2014; their definition, purpose, and how they have evolved into <strong>Agent AI</strong>. We discussed the <strong>key distinctions between Agent AI and Agentic AI</strong>, highlighting how Agentic AI represents a more autonomous, goal-driven, and adaptive form of intelligence.</p><p>The session then introduced <strong>Generative AI (GenAI)</strong> and its extension into <strong>Generative AI Agents</strong>, emphasizing how these agents combine reasoning, planning, and multimodal capabilities to perform complex tasks.</p><p>After establishing the conceptual groundwork, we examined <strong>Firebase AI tools</strong> &#x2014; understanding <strong>why</strong> they are used, <strong>how</strong> they simplify the integration of AI features into mobile apps, and <strong>what advantages</strong> they provide for building Agentic experiences.</p><p>Finally, we transitioned into the <strong>hands-on coding segment</strong>, where participants implemented these ideas in practice, integrating Firebase AI functionalities directly into a Flutter project.</p><p><strong>Number of People who attended the session: </strong>150</p><p><strong>22nd November 2025 - <a href="https://www.youtube.com/watch?v=O3Ox89jZBAI&amp;ref=zaahramujore.com" rel="noreferrer"><strong>Creating Rummy, a card game, with Flutter, Gemini and Firebase.</strong></a> @ DevFest Lafia</strong></p><p>In this session, I will talk you through how to build Rummy, a classical card game, using Flutter, Firebase and Gemini.</p><p>Rummy has always been one of my favourite game to play with friends and that sparked an idea. What if your gaming companion could think, reason and play like a real friend?</p><p>We will explore how to integrate agentic AI within the game, allowing it to understand the gameplay, make intelligence decisions and interact dynamically with players. By the end of the session, you will learn how Flutter handles the game logic and UI, how Firebase provides us with a service, allowing us to leverage onto building this Agentic AI with Gemini, to make your digital opponent come alive.</p><p><a href="https://www.youtube.com/watch?v=tgvY5-GSRyc&amp;ref=zaahramujore.com" rel="noreferrer"><strong>6th December 2025 - Adding AI Functionalities To Your Flutter App Using AI Toolkit. @ DevFest Pakistan</strong></a></p><p>In this session, we will explore how to bring intelligence to your Flutter apps using the Flutter AI Toolkit. Through a simple use case, you&#x2019;ll learn how to integrate AI seamlessly into your existing project.</p><p>We&#x2019;ll begin with the basics, set up the toolkit, connect it to your Flutter app, and then walk through a live demo to see how AI can transform a regular Flutter application into a smart, interactive experience.</p><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Portfolio[Made with Love in Flutter]: <a href="https://portfolio.zaahramujore.com/?ref=zaahramujore.com">https://portfolio.zaahramujore.com/</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p><p></p><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[From Idea to Code - Building Rummy with Flutter - Part 1]]></title><description><![CDATA[<p>This project stems from enjoying playing rummy with friends. So I thought, why not turn this into a mobile game?</p><p>Building this game wasn&apos;t an easy process because I was initially unsure of the direction I wanted to take. At first, I started by building the UI and</p>]]></description><link>https://zaahramujore.com/from-idea-to-code/</link><guid isPermaLink="false">694721a33b998104b437efbe</guid><category><![CDATA[flutter]]></category><category><![CDATA[complexity]]></category><category><![CDATA[technology]]></category><category><![CDATA[game]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Sun, 21 Dec 2025 15:46:06 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/12/buildingrummypart1.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/12/buildingrummypart1.png" alt="From Idea to Code - Building Rummy with Flutter - Part 1"><p>This project stems from enjoying playing rummy with friends. So I thought, why not turn this into a mobile game?</p><p>Building this game wasn&apos;t an easy process because I was initially unsure of the direction I wanted to take. At first, I started by building the UI and also tried a TDD approach, but in this blog post, I will only be breaking things down by screen, and we will be integrating the logic as we go.</p><p>Okay, let us skip the talking and get into code.</p><h3 id="the-app-and-sample-code">The app and Sample Code</h3><p>You can find the app on the App Store here:&#xA0;<a href="https://apps.apple.com/us/app/io-rummy/id6755792275?ref=zaahramujore.com">IO Rummy App</a></p><p>You can find the complete source code here:&#xA0;<a href="https://github.com/fz3hra/flutter_rummy?ref=zaahramujore.com">flutter_rummy repo</a></p><h3 id="the-app-structure">The app structure</h3><p>The app is primarily composed of 2 main screens</p><ol><li>Home Screen</li><li>Game Screen</li></ol><h3 id="building-the-home-screen">Building the Home Screen</h3><p>The Home Screen is simple. It welcomes the user and asks them to enter their name.</p><p><strong>The UI setup</strong></p><p>To have a modern look, I have used a background gradient and some animation to make it pop. </p><pre><code class="language-dart">  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF5F6FA),
      body: SafeArea(
        child: Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [
                const Color(0xFFF5F6FA),
                const Color(0xFFE8EAF6),
              ],
            ),
          ),
          child: Center(
            child: SingleChildScrollView(
              padding: const EdgeInsets.all(24),
              child: FadeTransition(
                opacity: _fadeAnimation,
                child: SlideTransition(
                  position: _slideAnimation,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text(
                        &apos;Welcome!&apos;,
                        style: TextStyle(
                          fontSize: 36,
                          fontWeight: FontWeight.bold,
                          color: Color(0xFF2C3E50),
                          letterSpacing: 1.2,
                        ),
                      ),
                      // ... Lottie animation and other widgets ...
                      
                      // Name Input
                      Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(16),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.black.withOpacity(0.04),
                              blurRadius: 8,
                              offset: Offset(0, 2),
                            ),
                          ],
                        ),
                        child: TextField(
                          controller: _playerNameController,
                          textAlign: TextAlign.center,
                          style: TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.w600,
                            color: Color(0xFF2C3E50),
                          ),
                          decoration: InputDecoration(
                            hintText: &apos;Player Name&apos;,
                            // ... styling ...
                          ),
                        ),
                      ),
                      
                      // ... Join Button ...
                    ],
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }</code></pre><p>To achieve this look:</p><ul><li>The entire screen is wrapped in a <code>Container</code> with a <code>BoxDecoration</code></li><li>We use <code>LinearGradient</code> that flows from top left to bottom right, using soft colors, to create a nice feel.</li><li><code>FadeTransition</code> and <code>SlideTransition</code> is used to animate the content when the screen loads.</li></ul><p><strong>Managing state and animations</strong></p><p>Since we have animations and text input, we need a <code>stateful widget</code>.</p><pre><code class="language-dart">class _HomeScreenState extends State&lt;HomeScreen&gt; with SingleTickerProviderStateMixin {
  final TextEditingController _playerNameController = TextEditingController();
  late AnimationController _animationController;
  late Animation&lt;double&gt; _fadeAnimation;
  late Animation&lt;Offset&gt; _slideAnimation;
  @override
  void initState() {
    super.initState();
    _playerNameController.text = &quot;Player ${Random().nextInt(1000)}&quot;;
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 800),
    );
    _fadeAnimation = CurvedAnimation(
      parent: _animationController,
      curve: Curves.easeIn,
    );
    // ... slide animation setup ...
    _animationController.forward();
  }
  @override
  void dispose() {
    _playerNameController.dispose();
    _animationController.dispose();
    super.dispose();
  }
</code></pre><ul><li><code>initState</code>: we initialise the controller and start the animation</li><li><code>dispose</code>: we clean up the controllers to prevent memory leaks</li></ul><p><strong>The logic</strong></p><p>When the user clicks &quot;Join Game&quot;, we simply push the user to the <code>GameScreen</code>. We pass the player&apos;s name to this game screen so that we can identify them.</p><pre><code class="language-dart">  void _joinGame() {
    String name = _playerNameController.text.trim();
    if (name.isEmpty) {
      name = &quot;Player ${Random().nextInt(1000)}&quot;;
    }

    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) =&gt; GameScreen(
          localPlayerName: name,
          startWithTutorial: false, 
        ),
      ),
    );
  }</code></pre><h3 id="building-the-game-screen">Building the Game Screen</h3><p>And now this is where things get interesting. </p><p><strong>The models</strong></p><p>Before we draw a single card, we need to define what a card is. Therefore, in our card_model.dart, we define the following</p><pre><code class="language-dart">enum Suit { HEARTS, DIAMONDS, CLUBS, SPADES }

class Card {
  final int rank; // 2..10, and A, K, Q, J
  final Suit suit;
  const Card({required this.rank, required this.suit});
}
</code></pre><p>I have also created a deck_model.dart where I am able to create the 52 cards on deck, and make them shuffle. There is another method like draw(), which will allow a user to pick a card from the deck. </p><p><strong>The game engine</strong></p><p>The EngineController is where we handle the logic of the game. </p><p>What does it actually do?</p><ul><li>Setup method: It creates the deck, shuffle, and deals cards to all players</li><li>Management of turns: It checks whose turn it is by using the <code>currentPlayerIndex</code></li><li>Piles: It manages the stock (face down) and discard (face up) piles</li><li>Bot Logic: There is an algorithm (backtracking) to check what the computer opponent should do.</li></ul><p>Therefore in engine_controller.dart</p><pre><code class="language-dart">class EngineController {
  // ... state variables ...
  
  // assign random cards to players.
  // setup card on table
  // TODO: dont use seed in Production
  void setup(int cardsPerPlayer, int decksUsed, int? seed) {
    if (_state.players.isEmpty) throw StateError(&quot;No players&quot;);
    setupCardOnGameStart(decksUsed);
    setupPlayerCards(cardsPerPlayer);
    setupCardOnDesk();
    currentPlayerIndex = 0;
    _hasDrawnThisTurn = false;
    winnerPlayerId = null;
    phase = GamePlayState.playing;
  }
  
  void setupCardOnGameStart(int decksUsed, [int? seed]) {
    stock = Deck.standard(decks: decksUsed);
    stock.shuffle(seed);
  }
  void setupPlayerCards(int cardsPerPlayer) {
    hands
      ..clear()
      ..addEntries(
        _state.players.map((player) =&gt; MapEntry(player.id, &lt;Card&gt;[])),
      );
    for (var i = 0; i &lt; cardsPerPlayer; i++) {
      for (final player in _state.players) {
        hands[player.id]!.add(stock.draw());
      }
    }
  }
  void setupCardOnDesk() {
    discardCard
      ..clear()
      ..add(stock.draw());
  }
}</code></pre><p><strong>Connecting the Logic to the UI</strong></p><p>Now, how does the GameScreen actually use this?</p><p>In the game screen, we hold a reference to this controller. When the screen loads, we initialise the engine.</p><pre><code class="language-dart">  void _setupGame() {
    if (!mounted) return;
    final gameState = _startController.gameState;
    if (gameState == null) {
      debugPrint(&apos;Cannot start game: gameState is null.&apos;);
      return;
    }
    setState(() {
      _engineController = EngineController(gameState: gameState);
      final e = _engineController!;
      e.setup(_cardsPerPlayer, _decksUsed, _seed);
      _arrangedHand = List.from(e.handOf(_localPlayerId));
      _isGameSetup = true;
      _isWaitingForOpponent = false;
      _hasDrawnCurrentTurn = false;
      // ... tutorial logic ...
    });
  }</code></pre><p><strong>Handling of user interaction</strong></p><p>When you tap the deck to draw a card, we call the engine and then <code>setState</code> to show the new card.</p><pre><code class="language-dart">void _drawFromStock() {
    // 1. Validate: Can we draw?
    final e = _engineController;
    if (e == null || !e.canDrawFromStock) return;
    setState(() {
      // 2. Action: Tell engine to draw
      e.playerDrawFromStock();
      // 3. UI: Update our local hand view
      final newHand = e.handOf(_localPlayerId);
      // ... logic to animate the new card into _arrangedHand ...
    });
  }</code></pre><p>This pattern, action -&gt; engine update -&gt; setstate, is the heart of the game. The engine keeps the truth and the UI reflects it.</p><p><strong>Rendering the hand</strong></p><p>We want the cards to look like they are being held in a hand. We use a <code>GridView</code> to display the cards.</p><pre><code class="language-dart">  Widget _buildCardGrid(bool isLocalTurn, GamePlayState phase) {
    return GridView.builder(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      padding: EdgeInsets.zero,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: _cardsPerRow,
        childAspectRatio: _cardWidth / _cardHeight,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8,
      ),
      itemCount: _arrangedHand.length,
      itemBuilder: (context, index) {
        return _buildDraggableCard(index, isLocalTurn, phase);
      },
    );
  }</code></pre><p>And each card is made draggable so you can move it around:</p><pre><code class="language-dart">Widget _buildDraggableCard(int index, bool isLocalTurn, GamePlayState phase) {
    final card = _arrangedHand[index];
    final isSelected = _selectedCard == card;
    return Draggable&lt;int&gt;(
      data: index,
      feedback: Material(
        color: Colors.transparent,
        child: _buildCardWidget(card, isSelected: isSelected, elevation: 12.0),
      ),
      childWhenDragging: Opacity(
        opacity: 0.3,
        child: _buildCardWidget(card, isSelected: false),
      ),
      onDragStarted: () {
        setState(() {
          _draggedIndex = index;
          _dragCard = card;
        });
      },
      onDragEnd: (_) {
        setState(() {
          _draggedIndex = null;
          _dragCard = null;
        });
      },
      child: DragTarget&lt;int&gt;(
        builder: (context, candidateData, rejectedData) {
          return GestureDetector(
            onTap: isLocalTurn &amp;&amp; phase == GamePlayState.playing
                ? () {
                    // ... selection logic ...
                    setState(() {
                      if (_selectedCard == card) {
                        _selectedCard = null;
                      } else {
                        _selectedCard = card;
                      }
                    });
                    // ...
                  }
                : null,
            child: _buildCardWidget(card, isSelected: isSelected),
          );
        },
        onWillAccept: (data) =&gt; data != null &amp;&amp; data != index,
        onAccept: (draggedIndex) {
          _swapCards(draggedIndex, index);
        },
      ),
    );
  }</code></pre><ul><li>Draggable: Allows the user to pick up a card. We use <code>feedback</code> and a <code>childWhenDragging as parameters.</code></li><li>DragTarget: Allows the card to be dropped onto another card to swap positions.</li></ul><p><strong>The &quot;Winning&quot; Logic</strong></p><p>This was the hardest part. How do we know if a player has won? They need to form sets or runs. I created a <code>MeldController</code> that uses a backtracking algorithm to check if all the cards in a hand fit into a valid melds.</p><pre><code class="language-dart">
  bool isAllMeldsExact(List&lt;Card&gt; cards, {bool allowPairs = false}) {
    if (cards.isEmpty) return true;
    if (cards.length &lt; 2) return false;
    // --- build candidate melds from current cards ---
    final candidates = &lt;List&lt;Card&gt;&gt;[];
    // sequences (same suit, consecutive, len &gt;= 3)
    for (int k = 3; k &lt;= cards.length; k++) {
      for (final g in _combinations(cards, k)) {
        if (isSequenceSameSuit(g)) candidates.add(g);
      }
    }
    // sets (3/4-of-a-kind, different suits)
    // ... (finding sets logic) ...
    // --- backtracking: try to cover ALL cards with disjoint melds ---
    final used = List&lt;bool&gt;.filled(cards.length, false);
    candidates.sort((a, b) =&gt; b.length.compareTo(a.length)); // try longer first
    bool dfs(int idx) {
      while (idx &lt; cards.length &amp;&amp; used[idx]) idx++;
      if (idx == cards.length) return true;
      final target = cards[idx];
      for (final m in candidates) {
        if (!m.contains(target)) continue;
        // ... check if meld can be used ...
        // ... mark used ...
        if (dfs(idx + 1)) return true;
        // ... backtrack ...
      }
      return false;
    }
    return dfs(0);
  }</code></pre><p>And that&apos;s it!</p><p>Next I will be showing you how we can use Firebase and flutter_ai as a package to make our opponent more intelligent. </p><p>Stay tuned!</p><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[Community Contribution [2021-2024]]]></title><description><![CDATA[<h3 id="as-gdsc-lead-20212022"><strong>As GDSC Lead (2021/2022)</strong></h3><p>- Mentor students<br>- Organise technical events so as to bridge the bap between theory and practice.<br>- Manage a team.<br><br><strong>Successful events/Workshops:</strong><br>- Zero to Hero (speaker: myself):<br><a href="https://gdsc.community.dev/events/details/developer-student-clubs-nigerian-army-university-biu-presents-zero-to-hero/?ref=zaahramujore.com" rel="noreferrer">https://gdsc.community.dev/events/details/developer-student-clubs-nigerian-army-university-biu-presents-zero-to-hero/</a><br>- Flutter workshop organised by GDSC in collaboration</p>]]></description><link>https://zaahramujore.com/community-contribution-2021-2024/</link><guid isPermaLink="false">692307323b998104b437ef74</guid><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Sun, 23 Nov 2025 13:13:43 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1553073520-80b5ad5ec870?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE0fHxjb21tdW5pdHl8ZW58MHx8fHwxNzYzOTAzNTkzfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<h3 id="as-gdsc-lead-20212022"><strong>As GDSC Lead (2021/2022)</strong></h3><img src="https://images.unsplash.com/photo-1553073520-80b5ad5ec870?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDE0fHxjb21tdW5pdHl8ZW58MHx8fHwxNzYzOTAzNTkzfDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Community Contribution [2021-2024]"><p>- Mentor students<br>- Organise technical events so as to bridge the bap between theory and practice.<br>- Manage a team.<br><br><strong>Successful events/Workshops:</strong><br>- Zero to Hero (speaker: myself):<br><a href="https://gdsc.community.dev/events/details/developer-student-clubs-nigerian-army-university-biu-presents-zero-to-hero/?ref=zaahramujore.com" rel="noreferrer">https://gdsc.community.dev/events/details/developer-student-clubs-nigerian-army-university-biu-presents-zero-to-hero/</a><br>- Flutter workshop organised by GDSC in collaboration with Oracle Club at UoM<br>- Python Workshop<br>- Introduction to Deep Learning<br>- Design Thinking Workshop organised by WTM in collaboration with GDSC<br>- Mastering Soft Skills - Speaker Session - I was among the panelists.<br>- Solution Challenge - global competition that was organised. <br>- Cybersecurity Awareness Workshop<br>- MDash Battle organised by Oracle Club in collaboration with GDSC<br>- SalesForce Workshop<br>- Web Development workshop<br>- Introduction to Git - conducted by myself<br>- Introduction to Flutter - conducted by myself (invited by another gdsc): <a href="https://gdsc.community.dev/events/details/developer-student-clubs-mcpherson-university-presents-introduction-to-flutter-1/?ref=zaahramujore.com" rel="noreferrer">https://gdsc.community.dev/events/details/developer-student-clubs-mcpherson-university-presents-introduction-to-flutter-1/</a><br>- Flutter Live coding - conducted by myself (invited by another gdsc): <a href="https://gdsc.community.dev/events/details/developer-student-clubs-mcpherson-university-presents-introduction-to-flutter-2-learning-to-code-using-flutter/?ref=zaahramujore.com" rel="noreferrer">https://gdsc.community.dev/events/details/developer-student-clubs-mcpherson-university-presents-introduction-to-flutter-2-learning-to-code-using-flutter/</a><br>- Getting started in tech - conducted by DeepAfrica <a href="https://www.youtube.com/watch?v=cIldVnA6p3k&amp;ref=zaahramujore.com" rel="noreferrer">https://www.youtube.com/watch?v=cIldVnA6p3k</a></p><h3 id="as-gdsc-mentor-2022-2024">As GDSC Mentor [2022-2024]</h3><p>Mentored people on:<br>- JAVA (current)<br>- Flutter (current)<br><br>Invited to speak on:<br>- Spoke at GDSC Lead&apos;s 2022-2023 onboarding session on &quot;How to make an impact as a Lead&quot;<br>- Spoke on How to change the world using some of the programming languages<br>- Mentor about 17 GDSC Lead Alumnis for Getting Started in Data Structures: Lists, Sets, Maps, Stacks, Queues, Trees<br>- Spoke on Leveraging Tech Communities as a student at GDSC FUNAAB <br>- Spoke on Starting out in Tech (Mobile App Development) at GDSC ADUN<br>- Spoke on what is GDSC on Info Session at GDSC Muni<br>- Spoke on Impact of GDSC at GDSC ACity<br>- Spoke on Info Session at GDSC UOM<br>- Spoke on Info Session at GDSC Academic City College<br>- Spoke on Info Session at GSDC Technical University of Mombasa<br>- Spoke on Info Session at GDSC Ndejje University<br><br>Speaker session - 2023:<br>https://zaahramujore.com/what-i-have-been-doing-in-2023/<br><br>References:<br>- https://gdsc.community.dev/events/details/developer-student-clubs-the-federal-polytechnic-ede-presents-the-impact-of-tech-in-the-ecosystem/<br>- https://gdsc.community.dev/events/details/developer-student-clubs-federal-university-of-agriculture-abeokuta-presents-20222023-info-session/<br>- https://gdsc.community.dev/events/details/developer-student-clubs-admiralty-university-of-nigeria-presents-gdsc-adun-info-session-starting-out-in-tech/<br>- https://gdsc.community.dev/j/wuetj3ju9dtbd/<br>- https://bit.ly/gdscfulafia_info<br>- https://gdsc.community.dev/events/details/developer-student-clubs-university-of-mauritius-presents-info-session/<br>- https://bit.ly/GDSC-ACity_info-session</p><figure class="kg-card kg-image-card"><img src="https://media.licdn.com/dms/image/v2/D4E0BAQHcgieIlQeRxQ/company-logo_100_100/company-logo_100_100/0/1692077057460/gdsc_depauw_logo?e=1765411200&amp;v=beta&amp;t=XogOkYqO5Paa0JC1W90M9hgsC-ZPRECK0Tr6K4Y9m54" class="kg-image" alt="Community Contribution [2021-2024]" loading="lazy" width="48" height="48"></figure><p><strong>I was also the Sub Saharan Africa Mentor During 2022/2023 where I helped build leads.</strong></p>]]></content:encoded></item><item><title><![CDATA[From Design to Code – Building Agentic AI into “Your BMI Buddy” with Flutter and Gemini [Part 3]]]></title><description><![CDATA[<p>At Women in Tech Africa, Mauritius Chapter, I spoke about &quot;Building Agentic Applications Using Flutter and Gemini&quot;.</p><p>During my session, I explained the core concepts of agents, their evolution into AI agents, and how Agentic AI differs from traditional AI. We also explored the idea of Generative AI</p>]]></description><link>https://zaahramujore.com/turning-design-to-flutter-project-part-three/</link><guid isPermaLink="false">6905ed117d9b2604e0786d4b</guid><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Sat, 08 Nov 2025 16:11:43 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/11/Gemini_Generated_Image_ax2fpzax2fpzax2f.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/11/Gemini_Generated_Image_ax2fpzax2fpzax2f.png" alt="From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3]"><p>At Women in Tech Africa, Mauritius Chapter, I spoke about &quot;Building Agentic Applications Using Flutter and Gemini&quot;.</p><p>During my session, I explained the core concepts of agents, their evolution into AI agents, and how Agentic AI differs from traditional AI. We also explored the idea of Generative AI Agents as they are intelligent models that can reason, decide, and act within defined boundaries.</p><p>I even demoed a fun example where the agent could decide whether to change a button color in an app based on context, and reason. That was a small but powerful example of an AI that doesn&#x2019;t just follow instructions, but thinks in context.</p><p>In this article, we&#x2019;ll explore another side of <strong>Agentic AI</strong> &#x2014; this time, through a friendly wellness coach that can guide users based on their BMI.<br>(If you missed my talk, you can catch the full session on my <a href="https://www.youtube.com/watch?v=hEN0AKmTuws&amp;ref=zaahramujore.com" rel="noreferrer"><strong>YouTube channel!</strong></a>)</p><h2 id="meet-your-agentic-bmi-buddy-%F0%9F%92%99">Meet your Agentic BMI Buddy &#x1F499;</h2><p>Ever wondered what it would be like to have a wellness coach right inside your app? One that takes into account your height, weight, age, sex, and more, and then gives you short, actionable advice?</p><p>That&apos;s exactly what Your Agentic BMI Buddy does.</p><p>After you calculate your BMI, your virtual coach will</p><ul><li>Offer a few personalised tips,</li><li>Propose weekly goals</li><li>Remind you to stay on track (if you ask it to)</li></ul><h3 id="setting-up-firebase">Setting up Firebase</h3><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/11/sparky_celebrate.gif" class="kg-image" alt="From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3]" loading="lazy" width="467" height="500"></figure><p>Before diving into the AI part, we will connect our app to Firebase since we will be using Firebase AI Tools to enable our agentic features.</p><p><strong>Step 1: Login and Configure your project by running the following commands:</strong></p><pre><code class="language-dart">firebase login
flutterfire configure</code></pre><p>Firebase CLI will ask you to either create a new project or select an existing one. You can choose the platforms you need, for example; Android, IOS, Web or Windows.</p><p>Once done, each platform will be assigned an App ID, and a <code>firebase_options.dart</code> file will be automatically generated inside your <code>lib/</code> folder.</p><p><strong>Step 2: Add dependencies in pubspec.yaml</strong></p><p>Here is the list of packages we will be using</p><pre><code class="language-dart">dependencies:
  firebase_core: ^latest
  firebase_ai: ^latest   
  hive: ^latest
  hive_flutter: ^latest
  gap: ^latest
</code></pre><p><strong>Step 3: Initialise Firebase and Hive </strong></p><pre><code class="language-dart">void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Hive.initFlutter();
  await Hive.openBox(&apos;profile&apos;);
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(const MyApp());
}
</code></pre><ul><li>We initialise the widget binding so the framework can talk to native OS.</li><li>Then we setup Firebase with platform-specific configurations using the <code>DefaultFirebaseOptions</code>.</li></ul><p>And that&apos;s it! Your Firebase Project is ready and the app is connected. Let us move into the fun part.</p><h3 id="the-agent-how-it-thinks-and-act">The agent (how it thinks and act)</h3><p>We start by creating an instance of the Gemini Model 2.5 Flash model through Firebase AI and expose a few tools so it knows what it&apos;s allowed to do. [For development purposes, we will be using Gemini Developer API, but in production, it is best to use vertexAI]</p><pre><code class="language-dart">late final GenerativeModel _model = FirebaseAI.googleAI().generativeModel(
  model: &apos;gemini-2.5-flash&apos;,
  systemInstruction: Content.text(&apos;&apos;&apos;
    You are &quot;Your BMI Buddy&quot;&#x2014;a friendly, concise wellness coach.
    Always personalize using provided context. Avoid diagnoses/medications/calorie targets.
    When BMI very low/high, gently suggest seeing a clinician.
    Outputs must be short, practical, and culturally neutral.
  &apos;&apos;&apos;),
  tools: [Tool.functionDeclarations([
    setUserContextTool,
    storeGoalTool,
    storePlanTool,
    logDailyTool,
    scheduleReminderTool,
  ])],
);
</code></pre><p>The <code>systemInstruction</code> defines the agent&apos;s role; giving our application an intelligent layer that can reason, respond and perform contextual actions.</p><p><strong>GenAI Limitations</strong></p><p>Since Generative AI models can access external information, we use function calling to call external API or functions.</p><p>To implement this pattern:</p><ol><li>write a function that provides the model with the information it needs.</li><li>create a function declaration for the model to know which function to use.</li><li>send all declared tools to the model.</li><li>provide the function response back to the model.</li></ol><p><strong>Tool Configuration</strong></p><p>Tool configurations are defined in order to tell our model what it is allowed to do. For instance, we could request the agent to store our goals.</p><p><strong>Function Declarations</strong></p><p>Allows us to define our tool&apos;s api contract.</p><pre><code class="language-dart">final storeGoalTool = FunctionDeclaration(
  &apos;storeGoal&apos;,
  &apos;Persist a single user-selected wellness goal.&apos;,
  parameters: {
    &apos;goal_id&apos;: Schema.string(description: &apos;Machine id like &quot;walk_20_min_5x&quot;&apos;),
    &apos;goal_text&apos;: Schema.string(description: &apos;What the user chose/typed&apos;),
  },
);</code></pre><p>The <code>Function Declaration</code> takes a function name called <code>storeGoal</code>. It is given a description &quot;Persist a single user selected wellness goal.&quot;. And it takes parameters; <code>goal id</code> and <code>goal text</code>.</p><p><strong>Pushing contexts to the model</strong></p><p>Whenever the user opens the coach, their profile data is pushed to the model. This ensures that the AI does not re ask for the same information.</p><pre><code class="language-dart">Future&lt;void&gt; pushContextToAgent() async {
  final ctx = getProfile().toJson();
  await chat.sendMessage(Content.text(&apos;&apos;&apos;
    SYSTEM CONTEXT (do not ignore). Use this as the user&apos;s current profile.
    If &quot;bmi_value&quot; and &quot;bmi_category&quot; exist, DO NOT ask for height/weight again.

    ```json
    ${jsonEncode(ctx)}
  &apos;&apos;&apos;));
}
</code></pre><p><strong>Handing Function Calls</strong></p><p>If the model decides to call one of the functions, for example, <code>storeGoal</code> we can capture that call, perform the action, and then send back a confirmation using <code>functionResponse</code>.</p><pre><code class="language-dart"> case &apos;storeGoal&apos;:
          final goalId = (call.args[&apos;goal_id&apos;] as String?) ?? &apos;&apos;;
          final goalTxt = (call.args[&apos;goal_text&apos;] as String?) ?? &apos;&apos;;
          if (goalId.isNotEmpty &amp;&amp; goalTxt.isNotEmpty) {
            await saveGoal(goalId, goalTxt);
          }
          await chat.sendMessage(
            Content.functionResponse(&apos;storeGoal&apos;, {&quot;ok&quot;: true}),
          );
          break;</code></pre><p>The agent only suggests what to do (save a goal, create a reminder) and the Flutter app decides on how to persist this information.</p><h3 id="putting-it-all-together">Putting it all together</h3><p>In order to make this application work properly, we will be prompting the user to enter his weight and height, then we calculate the BMI.</p><p>Once the BMI is computed, it is categorized using:</p><pre><code class="language-dart">String bmiCategoryKey(double bmi) {
  if (bmi &lt; 18.5) return &apos;underweight&apos;;
  if (bmi &lt; 25.0) return &apos;healthy&apos;;
  if (bmi &lt; 30.0) return &apos;overweight&apos;;
  if (bmi &lt; 35.0) return &apos;obesity_class_1&apos;;
  if (bmi &lt; 40.0) return &apos;obesity_class_2&apos;;
  return &apos;obesity_class_3&apos;;
}
</code></pre><p>The information is saved in the user&apos;s profile.</p><p>And we pass that BMI data to the model to get context-specific advice:</p><pre><code class="language-dart">final tips = await _agent.adviseForBmi(
  bmi: _profile.bmiValue!,
  category: _profile.bmiCategory!,
);
setState(() =&gt; _aiTips = tips);

</code></pre><p>As a result, the agent returns some tips and weekly goals.</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.18.53-1.png" class="kg-image" alt="From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3]" loading="lazy" width="1179" height="2556" srcset="https://zaahramujore.com/content/images/size/w600/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.18.53-1.png 600w, https://zaahramujore.com/content/images/size/w1000/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.18.53-1.png 1000w, https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.18.53-1.png 1179w" sizes="(min-width: 720px) 720px"></figure><h3 id="saving-goals">Saving goals</h3><p>If I respond with &#x201C;Save Goal 1&#x201D;, the model calls:</p><pre><code class="language-dart">storeGoal(goal_id, goal_text)
</code></pre><p>The app persists the goal locally using Hive. Later if i ask: &quot;What is my goal for this week&quot;, the agent retrieves and restates my saved goal.</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.34.16.png" class="kg-image" alt="From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3]" loading="lazy" width="1179" height="2556" srcset="https://zaahramujore.com/content/images/size/w600/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.34.16.png 600w, https://zaahramujore.com/content/images/size/w1000/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.34.16.png 1000w, https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.34.16.png 1179w" sizes="(min-width: 720px) 720px"></figure><h3 id="scheduling-reminders">Scheduling reminders!</h3><p>The agent can also set reminders!</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.39.32.png" class="kg-image" alt="From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3]" loading="lazy" width="1179" height="2556" srcset="https://zaahramujore.com/content/images/size/w600/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.39.32.png 600w, https://zaahramujore.com/content/images/size/w1000/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.39.32.png 1000w, https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.39.32.png 1179w" sizes="(min-width: 720px) 720px"></figure><p>I said: &#x201C;Since I&#x2019;ve to walk 3 times a week, send me a reminder on Monday, Wednesday and Friday at 5 PM.&#x201D;</p><p>The agent replied:</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.40.32.png" class="kg-image" alt="From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3]" loading="lazy" width="1179" height="2556" srcset="https://zaahramujore.com/content/images/size/w600/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.40.32.png 600w, https://zaahramujore.com/content/images/size/w1000/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.40.32.png 1000w, https://zaahramujore.com/content/images/2025/11/Simulator-Screenshot---iPhone-16---2025-11-08-at-01.40.32.png 1179w" sizes="(min-width: 720px) 720px"></figure><p>So, I responded with: &quot;yes, 10th, 12th, and 14th November &quot;</p><p>And here is what was logged in my console.</p><pre><code> Schedule reminder: Time to Walk! @ 2024-11-10T17:00:00
flutter: Remember your 10-15 minute walk!
flutter: Schedule reminder: Time to Walk! @ 2024-11-12T17:00:00
flutter: Remember your 10-15 minute walk!
flutter: Schedule reminder: Time to Walk! @ 2024-11-14T17:00:00
flutter: Remember your 10-15 minute walk!</code></pre><p>And... That was the agent in action!</p><p>I hope you liked it!.</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/11/original-2ad10c30f36214ae1b66efb04e4ded4b.gif" class="kg-image" alt="From Design to Code &#x2013; Building Agentic AI into &#x201C;Your BMI Buddy&#x201D; with Flutter and Gemini [Part 3]" loading="lazy" width="1600" height="1200" srcset="https://zaahramujore.com/content/images/size/w600/2025/11/original-2ad10c30f36214ae1b66efb04e4ded4b.gif 600w, https://zaahramujore.com/content/images/size/w1000/2025/11/original-2ad10c30f36214ae1b66efb04e4ded4b.gif 1000w, https://zaahramujore.com/content/images/2025/11/original-2ad10c30f36214ae1b66efb04e4ded4b.gif 1600w" sizes="(min-width: 720px) 720px"></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[From Design to Code - Creating “Your BMI Buddy” with Flutter [Part 2]]]></title><description><![CDATA[In this article, we will be completing the Calculate and Result Screen, implement the BMI Logic and the Unit testing.]]></description><link>https://zaahramujore.com/turning-design-to-flutter-project-part-two/</link><guid isPermaLink="false">6903cf1d7d9b2604e0786b4e</guid><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Fri, 31 Oct 2025 19:53:37 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/From-Design-to-Code---Creating--Your-BMI-Buddy--with-Flutter--Part-1--1.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/From-Design-to-Code---Creating--Your-BMI-Buddy--with-Flutter--Part-1--1.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"><p>Welcome back to part 2 of turning design to code. </p><p>In <a href="https://zaahramujore.com/turning-design-to-flutter-project-part-one/" rel="noreferrer">part 1</a> of this series, we completed the HomeScreen for Your BMI Buddy. </p><p>In this article, we will be completing the Calculate and Result Screen, implement the BMI Logic and the Unit testing.</p><p>Let us dive right in!</p><h2 id="building-the-calculatorscreen">Building the CalculatorScreen</h2><p>The background shares the same gradient as the Home Screen. Therefore to avoid duplication, we will be refactoring our code into an extension.</p><p>I previously wrote an article on extension. Please feel free to check out the <a href="https://zaahramujore.com/dart-extension-method-in-flutter-projects/" rel="noreferrer">Link</a> to learn more.</p><h3 id="creating-a-gradient-extension">Creating a gradient extension</h3><pre><code class="language-dart">extension BackgroundGradient on BuildContext {
  BoxDecoration get backgroundGradient =&gt; BoxDecoration(
    gradient: LinearGradient(
      begin: Alignment.bottomRight,
      end: Alignment.topLeft,
      colors: &lt;Color&gt;[
        Color(0xFF0F2333),
        Color(0xFF247BA0),
        Color(0xFF189AE5),
      ],
      stops: [0.50, 0.90, 1.00],
    ),
  );
}</code></pre><p>This extension adds a getter to the buildContext.</p><pre><code class="language-dart">decoration: context.backgroundGradient</code></pre><h3 id="setting-up-the-structure">Setting up the structure</h3><p>The calculator screen has  5 components</p><ol><li>GreetingWidget</li><li>CustomTextFields (from Part 1)</li><li>CustomPrimaryButton (from Part 1)</li><li>EditProfileWidget</li><li>PointerWidget</li></ol><h3 id="creating-the-greetingswidget">Creating the GreetingsWidget</h3><pre><code class="language-dart">class GreetingsWidget extends StatelessWidget {
  const GreetingsWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          &quot;Hi, Zaahra!&quot;,
          style: GoogleFonts.caveat(
            color: Colors.white,
            fontSize: 40,
            fontWeight: FontWeight.bold,
            letterSpacing: 0.40,
          ),
        ),
        Text(
          &quot;Let&apos;s Calculate Your \nBMI&quot;,
          textAlign: TextAlign.center,
          style: GoogleFonts.inter(
            color: Colors.white,
            fontSize: 18,
            fontWeight: FontWeight.w500,
            letterSpacing: 0.40,
          ),
        ),
      ],
    );
  }
}
</code></pre><p>This widget welcomes the user and displays a subtitle &quot;Let&apos;s calculate your BMI&quot;.</p><ul><li>&quot;Zaahra&quot; will be later made dynamic, therefore the code will become <code>&quot;Hi, $name&quot;</code></li><li>GoogleFonts.caveat gives a friendly, handwritten feel.</li></ul><h3 id="adding-our-textfields">Adding our TextFields</h3><p>We reuse our CustomTextFields from Part 1:</p><pre><code class="language-dart">CustomTextFields(
   keyValue: Key(&quot;weight&quot;),
   controller: weightController,
   onChanged: (String value) =&gt; setState(() {}),
   hintText: &apos;Weight&apos;,
 ),
Gap(24),
CustomTextFields(
   keyValue: Key(&quot;height&quot;),
   controller: heightController,
   onChanged: (String value) =&gt; setState(() {}),
   hintText: &apos;Height&apos;,
 ),</code></pre><p>Each field listens to the user input and updates the UI accordingly</p><h3 id="adding-the-button">Adding the Button</h3><p>Once again, we reuse our CustomPrimaryButton made in Part 1:</p><pre><code class="language-dart">CustomPrimaryButton(
 onTap: () =&gt; Navigator.push(
  context,
  MaterialPageRoute(
  builder: (context) =&gt; ResultScreen(),
   ),
  ),
 buttonName: &apos;Calculate BMI&apos;,
),</code></pre><p>This button triggers the &quot;Calculate BMI&quot;, navigating us to &quot;Results Screen&quot;. However shortly we will change this functionality to calculate the BMI and pass the results to the Result Screen. </p><h3 id="creating-and-using-the-edit-profile-widget">Creating and Using the Edit Profile Widget</h3><p>Sometimes users may want to change their details (like name or DOB).<br>Let&#x2019;s create an <code>EditProfileWidget</code> that takes them back to the Home Screen.</p><pre><code class="language-dart">class EditProfileWidget extends StatelessWidget {
  final VoidCallback onTap;
  final String buttonName;

  const EditProfileWidget({
    super.key,
    required this.onTap,
    required this.buttonName,
  });

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onTap,
      child: Row(
        children: [
          Icon(
            Icons.edit,
            color: Colors.white,
            size: 18,
          ),
          Gap(16),
          Text(
            buttonName,
            style: GoogleFonts.inter(
              color: Colors.white,
              fontSize: 14,
            ),
          ),
        ],
      ),
    );
  }
}
</code></pre><p>Example:</p><pre><code class="language-dart"> EditProfileWidget(
    onTap: () =&gt; Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) =&gt; HomeScreen(),
        ),
    ),
    buttonName: &apos;Edit Profile&apos;,
),</code></pre><h3 id="creating-the-pointerwidget">Creating the PointerWidget</h3><p>Indicating us to enter our height and weight to calculate our BMI (it is just a fun way)</p><p>Here we will be using Positioned widget, in order to make this work, we will also have to wrap our SizedBox Widget with Stack.</p><pre><code class="language-dart">Positioned(
  right: 16,
  bottom: 16,
  child: IgnorePointer(
    child: Image.asset(
      &apos;assets/finger.png&apos;,
      width: 240,
      fit: BoxFit.contain,
    ),
  ),
)
</code></pre><p>Now that we are done with Calculate Screen, we are going to build the Result Screen. </p><h2 id="building-the-resultscreen">Building the ResultScreen</h2><p>The result screen uses the same background gradient as the other pages, so we&#x2019;ll keep things DRY with our extension: <code>decoration: context.backgroundGradient</code> </p><p>Beyond that, Result Screen has some complexities. We will be drawing an arc and a wave in the background. We will use <code>CustomPainter</code> for both.</p><h3 id="building-the-arc">Building the Arc</h3><p>I&#x2019;ll keep this as approachable as possible and add extra links in the references later for deeper dives into custom painting.</p><pre><code class="language-dart">CustomPaint(
  size: Size(arcWidth, 200),
  painter: ArcsPainter(
  bmi: 20,
  minBmi: 10,
  maxBmi: 40,
  ),
)</code></pre><pre><code class="language-dart">class ArcsPainter extends CustomPainter {
  final double bmi, minBmi, maxBmi;
  ArcsPainter({required this.bmi, required this.minBmi, required this.maxBmi});

  @override
  void paint(Canvas canvas, Size size) {
    final startAngle = math.pi;
    final sweepAngle = math.pi;
    final strokeWidth = 22.0;
    final center = Offset(size.width / 2, size.height);
    final radius = size.width * 0.55;

    final rect = Rect.fromCircle(center: center, radius: radius);

    // 1) BACKGROUND TRACK (behind the arc)
    final trackPaint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round
      ..strokeWidth = strokeWidth + 4
      ..color = const Color(0xFF0B2534).withOpacity(0.35)
      ..maskFilter = const MaskFilter.blur(
        BlurStyle.normal,
        2.0,
      );

    canvas.drawArc(rect, startAngle, sweepAngle, false, trackPaint);

    final gradient = SweepGradient(
      stops: [0.0, 0.5, 1.0],
      startAngle: startAngle,
      endAngle: startAngle + sweepAngle,
      colors: [
        Color(0xFF24A056),
        Color(0xFFFCE700),
        Color(0xFFFC1300),
      ],
    ).createShader(rect);

    final arcPaint = Paint()
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.butt
      ..shader = gradient;

    canvas.drawArc(rect, startAngle, sweepAngle, false, arcPaint);

    final capR = strokeWidth / 2;

    // ---- compute arc endpoints ----
    final startPt = Offset(
      center.dx + radius * math.cos(startAngle),
      center.dy + radius * math.sin(startAngle),
    );

    final endPt = Offset(
      center.dx + radius * math.cos(startAngle + sweepAngle),
      center.dy + radius * math.sin(startAngle + sweepAngle),
    );

    canvas.drawCircle(
      startPt,
      capR,
      Paint()..color = const Color(0xFF24A056),
    );
    canvas.drawCircle(
      endPt,
      capR,
      Paint()..color = const Color(0xFFFC1300),
    );

    // ---- animated indicator (white knob) ----
    final t = ((bmi - minBmi) / (maxBmi - minBmi)).clamp(0.0, 1.0);
    final angle = startAngle + sweepAngle * t;

    final knob = Offset(
      center.dx + radius * math.cos(angle),
      center.dy + radius * math.sin(angle),
    );

    // ---- white knob ----
    canvas.drawCircle(knob, capR * 1.4, Paint()..color = Colors.white);
    canvas.drawCircle(
      knob,
      capR * 1.4,
      Paint()
        ..style = PaintingStyle.stroke
        ..strokeWidth = 2
        ..color = Colors.white.withOpacity(0.25),
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) =&gt; false;
}</code></pre><p>For this, </p><ul><li>We draw a semi circle starting from the left to the right. We compute this using the <code>Rect.fromCircle</code></li><li>We then draw a stroke with a blur behind the main arc.</li><li><code>SweepGradient</code> is used for the sole purpose of adding a gradient to the arc. This is applied to the arc using <code>shader</code> method. </li><li>It was a challenge making the arc rounded at the endpoints, which is why I decided to go with building &quot;circles&quot; to add at the endpoint by calculating their angles.</li><li>A white knob is used to indicate at what level the BMI is.</li></ul><h3 id="moving-on-to-the-wavepainter">Moving on to the WavePainter</h3><p>To match the original design more closely, I drew a <strong>custom bezier wave</strong> behind the content. This bit is mostly visual math&#x2014;so I&#x2019;m keeping it lightweight here and will point to references later. </p><pre><code class="language-dart">class WavePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final w = size.width;
    final h = size.height;

    final path = Path()
      ..moveTo(0, h * 0.28)
      ..cubicTo(
        w * 0.18,
        h * 0.22,
        w * 0.58,
        h * 0.60,
        w * 0.82,
        h * 0.40,
      )
      ..cubicTo(
        w * 1.18,
        h * 0.10,
        w * 1.02,
        h * 0.38,
        w * 1.08,
        h * 0.52,
      )
      ..lineTo(w * 1.08, h)
      ..lineTo(0, h)
      ..close();

    final fillShader = const LinearGradient(
      begin: Alignment.bottomCenter,
      end: Alignment.topCenter,
      colors: [
        Color(0xFF64B8FF),
        Color(0xFFD9ECFF),
      ],
      stops: [0.0, 1.0],
    ).createShader(Rect.fromLTWH(0, 0, w, h));

    final fill = Paint()
      ..shader = fillShader
      ..style = PaintingStyle.fill;

    canvas.drawShadow(path, Colors.black.withOpacity(0.45), 22, false);

    canvas.drawPath(path, fill);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) =&gt; false;
}
</code></pre><p>The important parts are:</p><ul><li>We start at the <code>left</code>, about 28% from the top</li><li>Then use 2 <code>cubicTo</code> to make the <code>Bezier</code> segment. We basically need to add &quot;control points&quot; in order to make this wave possible. </li><li>Then we will the background with a gradient color using <code>LinearGradient</code>.</li><li><code>shouldRepaint</code> is used in case there is a need to repaint this background. </li></ul><p>Let&apos;s wire these custom painters properly inside the ResultScreen. </p><p>We will layer the wave behind everything and draw the arc on top.</p><p>A <code>stack</code> widget is perfect here since the bottom layer uses <code>Positioned.fill</code> with a <code>CustomPaint</code> for the wave, and the top layer places the arc where we want it.</p><pre><code class="language-dart">Positioned.fill(
  child: CustomPaint(
  painter: WavePainter(),
  ),
),</code></pre><ul><li><code>Positioned.fill</code> keeps the wave perfectly stretched behind the page</li><li><code>Stack</code>, <code>fit expand</code>, ensures the stack matches the screen bounds without extra containers</li></ul><pre><code> Positioned(
  top: topInset,
  left: (size.width - arcWidth) / 2,
  child: TweenAnimationBuilder&lt;double&gt;(
  tween: Tween&lt;double&gt;(begin: 0, end: 23),
  duration: Duration(milliseconds: 900),
  curve: Curves.easeOutCubic,
  builder: (context, animatedBmi, _) {
    return CustomPaint(
      size: Size(arcWidth, 200),
      painter: ArcsPainter(
        bmi: bmiResult,
        minBmi: 10,
        maxBmi: 40,
        ),
      );
    },
  ),
),</code></pre><p>And that&apos;s basically it for the UI portion. </p><h2 id="time-to-make-this-application-functional">Time to make this application functional!</h2><p>We will start by creating <code>Calculator</code> that handles the logic for us. This class includes methods; <code>parse()</code>, <code>bmiCalc()</code>, and <code>displayBMI()</code>. </p><pre><code class="language-dart">class Calculator {
  const Calculator();

  double? parse(String text) {
    if (text.trim().isEmpty) return null;
    return double.tryParse(text.replaceAll(&apos;,&apos;, &apos;.&apos;));
  }

  double? bmiCalc(double? weight, double? height) {
    if (weight == null || height == null || height &lt;= 0) return null;
    //check if height is in m or cm
    final convertedHeight = height &gt; 3 ? height / 100 : height;
    return weight / pow(convertedHeight, 2);
  }

  double? displayBMI(String weightText, String heightText) {
    final weight = parse(weightText);
    final height = parse(heightText);

    if (weight != null &amp;&amp; height != null) {
      var result = bmiCalc(weight, height);
      return result;
    }
    return -1;
  }
}
</code></pre><p>Basically, it has several methods.</p><ol><li><code>parse</code> checks if the input is empty or invalid. It then converts the numbers. For example if &quot;70&quot; is entered, the method will convert it to &quot;70.0&quot;, or if &quot;30,2&quot; is entered, it will be converted to &quot;30.2&quot;.</li><li><code>bmiCalc</code> ensures that both the weight and the height are valid before calculating the BMI. It even checks whether height is meters or centimeters.</li><li><code>displayBMI</code> is another method which calls the others, and returns the final BMI result to be displayed on the screen. </li></ol><p>Once this logic is created, we call it inside of our <code>CalculateScreen</code>. Whenever a user taps on the &quot;calculate BMI&quot; button, the <code>displayBMI</code> is triggered, and the result is pushed to the <code>ResultScreen</code> as follows:</p><pre><code class="language-dart">CustomPrimaryButton(
  onTap: () {
    // Calculate the BMI, then push to the result screen.
    final calc = const Calculator();
    final bmiResult = calc.displayBMI(
      weightController.text,
      heightController.text,
    );

    if (bmiResult == -1) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
            &apos;Please enter a valid weight and height.&apos;,
          ),
        ),
      );
      return;
    }

    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) =&gt; ResultScreen(
          bmiResult: bmiResult,
        ),
      ),
    );
  },
  buttonName: &apos;Calculate BMI&apos;,
);
</code></pre><p>In Result Screen, we simply accept the &quot;bmiResult&quot; in the constructor</p><pre><code>final double? bmiResult;

const ResultScreen({
  super.key,
  required this.bmiResult,
});
</code></pre><p>And there you go...! That was the logic part done! :D</p><h2 id="unit-tests-for-calculator-logic">Unit tests for Calculator logic.</h2><p>Unit tests ensures our logics work correctly before wiring it into the UI. We will use the <code>test</code> package to validate each method.</p><pre><code class="language-dart">import &apos;package:flutter_bmi_calculator/calculator.dart&apos;;
import &apos;package:test/test.dart&apos;;

void main() {
  group(&apos;parse&apos;, () {
    test(&quot;Parsing value returns 4.9 if &apos;,&apos; is used&quot;, () {
      final calculator = Calculator();

      final result = calculator.parse(&quot;4,9&quot;);

      expect(result, 4.9);
    });

    test(&quot;Parsing value returns 5.1 if &apos;.&apos; is used&quot;, () {
      final calculator = Calculator();

      final result = calculator.parse(&quot;5.1&quot;);

      expect(result, 5.1);
    });

    test(&quot;If height/weight is empty&quot;, () {
      final calculator = Calculator();

      final result = calculator.parse(&quot;&quot;);

      expect(result, isNull);
    });

    test(&quot;If height/weight has whitespace&quot;, () {
      final calculator = Calculator();

      final result = calculator.parse(&quot;   &quot;);

      expect(result, isNull);
    });

    test(&quot;If height/weight has non numeric&quot;, () {
      final calculator = Calculator();

      final result = calculator.parse(&quot;xyz&quot;);

      expect(result, isNull);
    });
  });

  group(&quot;calculation&quot;, () {
    test(&quot;BMI Calculation; returns 2&quot;, () {
      final calculator = Calculator();

      final result = calculator.bmiCalc(8, 2);

      expect(result, 2);
    });

    test(&quot;BMI Calculation; returns 4&quot;, () {
      final calculator = Calculator();

      final result = calculator.bmiCalc(16, 2);

      expect(result, 4);
    });

    test(&quot;If height is 0&quot;, () {
      final calculator = Calculator();

      final result = calculator.bmiCalc(0, 2);

      expect(result, 0.0);
    });

    test(&quot;If height is 0&quot;, () {
      final calculator = Calculator();

      final result = calculator.bmiCalc(8, 0);

      expect(result, isNull);
    });

    test(&quot;If weight/height is 0&quot;, () {
      final calculator = Calculator();

      final result1 = calculator.bmiCalc(null, 2);
      expect(result1, isNull);

      final result2 = calculator.bmiCalc(8, null);
      expect(result2, isNull);
    });
  });

  group(&quot;Display&quot;, () {
    test(&quot;Display Correct BMI result; returns 2.0&quot;, () {
      final calculator = Calculator();

      final result = calculator.displayBMI(&quot;8&quot;, &quot;2&quot;);

      expect(result, 2.0);
    });

    test(&quot;Display Correct BMI result; returns 4.0&quot;, () {
      final calculator = Calculator();

      final result = calculator.displayBMI(&quot;16&quot;, &quot;2&quot;);

      expect(result, 4.0);
    });
  });
}
</code></pre><p>After running the test, all assertions pass successfully, confirming our logic work as expected.</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-31-at-23.04.38.png" class="kg-image" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]" loading="lazy" width="513" height="271"></figure><p><em>I will write a separate article later that dives deeper into Flutter testing. ;)</em></p><p>Thank you for reading! I hope you enjoyed this article :D.</p><hr><p>Up next: We will be integrating AI Agents in the Application.</p><hr><h1 id="references">References</h1><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://zaahramujore.com/turning-design-to-flutter-project-part-one/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]</div><div class="kg-bookmark-description">Today, I&#x2019;m excited to introduce my very own design &#x2014; Your BMI Buddy! Honestly, it wasn&#x2019;t easy getting this done, and I have to say &#x2014; hats off to all the designers out there! This may not be the most perfect design yet, but I&#x2019;m still learning and trying</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://zaahramujore.com/content/images/size/w256h256/2023/10/my_logo.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"><span class="kg-bookmark-author">fz3hra&apos;s blog</span><span class="kg-bookmark-publisher">Umme Faatimah-Iz-Zaahra Mujore</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://zaahramujore.com/content/images/2025/10/From-Design-to-Code---Creating--Your-BMI-Buddy--with-Flutter--Part-1-.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://zaahramujore.com/dart-extension-method-in-flutter-projects/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Dart extension method in Flutter projects</div><div class="kg-bookmark-description">What is the Dart Extension Method? The Dart Extension Method allows us to add functionality to existing libraries and classes. Extensions can not only define methods but also other members such as getter, setters, and operators. syntax for Dart Extension Method: extension &lt;extension name&gt;? on &lt;ty&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://zaahramujore.com/content/images/size/w256h256/2023/10/my_logo.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"><span class="kg-bookmark-author">fz3hra&apos;s blog</span><span class="kg-bookmark-publisher">Umme Faatimah-Iz-Zaahra Mujore</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://zaahramujore.com/content/images/2023/10/Dart-Extension-Method--1-.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://api.flutter.dev/flutter/widgets/CustomPaint-class.html?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">CustomPaint class - widgets library - Dart API</div><div class="kg-bookmark-description">API docs for the CustomPaint class from the widgets library, for the Dart programming language.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://api.flutter.dev/flutter/widgets/static-assets/favicon.png?v1" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"><span class="kg-bookmark-author">Dart API</span></div></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.dhiwise.com/post/flutter-custom-paint-tutorial-from-basics-to-complex-graphics?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Flutter CustomPaint Tutorial</div><div class="kg-bookmark-description">Master custom graphics with the Flutter CustomPaint tutorial.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.dhiwise.com/favicon.ico" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"><span class="kg-bookmark-publisher">DhiWise</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://strapi.dhiwise.com/uploads/Blog_Common_Image_Flutter_OG_Image_1e84f31275.jpg" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 2]"></div></a></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[Flutter Merge Threads #BreakingChanges]]></title><description><![CDATA[<p>In older versions, Flutter ran multiple threads to keep things smooth:</p><ul><li><strong>UI Thread</strong> &#x2192; where your Dart code runs.</li><li><strong>Raster Thread</strong> &#x2192; where the engine turns that UI into pixels.<br>These two worked in sync to render frames efficiently.</li></ul><p>Alongside them, Flutter also had the <strong>Platform Thread</strong>, which runs native</p>]]></description><link>https://zaahramujore.com/flutter-merge-threads/</link><guid isPermaLink="false">6900f0c07d9b2604e0786a82</guid><category><![CDATA[flutter]]></category><category><![CDATA[dart]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Tue, 28 Oct 2025 17:17:32 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/flutter-breaking-changes.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/flutter-breaking-changes.png" alt="Flutter Merge Threads #BreakingChanges"><p>In older versions, Flutter ran multiple threads to keep things smooth:</p><ul><li><strong>UI Thread</strong> &#x2192; where your Dart code runs.</li><li><strong>Raster Thread</strong> &#x2192; where the engine turns that UI into pixels.<br>These two worked in sync to render frames efficiently.</li></ul><p>Alongside them, Flutter also had the <strong>Platform Thread</strong>, which runs native code (Swift on iOS or Kotlin/Java on Android).</p><p>Every time Flutter needed to talk to these threads &#x2014; Dart &#x2194; native, it caused problems.</p><p>Therefore, <strong>Platform Channels</strong> were built for the transmission of messages between the threads and languages. </p><p>You&#x2019;d often use the <strong>Pigeon package</strong> to define the interface between Dart and native, but since platform calls are synchronous while Dart Futures are asynchronous, it got&#x2026; complicated. Every payload had to be serialized, and every call had to bounce between threads and languages.</p><h3 id="what%E2%80%99s-changing-now">What&#x2019;s Changing Now</h3><p>With Flutter 3.35, the engine is getting smarter.<br>All Dart code is being migrated onto the<strong> main Platform Thread,</strong> bridging the gap between Dart and native more directly.</p><p>This allows <strong>FFI (Foreign Function Interface)</strong> to preserve type safety while calling native Swift or Kotlin code, no more fragile serialization back-and-forth.</p><p>The migration is rolling out in two phases, and it&#x2019;s not yet fully applied everywhere, but it&#x2019;s happening fast.</p><h3 id="what-you-should-do">What You Should Do</h3><p>If you have custom logic running on the <strong>Platform Thread</strong>, it&#x2019;s time to <strong>relocate it</strong>.<br>Because with the Flutter UI thread now merging into the platform thread, heavy computations could block your UI and break frame budgets.</p><p>Move expensive work to <strong>standalone isolates</strong> instead.</p><h3 id="example">Example</h3><p>Before the merge, we had to write asynchronous code even when the underlying native function was synchronous.<br>Flutter&#x2019;s calls to those APIs had to go through a Platform Channel and therefore, had to be async.</p><pre><code class="language-dart">// Old
Future&lt;double&gt; getBatteryCharge();</code></pre><p>Now, it is much simpler:</p><pre><code class="language-dart">// New
double getBatteryCharge();</code></pre><p>That&#x2019;s right! No more async placeholder!<br>Since the call is now synchronous, there&#x2019;s no need for:</p><ul><li>Null checks</li><li><code>initState</code> setup</li><li>Helper async wrappers</li><li>Even <code>StatefulWidget</code> in some cases!</li></ul><h3 id="tldr">TL;DR</h3><p>Flutter&#x2019;s <strong>Merge Threads</strong> is all about simplifying the bridge between Dart and native layers, cutting down async overhead, improving type safety, and letting the engine run closer to the metal.</p><p>Less thread hopping.<br>Less serialization.<br>More performance.</p><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[From Design to Code - Creating “Your BMI Buddy” with Flutter [Part 1]]]></title><description><![CDATA[<p>Today, I&#x2019;m excited to introduce my very own design &#x2014; Your BMI Buddy!</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-18-at-01.19.58.png" class="kg-image" alt loading="lazy" width="684" height="468" srcset="https://zaahramujore.com/content/images/size/w600/2025/10/Screenshot-2025-10-18-at-01.19.58.png 600w, https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-18-at-01.19.58.png 684w"></figure><p>Honestly, it wasn&#x2019;t easy getting this done, and I have to say &#x2014; <em>hats off to all the designers out there!</em> This may not be the most perfect design yet, but I&#x2019;</p>]]></description><link>https://zaahramujore.com/turning-design-to-flutter-project-part-one/</link><guid isPermaLink="false">68f162057d9b2604e0786924</guid><category><![CDATA[flutter]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Tue, 28 Oct 2025 10:18:11 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/From-Design-to-Code---Creating--Your-BMI-Buddy--with-Flutter--Part-1-.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/From-Design-to-Code---Creating--Your-BMI-Buddy--with-Flutter--Part-1-.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]"><p>Today, I&#x2019;m excited to introduce my very own design &#x2014; Your BMI Buddy!</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-18-at-01.19.58.png" class="kg-image" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]" loading="lazy" width="684" height="468" srcset="https://zaahramujore.com/content/images/size/w600/2025/10/Screenshot-2025-10-18-at-01.19.58.png 600w, https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-18-at-01.19.58.png 684w"></figure><p>Honestly, it wasn&#x2019;t easy getting this done, and I have to say &#x2014; <em>hats off to all the designers out there!</em> This may not be the most perfect design yet, but I&#x2019;m still learning and trying to apply all the right <strong>design principles and guidelines</strong> &#x2014; so bear with me.</p><p>Let&#x2019;s skip the design talk for now and move straight into the <strong>code.</strong></p><h2 id="the-sample-project">The sample project</h2><p><a href="https://github.com/fz3hra/flutter_bmi_calculator/tree/main?ref=zaahramujore.com" rel="noreferrer">You can find the sample project right here.</a></p><h2 id="the-starter-project-with-assets">The starter project with assets</h2><p>I&apos;ve set up a <a href="https://github.com/fz3hra/flutter_bmi_calculator/tree/starter_project?ref=zaahramujore.com" rel="noreferrer">starter branch</a> where all the required assets are already included in the new app, so you won&#x2019;t have to worry about setting those up manually.</p><p>Normally, images and content would come from a backend, but for this tutorial, we&#x2019;ll focus on:</p><ol><li>Building the UI</li><li>Writing the logic</li><li>Running some <strong>unit tests</strong> (already included)</li><li>Integrating an <strong>AI Agent</strong></li></ol><p>Our app is composed of three main screens:</p><ol><li>HomeScreen</li><li>CalculatorScreen</li><li>ResultScreen</li></ol><h2 id="building-the-home-screen">Building the Home Screen</h2><p>Okay let us get started with our home screen.</p><p>You&#x2019;ll notice that the <strong>background</strong> has a <strong>gradient</strong> color effect.<br>Here are the colors I used:</p><pre><code>0xFF0F2333

0xFF247BA0

0xFF189AE5</code></pre><p>We&#x2019;ll create this gradient using the <code>DecoratedBox</code> widget, which allows us to apply a <code>LinearGradient</code> directly.</p><pre><code class="language-dart">import &apos;package:flutter/material.dart&apos;;

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SizedBox(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: DecoratedBox(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.bottomRight,
              end: Alignment.topLeft,
              colors: &lt;Color&gt;[
                Color(0xFF0F2333),
                Color(0xFF247BA0),
                Color(0xFF189AE5),
              ],
              stops: [0.50, 0.90, 1.00],
            ),
          ),
          // TODO: build the screen
          // child: Container(),
        ),
      ),
    );
  }
}</code></pre><p>To achieve this gradient background:</p><ul><li>The entire screen is wrapped inside a <code>SizedBox</code> with its width and height set using <code>MediaQuery.of(context).size.width</code> and <code>MediaQuery.of(context).size.height</code>.</li><li>Inside it, we use a <code>DecoratedBox</code> with a <code>BoxDecoration</code> that includes a <code>LinearGradient</code>.</li><li>The <code>LinearGradient</code> defines where the color begins and ends (<code>bottomRight</code> to <code>topLeft</code>), the list of colors, and their positions (<code>stops</code>).</li></ul><h3 id="setting-up-other-structures">Setting up other structures</h3><p>To keep our code organised, we will create a separate widget called <code>HomeContent()</code> instead of placing everything inside <code>HomeScreen()</code>. </p><p>An alternative would be to create a helper method within the same class, but that approach can lead to unnecessary widget rebuilds, which affects performance.</p><p>Our design has three main components:</p><ol><li>Logo</li><li>TextFields</li><li>Primary Button</li></ol><p>Let&#x2019;s define these as separate widgets.</p><pre><code class="language-dart">class HomeContent extends StatelessWidget {
  const HomeContent({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(
        vertical: 72.0,
        horizontal: 32,
      ),
      child: Column(
        children: [
          // LogoWidget
          // CustomTextFields
          // CustomPrimaryButton
        ],
      ),
    );
  }
}</code></pre><pre><code class="language-dart">class LogoWidget extends StatelessWidget {
  const LogoWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}</code></pre><pre><code class="language-dart">class CustomTextFields extends StatelessWidget {
  const CustomTextFields({super.key});

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}</code></pre><pre><code class="language-dart">class CustomPrimaryButton extends StatelessWidget {
  const CustomPrimaryButton({super.key});

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}</code></pre><p>Now let us create these widgets one by one.</p><h3 id="creating-the-logo">Creating the Logo </h3><p>Our logo comes from the <strong>assets</strong> folder.<br>Here&#x2019;s the widget for that:</p><pre><code class="language-dart">class LogoWidget extends StatelessWidget {
  const LogoWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Image.asset(
      &quot;assets/logo.png&quot;,
    );
  }
}</code></pre><p>Don&#x2019;t forget to include the assets in your <code>pubspec.yaml</code> file:</p><p>After adding this image, you need to run <code>flutter pub get</code>.</p><pre><code class="language-yaml">  assets:
    - assets/</code></pre><h3 id="creating-the-textfields-section">Creating the textfields section</h3><p>The <code>TextField</code> widget in Flutter is powerful and highly customizable.<br>Here&#x2019;s how we&#x2019;ll structure our custom version:</p><pre><code class="language-dart">class CustomTextFields extends StatelessWidget {
  final Key keyValue;
  final TextEditingController controller;
  final ValueChanged&lt;String&gt; onChanged;
  final String hintText;
  const CustomTextFields({
    super.key,
    required this.keyValue,
    required this.controller,
    required this.onChanged,
    required this.hintText,
  });

  @override
  Widget build(BuildContext context) {
    return TextField(
      key: Key(keyValue.toString()),
      controller: controller,
      onChanged: onChanged,
      decoration: InputDecoration(
        hintText: hintText,
        hintStyle: GoogleFonts.inter(
          textStyle: TextStyle(
            color: Color(0xFF546A7B),
            fontSize: 14,
          ),
        ),
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12),
        ),
        filled: true,
        fillColor: Color(0xFFE8F1F2),
      ),
    );
  }
}
</code></pre><p>Our textfield takes a key, controller, onChanged, and decoration.</p><ul><li>Key is required to be set because we are using this for our widget testing</li><li>The <code>controller</code> lets you manage text changes.</li><li><code>onChanged</code> triggers whenever the user types.</li><li>The <code>InputDecoration</code> defines visual styles. In this case, a soft background color, rounded corners, and a light hint text.</li></ul><h3 id="creating-the-primary-button">Creating the primary button </h3><pre><code class="language-dart">class CustomPrimaryButton extends StatelessWidget {
  final VoidCallback onTap;
  final String buttonName;

  const CustomPrimaryButton({
    super.key,
    required this.onTap,
    required this.buttonName,
  });

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.transparent,
      clipBehavior: Clip.none,
      child: InkWell(
        onTap: () =&gt; Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) =&gt; CalculatorScreen(),
          ),
        ),
        child: DecoratedBox(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(12),
            color: const Color(0xFF1B98E1),
            boxShadow: const [
              BoxShadow(
                color: Color(
                  0x33247BA0,
                ),
                offset: Offset(0, 5),
                blurRadius: 14,
                spreadRadius: 0,
              ),
            ],
          ),
          child: SizedBox(
            width: double.infinity,
            child: Center(
              child: Padding(
                padding: const EdgeInsets.all(18.0),
                child: Text(
                  &quot;Continue&quot;,
                  style: GoogleFonts.inter(
                    color: Colors.white, // make text solid white
                    fontWeight: FontWeight.bold,
                    fontSize: 16,
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
</code></pre><ul><li>While <code>ElevatedButton</code> is great, here we need a custom shadow and more flexibility.</li><li>Using <code>InkWell</code> inside a <code>DecoratedBox</code> gives us both. Full control over design and a ripple effect when tapped.</li><li>We&#x2019;re using a simple navigation method with <code>MaterialPageRoute</code>, but you can easily swap this with more advanced routing later.</li></ul><h3 id="bringing-it-all-together">Bringing it all together</h3><p>Now that we have built our <code>HomeContent</code> widget, let&#x2019;s bring everything together inside the <code>HomeScreen</code> class.</p><pre><code class="language-dart">class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

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

class _HomeScreenState extends State&lt;HomeScreen&gt; {
  late TextEditingController nameController;
  late TextEditingController genderController;
  late TextEditingController dobController;

  String bmi = &quot;&quot;;

  @override
  void initState() {
    super.initState();
    nameController = TextEditingController();
    genderController = TextEditingController();
    dobController = TextEditingController();
  }

  @override
  void dispose() {
    nameController.dispose();
    genderController.dispose();
    dobController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // var bmiResult = displayBMI();
    final calc = const Calculator();
    final bmiResult = calc.displayBMI(
      nameController.text,
      genderController.text,
    );

    void onAnyFieldChanged(String _) =&gt; setState(() {});

    return Scaffold(
      body: SizedBox(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: DecoratedBox(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.bottomRight,
              end: Alignment.topLeft,
              colors: &lt;Color&gt;[
                Color(0xFF0F2333),
                Color(0xFF247BA0),
                Color(0xFF189AE5),
              ],
              stops: [0.50, 0.90, 1.00],
            ),
          ),
          child: HomeContent(
            nameController: nameController,
            genderController: genderController,
            dobController: dobController,
            onChanged: onAnyFieldChanged,
          ),
        ),
      ),
    );
  }
}</code></pre><p><code>HomeScreen</code> needs to store and manage state as the user types.</p><ul><li>We create 3 textfield controllers (<code>nameController</code>, <code>genderController</code>, <code>dobController</code>) to handle user input.</li><li>The <code>initState()</code> method runs once when the widget is inserted into the widget tree, allowing us to initialize the controllers.</li><li>The <code>dispose()</code> method is called when the widget is removed, ensuring we properly release memory and avoid leaks.</li><li>Whenever a field value changes, we call <code>setState()</code> (inside <code>onAnyFieldChanged</code>) so the UI updates immediately with the latest data.</li></ul><p>And we are done for the HomeScreen!</p><hr><p>Next, we&#x2019;ll move on to building the calculator screen.</p><hr><h2 id="learn-more-about-statelessstateful-lifecycle">Learn more about Stateless/Stateful &amp; Lifecycle</h2><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://zaahramujore.com/understanding-stateless-and-stateful-widget-beginner-flutter-edition-part-1-2/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Understanding stateless widget [Flutter Edition - Part 1]</div><div class="kg-bookmark-description">Flutter, a popular framework for building cross-platform apps, is unique in its approach to UI design through widgets. Among these, stateless widgets play a fundamental role. Let&#x2019;s demystify what they are and how they function. The essence of a stateless widget A stateless widget never changes. I&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://zaahramujore.com/content/images/size/w256h256/2023/10/my_logo.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]"><span class="kg-bookmark-author">fz3hra&apos;s blog</span><span class="kg-bookmark-publisher">Umme Faatimah-Iz-Zaahra Mujore</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://zaahramujore.com/content/images/2023/11/Understanding-stateless-widget--Flutter-Edition---Part-1-.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://zaahramujore.com/flutter-understanding-stateful-widget-2/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">[Flutter - part 2] Understanding Stateful Widget</div><div class="kg-bookmark-description">Previously in my post about &#x201C;understanding stateless&#x201D; widget&#x1F517; we saw how they are immutable configurations. But, have you ever wondered how your UI gets updated? Well, that&#x2019;s exactly where Stateful Widget comes in. Stateful Widget By definition, a stateful widget is one that has a mutable state.&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://zaahramujore.com/content/images/size/w256h256/2023/10/my_logo.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]"><span class="kg-bookmark-author">fz3hra&apos;s blog</span><span class="kg-bookmark-publisher">Umme Faatimah-Iz-Zaahra Mujore</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://zaahramujore.com/content/images/2025/10/-Flutter---part-2--Understanding-Stateful-Widget.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://zaahramujore.com/flutter-didchangedependencies/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">[Flutter] initState v/s didChangeDependencies methods</div><div class="kg-bookmark-description">In an attempt to solve a bug in my code, I stumbled onto the didChangeDependencies method. While it did not solve my problem, it significantly expanded my understanding on the flutter lifecycle methods. Let me share my understanding. If you have been in the Flutter world for a while now,</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://zaahramujore.com/content/images/size/w256h256/2023/10/my_logo.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]"><span class="kg-bookmark-author">fz3hra&apos;s blog</span><span class="kg-bookmark-publisher">Umme Faatimah-Iz-Zaahra Mujore</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://zaahramujore.com/content/images/2024/05/flutter-lifecycle-methods-part-1.png" alt="From Design to Code - Creating &#x201C;Your BMI Buddy&#x201D; with Flutter [Part 1]"></div></a></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[When Dart Stops Formatting Your Code the Way You Want]]></title><description><![CDATA[<p>I have been noticing something odd for a while now. Whenever I save a Dart file, my code suddenly gets formatted, in a not very good way. </p><p>Here is what it looks like after saving:</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-16-at-23.16.17.png" class="kg-image" alt loading="lazy" width="420" height="164"></figure><p>And here is what it used to look like before:</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-16-at-23.16.48.png" class="kg-image" alt loading="lazy" width="385" height="251"></figure><p>Honestly, it is not just</p>]]></description><link>https://zaahramujore.com/dart-formatter-issue/</link><guid isPermaLink="false">68f145ee7d9b2604e07868da</guid><category><![CDATA[dart]]></category><category><![CDATA[flutter]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Thu, 16 Oct 2025 19:39:11 GMT</pubDate><content:encoded><![CDATA[<p>I have been noticing something odd for a while now. Whenever I save a Dart file, my code suddenly gets formatted, in a not very good way. </p><p>Here is what it looks like after saving:</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-16-at-23.16.17.png" class="kg-image" alt loading="lazy" width="420" height="164"></figure><p>And here is what it used to look like before:</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-16-at-23.16.48.png" class="kg-image" alt loading="lazy" width="385" height="251"></figure><p>Honestly, it is not just ugly; it also makes the code harder to read.</p><p>So, I started digging.</p><p>That&apos;s when I found out that this change came with Dart&apos;s update, which is around version <strong>3.7</strong>. </p><p>The new formatter, no longer preservers trailing commas by default.</p><h3 id="so-whats-the-fix">So.. What&apos;s the fix?</h3><p>Luckily, Dart gives us a simple way to take back control.</p><p>We can run the following command in our project root to reformat all of our Dart files properly:</p><p><code>dart format .</code></p><p>But there is a small catch. You also need to tell the formatter to preserve trailing commas explicitly. </p><p>To do that, we add the following configuration in our <code>analysis_options.yaml</code> file:</p><pre><code class="language-yaml">formatter:
  trailing_commas: preserve</code></pre><p>Once this is in place, <code>dart format .</code> will restore your preferred formatting style.</p><hr><h1 id="references">References</h1><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://dart.dev/tools/dart-format?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">dart format</div><div class="kg-bookmark-description">Command-line tool for formatting Dart source code.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://dart.dev/assets/img/touch-icon-iphone-retina.png" alt></div></div><div class="kg-bookmark-thumbnail"><img src="https://dart.dev/assets/img/logo/dart-logo-for-shares.png" alt></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://codewithandrea.com/articles/updated-formatter-dart-3-8/?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">How to Configure the Updated Code Formatter in Dart 3.8</div><div class="kg-bookmark-description">Dart 3.8 introduced an updated formatter with various configuration options. Learn what&#x2019;s new and how to prepare your codebase.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://codewithandrea.com/img/favicon/apple-touch-icon.png" alt><span class="kg-bookmark-author">CODE WITH ANDREA</span><span class="kg-bookmark-publisher">Andrea BizzottoUpdated&#xA0;May 21, 20256 min read</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://codewithandrea.com/articles/updated-formatter-dart-3-8/images/twitter-card.png" alt></div></a></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[[Dart] WildCard Variables]]></title><description><![CDATA[<p>Did you know that <strong>wildcard variables</strong> were introduced in <strong>Dart 3.7</strong>?</p><p>Let&#x2019;s explore what they are and why you might want to use them.</p><h3 id="so-what-is-a-wildcard-variable">So... What is a WildCard Variable?</h3><blockquote><em>A wildcard variable _ declares a local variable or parameter that is non-binding. It serves as a placeholder.</em></blockquote>]]></description><link>https://zaahramujore.com/dart-wildcard-variables/</link><guid isPermaLink="false">68f003387d9b2604e0786850</guid><category><![CDATA[dart]]></category><category><![CDATA[flutter]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Wed, 15 Oct 2025 21:26:26 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/-Dart--WildCard-Variables.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/-Dart--WildCard-Variables.png" alt="[Dart] WildCard Variables"><p>Did you know that <strong>wildcard variables</strong> were introduced in <strong>Dart 3.7</strong>?</p><p>Let&#x2019;s explore what they are and why you might want to use them.</p><h3 id="so-what-is-a-wildcard-variable">So... What is a WildCard Variable?</h3><blockquote><em>A wildcard variable _ declares a local variable or parameter that is non-binding. It serves as a placeholder.</em></blockquote><h3 id="where-can-you-declare-wildcards">Where Can You Declare Wildcards?</h3><p>At its core, a wildcard can appear in:</p><ul><li>local declarations</li><li>function parameters</li><li>catch clauses</li><li>loop variables</li><li>and even generic types</li></ul><h3 id="a-quick-recap">A  quick recap</h3><p>Before Dart 3.7, we were writing functions like below to avoid name collisions</p><p><code>function A(_ , __, ___)</code> </p><p>Now since Dart 3.7, we can simply writing it as follows:</p><p><code>function A(_, _, _)</code> </p><p>To use it, make sure your SDK in <code>pubspec.yml</code> is set to 3.7 or above</p><pre><code>environment:
  sdk: ^3.8.1</code></pre><h3 id="why-use">Why use <code>_</code>?</h3><p>Wildcards are useful when you want to ignore values that you don&#x2019;t need.<br>They tell the compiler: &#x201C;Yes, I&#x2019;m aware of this value, but I don&#x2019;t need it.&#x201D;</p><p>They can match <strong>any type</strong>, and they <strong>don&#x2019;t create assignments</strong> or memory bindings for those values.</p><h3 id="common-uses">Common Uses</h3><ol><li>Destructuring records, or lists.</li></ol><pre><code class="language-dart">void main() {
  var (country, _, _) = record;
  print(country); // France is returned
}

var record = (&apos;France&apos;, &apos;Orange&apos;, &apos;Car&apos;);
</code></pre><ol start="2"><li>Declaring local variable</li></ol><pre><code class="language-dart">var _ = 10;</code></pre><ol start="3"><li>Loop Variables</li></ol><pre><code class="language-dart">for (final _ in [1, 2, 3]) {
  print(&apos;tick&apos;);
}
</code></pre><ol start="4"><li>Catch Clause Parameters</li></ol><pre><code class="language-dart">try {
  throw Exception(&apos;error&apos;);
} catch (_) {
  print(&apos;Something went wrong.&apos;);
}
</code></pre><ol start="5"><li>Generic Types</li></ol><pre><code class="language-dart">List&lt;_&gt; values = [];</code></pre><h3 id="what-you-need-to-know">What you need to know</h3><ul><li>Wildcards do not create variables that you can use later.</li></ul><p>And that&apos;s it. On that note, I hope you learned something new!</p><hr><h1 id="references">References</h1><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://dart.dev/language/variables?ref=zaahramujore.com#wildcard-variables"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Variables</div><div class="kg-bookmark-description">Learn about variables in Dart.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://dart.dev/assets/img/touch-icon-iphone-retina.png" alt="[Dart] WildCard Variables"></div></div><div class="kg-bookmark-thumbnail"><img src="https://dart.dev/assets/img/logo/dart-logo-for-shares.png" alt="[Dart] WildCard Variables"></div></a></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[[Flutter - part 3] Passing Data Efficiently Across Widgets]]></title><description><![CDATA[<p>As our Flutter projects grow, passing and accessing data down the widget tree can quickly become cumbersome.</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-15-at-22.18.48.png" class="kg-image" alt loading="lazy" width="598" height="478"></figure><p>Let us take a simple example.</p><p>Imagine you have a <strong>Parent Widget</strong> that has a <code>color</code> property which a deeply nested <strong>Child Widget (E)</strong> needs to access.</p><p>Normally, you would have to pass</p>]]></description><link>https://zaahramujore.com/flutter-inherited-widget/</link><guid isPermaLink="false">68e953767d9b2604e07864cc</guid><category><![CDATA[flutter]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Wed, 15 Oct 2025 18:56:18 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/-Flutter---part-3--Passing-Data-Efficiently-Across-Widgets.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/-Flutter---part-3--Passing-Data-Efficiently-Across-Widgets.png" alt="[Flutter - part 3] Passing Data Efficiently Across Widgets"><p>As our Flutter projects grow, passing and accessing data down the widget tree can quickly become cumbersome.</p><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-15-at-22.18.48.png" class="kg-image" alt="[Flutter - part 3] Passing Data Efficiently Across Widgets" loading="lazy" width="598" height="478"></figure><p>Let us take a simple example.</p><p>Imagine you have a <strong>Parent Widget</strong> that has a <code>color</code> property which a deeply nested <strong>Child Widget (E)</strong> needs to access.</p><p>Normally, you would have to pass this value through the constructors and build methods, of all intermediate widgets.</p><p><code>ParentWidget &#x2192; WidgetB &#x2192; WidgetD &#x2192; WidgetE</code></p><pre><code class="language-flutter">WidgetB(color: color);
WidgetD(color: color);
WidgetE(color: color);
</code></pre><p>This approach is known as prop-drilling, and it leads to tight coupling between widgets. Every intermediate immediate becomes responsible for passing data it does not even use, and also, changes at the top may not tripper proper rebuilds at the bottom.</p><p>Since this is an issue, Flutter has provided us with <code>Inherited Widget</code>.</p><blockquote>Inherited Widget allows us to easily propagate information down the widget tree. </blockquote><p>Examples where it is used internally by Flutter widget is Theme, MediaQuery, and Navigator.</p><p><strong>Example.</strong></p><p>Let us create a simple project, using the following command:</p><p><code>flutter create PROJECT_name</code></p><p>In the main.dart, The following widget are defined</p><ol><li>Parent Widget</li><li>Child WidgetA, WidgetB, WidgetC, WidgetD, and WidgetE</li></ol><p>Here, we will be passing the property color down to E from the parent Widget. <strong>[Bad Example] - Without InheritedWidget</strong></p><pre><code class="language-Flutter">import &apos;package:flutter/material.dart&apos;;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: ParentWidget(color: Color(0xFF6EA2FF)),
    );
  }
}

class ParentWidget extends StatelessWidget {
  final Color color;
  const ParentWidget({super.key, required this.color});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          WidgetA(),
          WidgetB(color: color),
        ],
      ),
    );
  }
}

class WidgetA extends StatelessWidget {
  const WidgetA({super.key});

  @override
  Widget build(BuildContext context) {
    return WidgetC();
  }
}

class WidgetB extends StatelessWidget {
  final Color color;
  const WidgetB({super.key, required this.color});

  @override
  Widget build(BuildContext context) {
    return WidgetD(color: color);
  }
}

class WidgetC extends StatelessWidget {
  const WidgetC({super.key});

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class WidgetD extends StatelessWidget {
  final Color color;

  const WidgetD({super.key, required this.color});

  @override
  Widget build(BuildContext context) {
    return WidgetE(color: color);
  }
}

class WidgetE extends StatelessWidget {
  final Color color;

  const WidgetE({super.key, required this.color});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      width: 200,
      height: 200,
      child: Text(&quot;Test&quot;),
    );
  }
}
</code></pre><p>We are prop-drilling the color from parent to widget B, to D, then E, and each of these widget are now dependent of one another, i.e, tightly coupled, as you can see. </p><p>To solve this issue, we will be using <code>Inherited Widget</code>.</p><blockquote>According to Flutter, Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.</blockquote><p>Now, how does it work under the hood?</p><p>Inherited Widget, has a method called <code>of</code> which calls <code>dependOnInheritedWidgetOfExactType</code> in order to get the property from the build context.</p><p>Otherwise, we would have to call the <code>context.inheritFromWidgetOfExactType</code> for our descendants widget to gain access to color property in its build method. This is actually asking Flutter to go up the tree, starting from the build context, and to look for a widget that matches that type. And the <code>of</code> static method in the InheritedWidget simplifies that for us.</p><p><strong>Good Example:</strong></p><p>TestColorScope is created and placed at the top of the true so that all of its data is accessible to its descendants. Color is added as a field of TestColorScope.</p><pre><code class="language-  flutter">class TestColorScope extends InheritedWidget {
  const TestColorScope({super.key, required this.color, required super.child});

  final Color color;

  static TestColorScope? maybeOf(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType&lt;TestColorScope&gt;();
  }

  static TestColorScope of(BuildContext context) {
    final TestColorScope? result = maybeOf(context);
    assert(result != null, &apos;No Color found in context&apos;);
    return result!;
  }

  @override
  bool updateShouldNotify(TestColorScope oldWidget) =&gt; color != oldWidget.color;
}</code></pre><p>TestColorScope have 2 methods, <code>of</code> and <code>maybeOf</code> which is called to check if a widget exists.</p><p><code>TestColorScope</code> Widget, is wrapped with the Parent Widget so all descendants can access its colors.</p><pre><code class="language-flutter">class ParentWidget extends StatelessWidget {
  final Color color;
  ParentWidget({super.key, required this.color});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TestColorScope(
        color: color,
        child: Column(children: [WidgetA(), WidgetB()]),
      ),
    );
  }
}</code></pre><p>Widget E can now directly access the inherited color in its build method by calling <code>TestColorScope.of(context).</code></p><pre><code class="language-flutter">class WidgetE extends StatelessWidget {
  const WidgetE({super.key});

  @override
  Widget build(BuildContext context) {
    final inheritedColor = TestColorScope.of(context);

    return Container(
      color: inheritedColor.color,
      width: 200,
      height: 200,
      child: Text(&quot;Test&quot;),
    );
  }
}</code></pre><p><strong>Full Code</strong></p><pre><code class="language-flutter">import &apos;package:flutter/material.dart&apos;;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &apos;Flutter Demo&apos;,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: ParentWidget(color: Color(0xFF6EA2FF)),
    );
  }
}

class ParentWidget extends StatelessWidget {
  final Color color;
  ParentWidget({super.key, required this.color});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TestColorScope(
        color: color,
        child: Column(children: [WidgetA(), WidgetB()]),
      ),
    );
  }
}

class WidgetA extends StatelessWidget {
  const WidgetA({super.key});

  @override
  Widget build(BuildContext context) {
    return WidgetC();
  }
}

class WidgetB extends StatelessWidget {
  const WidgetB({super.key});

  @override
  Widget build(BuildContext context) {
    return WidgetD();
  }
}

class WidgetC extends StatelessWidget {
  const WidgetC({super.key});

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class WidgetD extends StatelessWidget {
  const WidgetD({super.key});

  @override
  Widget build(BuildContext context) {
    return WidgetE();
  }
}

class WidgetE extends StatelessWidget {
  const WidgetE({super.key});

  @override
  Widget build(BuildContext context) {
    final inheritedColor = TestColorScope.of(context);

    return Container(
      color: inheritedColor.color,
      width: 200,
      height: 200,
      child: Text(&quot;Test&quot;),
    );
  }
}

class TestColorScope extends InheritedWidget {
  const TestColorScope({super.key, required this.color, required super.child});

  final Color color;

  static TestColorScope? maybeOf(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType&lt;TestColorScope&gt;();
  }

  static TestColorScope of(BuildContext context) {
    final TestColorScope? result = maybeOf(context);
    assert(result != null, &apos;No Color found in context&apos;);
    return result!;
  }

  @override
  bool updateShouldNotify(TestColorScope oldWidget) =&gt; color != oldWidget.color;
}
</code></pre><p>And that&#x2019;s it! We&#x2019;ve managed to decouple our widgets and make the data flow much cleaner with InheritedWidget.</p><hr><h1 id="references">References</h1><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">InheritedWidget class - widgets library - Dart API</div><div class="kg-bookmark-description">API docs for the InheritedWidget class from the widgets library, for the Dart programming language.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://api.flutter.dev/flutter/widgets/static-assets/favicon.png?v1" alt="[Flutter - part 3] Passing Data Efficiently Across Widgets"><span class="kg-bookmark-author">Dart API</span></div></div></a></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[[Flutter - part 2] Understanding Stateful Widget]]></title><description><![CDATA[<p>Previously in my post about <a href="https://zaahramujore.com/understanding-stateless-and-stateful-widget-beginner-flutter-edition-part-1-2/" rel="noreferrer">&quot;understanding stateless&quot; widget&#x1F517;</a> we saw how they are <strong>immutable configurations</strong>. But, have you ever wondered how your UI gets updated? Well, that&apos;s exactly where <code>Stateful Widget</code> comes in. </p><h3 id="stateful-widget">Stateful Widget</h3><blockquote>By definition, a stateful widget is one that has a</blockquote>]]></description><link>https://zaahramujore.com/flutter-understanding-stateful-widget-2/</link><guid isPermaLink="false">68ed11f67d9b2604e078663d</guid><category><![CDATA[flutter]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Mon, 13 Oct 2025 16:26:47 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/-Flutter---part-2--Understanding-Stateful-Widget.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/-Flutter---part-2--Understanding-Stateful-Widget.png" alt="[Flutter - part 2] Understanding Stateful Widget"><p>Previously in my post about <a href="https://zaahramujore.com/understanding-stateless-and-stateful-widget-beginner-flutter-edition-part-1-2/" rel="noreferrer">&quot;understanding stateless&quot; widget&#x1F517;</a> we saw how they are <strong>immutable configurations</strong>. But, have you ever wondered how your UI gets updated? Well, that&apos;s exactly where <code>Stateful Widget</code> comes in. </p><h3 id="stateful-widget">Stateful Widget</h3><blockquote>By definition, a stateful widget is one that has a mutable state. It can store data that can change synchronously and throughout its lifetime. </blockquote><p>Just like a <code>StatelessWidget</code> creates an Element called <code>Stateless Element</code>which mounts the widget on the element tree, the same way <code>Stateful Widget</code> does something similar, but with an extra step.</p><p>Instead of creating a stateless element, the <code>StatefulWidget</code> creates a <code>StatefulElement</code>, which is then mounted on the element tree. After that, this element calls <code>createState()</code> to produce a <strong>State object</strong>.</p><h3 id="lifecycle-of-a-stateful-widget">LifeCycle of a Stateful Widget</h3><figure class="kg-card kg-image-card"><img src="https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-13-at-19.52.11.png" class="kg-image" alt="[Flutter - part 2] Understanding Stateful Widget" loading="lazy" width="1162" height="1160" srcset="https://zaahramujore.com/content/images/size/w600/2025/10/Screenshot-2025-10-13-at-19.52.11.png 600w, https://zaahramujore.com/content/images/size/w1000/2025/10/Screenshot-2025-10-13-at-19.52.11.png 1000w, https://zaahramujore.com/content/images/2025/10/Screenshot-2025-10-13-at-19.52.11.png 1162w" sizes="(min-width: 720px) 720px"></figure><h4 id="1-createstate"><strong>1. createState()</strong></h4><p>Called when Flutter needs a fresh <code>State</code> object for your widget.<br>It links your immutable <code>StatefulWidget</code> with its mutable <code>State</code>.</p><h4 id="2-initstate"><strong>2. initState()</strong></h4><p>This is the first method that runs inside the <code>State</code> class.<br>It&#x2019;s used for <strong>initial setup</strong> &#x2014; starting animations, fetching data, initializing controllers, or subscribing to streams.<br>Always call <code>super.initState()</code> first.</p><h4 id="3-didchangedependencies"><strong>3. didChangeDependencies()</strong></h4><p>Called after <code>initState()</code> and again whenever any <strong>inherited dependencies</strong> (like <code>Theme</code>, <code>MediaQuery</code>, or <code>Provider</code>) change.<br>Perfect for cases where your widget depends on external context.</p><h4 id="4-build"><strong>4. build()</strong></h4><p>Defines the widget tree that describes your UI.<br>This method can run multiple times (for example, after calling <code>setState()</code>).</p><h4 id="5-setstate"><strong>5. setState()</strong></h4><p>Used to <strong>update the UI</strong>.<br>When something changes (like a counter value), you wrap that change inside <code>setState(() { ... })</code>.<br>This marks the widget as <em>dirty</em>, triggering a rebuild via the framework.</p><h4 id="6-didupdatewidget"><strong>6. didUpdateWidget</strong></h4><p>Called when the parent rebuilds the widget with new parameters.<br>You can compare <code>oldWidget</code> and <code>widget</code> to detect changes in configuration.</p><h4 id="7-dispose"><strong>7. dispose()</strong></h4><p>The final cleanup step.<br>Called when the widget is permanently removed from the tree.<br>Cancel timers, dispose controllers, and clean up streams here.<br>Always call <code>super.dispose()</code> at the end.</p><h3 id="example"><strong>Example</strong></h3><pre><code class="language-flutter">class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State&lt;MyHomePage&gt; createState() =&gt; _MyHomePageState();
}

class _MyHomePageState extends State&lt;MyHomePage&gt; {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            const Text(&apos;You have pushed the button this many times:&apos;),
            Text(
              &apos;$_counter&apos;,
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: &apos;Increment&apos;,
        child: const Icon(Icons.add),
      ),
    );
  }
}</code></pre><p>As you can see, title and count are used in the widget. Title comes from the widget itself while _counter comes from the state object. The <code>StatefulElement</code> maintains a reference to its <code>State</code>, so both the widget&#x2019;s data <code>title</code> and its changing state <code>_counter </code>can be accessed and displayed.</p><p>When you tap the <strong>FloatingActionButton</strong>, the <code>_incrementCounter()</code> function runs. Inside it, <code>setState()</code> updates <code>_counter</code> and tells Flutter to rebuild the widget. </p><p>When you call <code>setState()</code>, Flutter doesn&#x2019;t immediately rebuild your widget tree.<br>Instead, it marks that widget&#x2019;s element as <strong><em>dirty,</em></strong> meaning this part of the UI is <em>out of sync</em> with its current state and needs to be rebuilt.</p><p>Flutter keeps track of all the elements that are marked dirty.<br>Then, during the next frame, it efficiently rebuilds only those widgets, not your entire app.<br>After rebuilding, those widgets become <strong>clean</strong> again until another state change happens.</p><p>The updated value is then reflected in the UI.</p><p>If <code>MyHomePage</code> is rebuild by its parent, the title changes but the stateful element and stateful object remains the same. The state is preserved.</p><p>And that&apos;s it for this small article about Understanding Stateful Widget... I hope you liked it!</p><p><strong>Coming Next:</strong> Part 3 - Inherited Widget used in Flutter.</p><hr><h1 id="references">References</h1><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">StatefulWidget class - widgets library - Dart API</div><div class="kg-bookmark-description">API docs for the StatefulWidget class from the widgets library, for the Dart programming language.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://api.flutter.dev/flutter/widgets/static-assets/favicon.png?v1" alt="[Flutter - part 2] Understanding Stateful Widget"><span class="kg-bookmark-author">Dart API</span></div></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.dbestech.com/tutorials/learn-flutter-widgets-life-cycle-basic-state-management-tutorials?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">All About Flutter</div><div class="kg-bookmark-description">Learn Flutter Life Cycle, Learn Flutter App Life Cycle, Flutter initstate, Flutter stateful widget</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.dbestech.com/favicon.ico" alt="[Flutter - part 2] Understanding Stateful Widget"></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.dbestech.com/uploads/" alt="[Flutter - part 2] Understanding Stateful Widget"></div></a></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item><item><title><![CDATA[[Dart]: Understanding Isolates]]></title><description><![CDATA[<p>As you may know, Dart is a Single Thread Language where operations are executed one at a time, one after the other, in a purely synchronous manner. </p><p>In other languages like C++, multiple threads can share the same memory and run whatever code you may want. However, in Dart, each</p>]]></description><link>https://zaahramujore.com/dart-isolates/</link><guid isPermaLink="false">68e9441d7d9b2604e07864c0</guid><category><![CDATA[dart]]></category><category><![CDATA[flutter]]></category><dc:creator><![CDATA[Umme Faatimah-Iz-Zaahra Mujore]]></dc:creator><pubDate>Fri, 10 Oct 2025 20:24:54 GMT</pubDate><media:content url="https://zaahramujore.com/content/images/2025/10/-Dart--Understanding-Isolates.png" medium="image"/><content:encoded><![CDATA[<img src="https://zaahramujore.com/content/images/2025/10/-Dart--Understanding-Isolates.png" alt="[Dart]: Understanding Isolates"><p>As you may know, Dart is a Single Thread Language where operations are executed one at a time, one after the other, in a purely synchronous manner. </p><p>In other languages like C++, multiple threads can share the same memory and run whatever code you may want. However, in Dart, each thread has its own isolate within its own memory, and processes events independently. </p><h3 id="how-dart-manages-execution-flow">How Dart Manages Execution Flow</h3><p>To understand how Dart manage its sequence of operation, we will need to look within a Flutter application.</p><p>When a Flutter application starts, a new thread process is created and launched. This thread is the only thread you&apos;ll work with throughout your application.</p><p>There are 2 different types of threads that handle tasks:</p><ul><li>MicroTask</li><li>Event Queue</li></ul><p>MicroTask always runs first, and developers do not need to worry about its internal tasks since it is taken care for us. </p><p>When you call a future, the task is placed on an event queue once the Future completes.</p><p>After the main method runs, the <strong>Event Loop</strong> is executed.</p><ul><li>This invisible process determines how and in what order your code executes, depending on the content of the MicroTask and Event Queue.</li></ul><h3 id="key-differencesmicrotask-vs-event-queue">Key differences - MicroTask vs Event Queue </h3><p>A MicroTask handle short internal actions such as disposing of a resource. After this is done, it will hand back to the Event Queue.</p><p>An EventQueue on the other hand, is used to reference operations that result from external events such as IO, gesture, drawing, streams, timers, or futures.</p><h3 id="future-and-async-methods">Future and Async Methods</h3><p>Before diving into Isolates, Let us understand how Futures and Async methods works</p><ul><li>A <strong>Future</strong> is a task that runs asynchronously and completes later.</li><li>After it is created, it is added to an internal array which is placed in the Event Queue.</li><li>While running, the Future&apos;s status remains incomplete, allowing the next lines of code to execute without waiting.</li><li><strong>Async</strong> methods are built around Futures.</li><li>It runs synchronously, up to the first &quot;await&quot; keyword then pauses execution of the remaining codes of that method until the Future completes.</li></ul><h3 id="multithreading">MultiThreading</h3><p>Question: Can we run parallel codes in Flutter?</p><p>Answer: Yes, this is made possible by using &quot;Isolates&quot;</p><p><strong>What is an Isolate?</strong></p><p>As explained earlier, Dart code runs on a single thread called isolate. However, the major difference is that, isolates do not hang together. They only communicate to each other through messages sent over &quot;ports&quot;, without sharing any memory</p><p>Each isolate has an event loop, which schedules asynchronous tasks to run. </p><p><strong>Ways to create an Isolate</strong></p><ol><li>Through low-level solutions.</li></ol><p>As explained earlier, Isolates do not share memory and communicate using messages sent over ports. Therefore, in order to make this work, we need to establish communication between the &quot;Caller&quot;, and the new Isolate</p><ul><li>A SendPort is used by an isolate to convey messages.</li><li>A ReceivePort is used by another Isolate to listen to those messages.</li><li>Both the Isolate and the caller, will reference this port for communication.</li></ul><p><strong>A simple scenario: </strong></p><p>Create a Dart project that returns &apos;Hello World&apos; using Isolates.</p><p>a. To create a Dart Project, use the following command</p><p><code>dart create project_name</code></p><p>After having created the project, you will notice 2 main folders, bin and lib.</p><ul><li>Bin is where your main dart file lives</li><li>Lib is where you will be keeping your helper functions</li></ul><p>b. Within the main method, </p><ul><li>Create a temporary ReceivePort</li></ul><p><code>final receivePort = ReceivePort();</code></p><ul><li>then instantiate a new Isolate.</li><li>This Isolate, will be taking a function, and 2 parameters, a value, and sendPort for communication.</li></ul><pre><code class="language-Dart">await Isolate.spawn(callBackFunction, (
    value: &apos;Hello World&apos;,
    sendPort: receivePort.sendPort,
  ));</code></pre><ul><li>Listen for incoming messages.</li></ul><pre><code>receivePort.listen((value) {
    print(&apos;Result: $value&apos;);
  });</code></pre><ul><li>Define the callback function</li></ul><pre><code>callBackFunction(({String value, SendPort sendPort}) data) {
  data.sendPort.send(data.value);
}
</code></pre><p><strong>Full Code</strong></p><pre><code class="language-Dart">void main() async {
  final receivePort = ReceivePort();
  await Isolate.spawn(callBackFunction, (
    value: &apos;Hello World&apos;,
    sendPort: receivePort.sendPort,
  ));
  receivePort.listen((value) {
    print(&apos;Result: $value&apos;);
  });
}

// isolate
callBackFunction(({String value, SendPort sendPort}) data) {
  data.sendPort.send(data.value);
}</code></pre><p>It is best practice to dispose of Isolate when it is no longer needed.</p><pre><code>void dispose(){
  isolate?.kill(priority: Isolate.immediate);
  isolate = null;
}</code></pre><p>That&apos;s one way to writing this code.</p><ol start="2"><li>Instead of manually managing ports and isolates, Flutter provides a convenient helper method called <strong><code>compute</code></strong>, which automatically handles message passing for you.<br>It&#x2019;s ideal for offloading heavy computations to another isolate without manual setup.</li></ol><p>And thats&apos; it.</p><hr><h1 id="references">References</h1><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://dart.dev/language/isolates?ref=zaahramujore.com"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Isolates</div><div class="kg-bookmark-description">Information on writing isolates in Dart.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://dart.dev/assets/img/touch-icon-iphone-retina.png" alt="[Dart]: Understanding Isolates"></div></div><div class="kg-bookmark-thumbnail"><img src="https://dart.dev/assets/img/logo/dart-logo-for-shares.png" alt="[Dart]: Understanding Isolates"></div></a></figure><hr><h1 id="about-me">About Me</h1><p>I am Zaahra, a Google Women Techmakers Ambassador who enjoy mentoring people and writing about technical contents that might help people in their developer journey. I also enjoy building stuffs to solve real life problems.</p><p><strong><em>To reach me:</em></strong></p><p>LinkedIn:&#xA0;<a href="https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/?ref=zaahramujore.com" rel="noopener ugc nofollow">https://www.linkedin.com/in/faatimah-iz-zaahra-m-0670881a1/</a></p><p>X (previously Twitter):&#xA0;<a href="https://twitter.com/_fz3hra?ref=zaahramujore.com" rel="noreferrer">_fz3hra</a></p><p>GitHub:&#xA0;<a href="https://github.com/fz3hra?ref=zaahramujore.com" rel="noopener ugc nofollow">https://github.com/fz3hra</a></p><p>Cheers,</p><p>Umme Faatimah-Iz-Zaahra Mujore | Google Women TechMakers Ambassador | Software Engineer</p>]]></content:encoded></item></channel></rss>