<?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"><channel><title><![CDATA[Roy Developer]]></title><description><![CDATA[Senior Flutter Developer, fan of mobile development, and I like dinosaurs. 🦖]]></description><link>https://roydeveloper.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 15:12:56 GMT</lastBuildDate><atom:link href="https://roydeveloper.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A small introduction to Flutter Performance]]></title><description><![CDATA[When we discuss performance in mobile applications, we think of it as an advanced topic, only reserved to the final stage of the development of an app or for an extreme case when we need to optimize it, but in real life, performance is one of the ver...]]></description><link>https://roydeveloper.com/a-small-introduction-to-flutter-performance</link><guid isPermaLink="true">https://roydeveloper.com/a-small-introduction-to-flutter-performance</guid><category><![CDATA[performance]]></category><category><![CDATA[Flutter performance optimization]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[mobile app development]]></category><category><![CDATA[mobile]]></category><category><![CDATA[Mobile apps]]></category><category><![CDATA[Flutter Examples]]></category><dc:creator><![CDATA[Rodrigo Guzman]]></dc:creator><pubDate>Tue, 10 Feb 2026 19:41:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770752393174/552d22a7-26eb-4600-9a6d-48693dbd5f01.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When we discuss performance in mobile applications, we think of it as an advanced topic, only reserved to the final stage of the development of an app or for an extreme case when we need to optimize it, but in real life, performance is one of the very first issues an end user notices when using our apps, even before understanding what the app is doing.</p>
<p>A slow animation, a not fluid scroll, or an unresponsive interaction that has a delayed response are some signals that tell that something is bad. It does not matter how good your app idea is; if the user experience is broken, your user’s perspective of the app is going to be a bad one, which will affect your app’s reputation from day one.</p>
<p>This article will construct a starting point to help us understand what it means for an app to have a good performance, I will tell you a couple of good practices I use when creating my own projects, and a tip or two on how we can user Flutter DevTools to debug performance issues in a Flutter application.</p>
<p>As a general Idea we should not get obsessed with premature optimization, but understand and learn to identify some performance bottlenecks so that we can take informed decisions over the app.</p>
<h2 id="heading-what-does-it-mean-to-have-good-performance-in-a-mobile-application">What does it mean to have good performance in a mobile application?</h2>
<p>Let us start by discussing this question because it is not a trivial one. In a mobile application, good performance is not only about speed or the best hardware available on your device. From the user’s perspective, good performance is mainly about how the application feels while being used.</p>
<p>A performant mobile app feels smooth and responsive. Animations run without stutter, scrolling feels natural, and interactions respond immediately after a user taps the screen. End users rarely think in terms of milliseconds or frames per second; instead, they intuitively perceive when something feels slow, laggy, or unreliable.</p>
<p>In practical terms, good performance means that the application can deliver a consistent and fluid experience without blocking user interaction, even under non-ideal conditions such as older devices, limited memory, or background processes running at the same time.</p>
<h2 id="heading-why-is-it-important-for-our-app-to-have-good-performance">Why is it important for our app to have good performance?</h2>
<p>Performance has a direct impact on how users perceive an application.</p>
<p>Apps that feel slow or janky quickly generate frustration. This frustration translates into higher abandonment rates, poor reviews, and lower user retention. Even when an app’s functionality is correct, bad performance can make it feel unpolished or unstable, which erodes user trust.</p>
<p>From a product and business perspective, performance is not optional. It is part of the minimum quality bar. A performant app reduces friction, improves engagement, and allows users to focus on the value the app provides rather than on its technical limitations.</p>
<h2 id="heading-android-vs-ios-why-performance-context-matters">Android vs. iOS: Why performance context matters</h2>
<p>When discussing mobile performance, platform context matters.</p>
<p>Generally speaking, iOS devices deliver better and more consistent performance than Android devices. This is largely because of Apple’s tightly controlled ecosystem and the close optimization between hardware and operating system. With fewer device variations, it is easier to maintain predictable performance across the platform.</p>
<p>Android, on the other hand, runs on a very wide range of devices with so many hardware capabilities. Entry-level and mid-range devices are common, especially in emerging markets, and performance constraints are much more visible in real-world usage.</p>
<p>This becomes clear when looking at benchmarks. For example, comparing an iPhone running iOS 13 against a Samsung A21s shows a significant gap in CPU, GPU, and memory power.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770157268781/bc6c4014-d053-446d-b7df-77fb51cd4a0a.png" alt class="image--center mx-auto" /></p>
<p>Market data reinforces this reality. In Latin America, roughly 80% of users are on Android. This means that for many applications, most users are running on devices where performance is not guaranteed by hardware alone.</p>
<p>If your primary audience is on Android, performance is not something you can postpone—it needs to be considered from the very beginning.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770157348586/bee1361a-0db7-48ee-80d7-31ad5816daa7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-what-is-good-performance-in-flutter">What is good performance in Flutter?</h2>
<p>In Flutter, performance has a very concrete definition.</p>
<p>For a long time, a Flutter application was considered performant if it could consistently render at 60 frames per second (FPS), matching the refresh rate of most mobile displays.</p>
<p>In recent Flutter versions, the bar has been raised. Flutter now supports 120 FPS on devices with high refresh rate screens. This allows for much smoother animations and interactions, but it also reduces the margin for error.</p>
<p>In Flutter terms, good performance means consistently reaching the target frame rate of the device without dropping frames or introducing visible Jank.</p>
<h2 id="heading-understanding-the-flutter-rendering-pipeline">Understanding the Flutter Rendering Pipeline</h2>
<p>To understand how Flutter achieves high frame rates, we need to understand its Rendering Pipeline.</p>
<p>Each frame goes through several stages:</p>
<p>The UI thread, where widgets are built, layouts are calculated, and state changes are processed</p>
<p>The Raster thread, where the scene is converted into pixels</p>
<p>The GPU, which finally renders those pixels on the screen</p>
<p>To reach 120 FPS, every frame must complete in under ~8 milliseconds (1 second divided by 120). If any of these stages exceeds that time budget, frames are dropped, and the user perceives stutter or lag.</p>
<p>Therefore, many Flutter performance issues are related to blocking the UI thread or doing excessive rendering work.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770159150357/6d4d5e06-9860-4908-b688-909dc8ec221d.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-flutter-performance-best-practices">Flutter performance best practices</h2>
<p>To stay within the frame time budget, there are a few fundamental best practices that Flutter encourages:</p>
<ul>
<li><p><strong>Avoid blocking the UI thread:</strong> Heavy or synchronous work on the UI thread will immediately result in dropped frames.</p>
</li>
<li><p><strong>Use Stateless widget whenever possible:</strong> Stateless widgets are cheaper to rebuild and easier for Flutter to optimize.</p>
</li>
<li><p><strong>Avoid rebuilding widgets unnecessarily:</strong> Rebuilding large portions of the widget tree when nothing meaningful has changed wastes valuable frame time.</p>
</li>
<li><p><strong>Avoid costly tasks Expensive computations:</strong> Heavy layouts, or very large widget trees can easily exceed the frame budget.</p>
</li>
<li><p><strong>Use Flutter DevTools:</strong> For performance monitoring DevTools make performance issues visible and measurable instead of relying on assumptions.</p>
</li>
</ul>
<h2 id="heading-common-performance-issues-in-flutter">Common performance issues in Flutter</h2>
<p>A very common performance issue appears when too numerous widgets are built at the same time. Large widget trees rendered all at once can overwhelm the UI thread, especially on lower-end Android devices. In Flutter DevTools, this usually appears as long frame times and frequent Jank.</p>
<p>Another frequent issue comes from unnecessary widget rebuilds caused by poor widget placement. A small widget that depends on frequently changing state can trigger a rebuild of its parent and all its descendants, even when most of the UI did not change. In DevTools, this shows up as repeated rebuilds of large parts of the widget tree.</p>
<p>These issues are not theoretical. They are extremely common in real Flutter applications and can be clearly identified using Flutter DevTools.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This only covers Flutter Performance in Flutter as an introduction. We can have a deeper dive into this topic in later articles. The only thing I want you to keep after reading this is that performance is not about premature micro-optimizations. It is about understanding the constraints of the rendering pipeline, the reality of user devices, and how our code interacts with both.</p>
<p>Flutter provides everything needed to build smooth, high-performance applications—even at 120 FPS. Achieving this consistently requires awareness, solid architectural decisions, and regular performance monitoring.</p>
]]></content:encoded></item><item><title><![CDATA[How to Integrate Generative AI in Flutter Using a NestJS Backend with Gemini]]></title><description><![CDATA[Many tutorials show how to “use AI in Flutter” by calling an API directly from the app. That approach works for prototypes, but in real production environments, it brings several problems: exposed API keys, no rate limiting, no control over costs, an...]]></description><link>https://roydeveloper.com/how-to-integrate-generative-ai-in-flutter-using-a-nestjs-backend-with-gemini</link><guid isPermaLink="true">https://roydeveloper.com/how-to-integrate-generative-ai-in-flutter-using-a-nestjs-backend-with-gemini</guid><category><![CDATA[nestjs]]></category><category><![CDATA[gemini]]></category><category><![CDATA[Gemini integration]]></category><category><![CDATA[geminiAPI]]></category><category><![CDATA[nestjs-cli]]></category><category><![CDATA[Flutter Examples]]></category><dc:creator><![CDATA[Rodrigo Guzman]]></dc:creator><pubDate>Thu, 27 Nov 2025 04:40:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764212914846/26e2d24f-797e-41bd-af73-196334d9a501.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Many tutorials show how to “use AI in Flutter” by calling an API directly from the app. That approach works for prototypes, but in real production environments, it brings several problems: exposed API keys, no rate limiting, no control over costs, and no ability to enforce business rules or version models over time.</p>
<p>A better architecture — the one I actually use in my own projects — is:</p>
<h3 id="heading-flutter-nestjs-backend-gemini-api"><strong>Flutter → NestJS backend → Gemini API</strong></h3>
<p>This gives you security, observability, and full control of your AI layer.</p>
<p>In this article, I will walk you through the whole architecture and provide complete NestJS + Flutter code you can copy and use in your own apps.</p>
<h2 id="heading-why-you-should-not-call-gemini-directly-from-flutter"><strong>Why You Should NOT Call Gemini Directly from Flutter</strong></h2>
<p>Before we start, you might be wondering why it's not a good idea to use the Gemini API key directly in your Flutter applications.</p>
<p>Calling Gemini (or any AI provider) straight from the mobile client creates several critical problems:</p>
<ul>
<li><p>Your API key will be exposed in case anyone reverse engineers your app.</p>
</li>
<li><p>No rate limiting to protect your bill.</p>
</li>
<li><p>No control over quotas, logs, or usage patterns.</p>
</li>
<li><p>No way to enforce business rules (max prompt size, allowed features, etc.).</p>
</li>
<li><p>You must ship new app versions if you want to change models or parameters.</p>
</li>
</ul>
<p>Just to mention some reasons, this is why the correct architecture always includes a backend. NestJS sits between your Flutter app and Gemini, handling authentication, validation, logging, safeguards, rate limiting, and model management — while keeping your API keys 100% secure.</p>
<p>High-Level Architecture</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764217202923/c5edb5d9-1258-4f22-9823-112aa83fac1d.png" alt class="image--center mx-auto" /></p>
<p>NestJS becomes the “AI gateway” for all your apps — secure, flexible, and future-proof.</p>
<p>Before we start, please make sure you get a Gemini API key. You can get one from this page: https://aistudio.google.com/api-keys</p>
<p>Remember to create a Google Cloud Project before creating a new API Key.</p>
<h2 id="heading-1-creating-an-ai-module-in-nestjs">1. Creating an AI Module in NestJS</h2>
<p>Inside your NestJS project, we're gonna create a new module using NestJS CLI. On your terminal, put the following:</p>
<pre><code class="lang-plaintext">nest g module gemini
</code></pre>
<p>This will give us a new Module called Gemini.</p>
<pre><code class="lang-plaintext">src/gemini/gemini.module.ts
src/gemini/gemini.service.ts
src/gemini/gemini.controller.ts
</code></pre>
<p>NestJS CLI is a really cool tool that lets you create NestJS code really fast. This is one of the features I love most about Nest.</p>
<p>There are three key files: the Module, the Service, and the Controller. The Module acts as the orchestrator, wiring the controllers and providers for this Gemini Feature. The Gemini Controller communicates with the Gemini API through a use case that we will implement later on. And finally, the Gemini Service exposes the endpoints that our Flutter application will interact with.</p>
<h3 id="heading-geminimodulets">gemini.module.ts</h3>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Module } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { GeminiService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./gemini.service'</span>;
<span class="hljs-keyword">import</span> { GeminiController } <span class="hljs-keyword">from</span> <span class="hljs-string">'./gemini.controller'</span>;

<span class="hljs-meta">@Module</span>({
  controllers: [GeminiController],
  providers: [GeminiService],
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> GeminiModule {}
</code></pre>
<h2 id="heading-2-nestjs-service-to-call-gemini">2. NestJS Service to Call Gemini</h2>
<p>We're gona use @google/genai package. You can read the documentation in here for more information: https://ai.google.dev/gemini-api/docs For a better structure, we are gonna create a use case file as well. We have the following:</p>
<h3 id="heading-geminiservicets">gemini.service.ts</h3>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;

<span class="hljs-keyword">import</span> { GoogleGenAI } <span class="hljs-keyword">from</span> <span class="hljs-string">'@google/genai'</span>;
<span class="hljs-keyword">import</span> { geminiUseCase } <span class="hljs-keyword">from</span> <span class="hljs-string">'./use-cases/gemini.use-case'</span>;
<span class="hljs-keyword">import</span> { GeminiPromptDto } <span class="hljs-keyword">from</span> <span class="hljs-string">'./dtos/gemini-promot.dto'</span>;

<span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> GeminiService {
  <span class="hljs-keyword">private</span> ai = <span class="hljs-keyword">new</span> GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

  <span class="hljs-keyword">async</span> geminiPrompt(geminiPromptDto: GeminiPromptDto) {
    <span class="hljs-keyword">return</span> geminiUseCase(<span class="hljs-built_in">this</span>.ai, geminiPromptDto);
  }
}
</code></pre>
<h3 id="heading-geminiuse-casets">gemini.use-case.ts</h3>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { GoogleGenAI } <span class="hljs-keyword">from</span> <span class="hljs-string">'@google/genai'</span>;
<span class="hljs-keyword">import</span> { GeminiPromptDto } <span class="hljs-keyword">from</span> <span class="hljs-string">'../dtos/gemini-promot.dto'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> geminiUseCase = <span class="hljs-keyword">async</span> (
  ai: GoogleGenAI,
  basicPromptDto: GeminiPromptDto,
) =&gt; {
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> ai.models.generateContent({
    model: <span class="hljs-string">'gemini-2.5-flash'</span>,
    contents: basicPromptDto.prompt,
    config: {
      systemInstruction: <span class="hljs-string">`
      Responde únicamente en español
      En formato markdown
      Usa negritas de esta forma __
      Usa el sistema métrico decimal
      `</span>,
    },
  });

  <span class="hljs-keyword">return</span> response.text;
};
</code></pre>
<p>Add your key to <code>.env</code> :</p>
<pre><code class="lang-plaintext">GEMINI_API_KEY=your_key_here
</code></pre>
<h2 id="heading-3-exposing-a-clean-endpoint-for-flutter">3. Exposing a Clean Endpoint for Flutter</h2>
<h3 id="heading-geminicontrollerts">gemini.controller.ts</h3>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Body, Controller, Post } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { GeminiService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./gemini.service'</span>;
<span class="hljs-keyword">import</span> { GeminiPromptDto } <span class="hljs-keyword">from</span> <span class="hljs-string">'./dtos/gemini-promot.dto'</span>;

<span class="hljs-meta">@Controller</span>(<span class="hljs-string">'gemini'</span>)
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> GeminiController {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> geminiService: GeminiService</span>) {}

  <span class="hljs-meta">@Post</span>(<span class="hljs-string">'generate'</span>)
  basicPrompt(<span class="hljs-meta">@Body</span>() geminiPromptDto: GeminiPromptDto) {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.geminiService.geminiPrompt(geminiPromptDto);
  }
}
</code></pre>
<p>Now you have:</p>
<pre><code class="lang-bash">POST /gemini/generate
{
  <span class="hljs-string">"prompt"</span>: <span class="hljs-string">"Write a product description for a tip calculator app."</span>
}
</code></pre>
<h2 id="heading-4-recommend-protections-very-important-in-production">4. Recommend Protections (Very Important in Production)</h2>
<p>To operate AI safely:</p>
<h3 id="heading-rate-limiting">Rate limiting:</h3>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Throttle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/throttler'</span>;

<span class="hljs-meta">@Throttle</span>(<span class="hljs-number">5</span>, <span class="hljs-number">60</span>) <span class="hljs-comment">// 5 requests per minute</span>
</code></pre>
<h3 id="heading-validation-pipe">Validation Pipe</h3>
<p>Prevent sending empty or malicious prompts.</p>
<h3 id="heading-api-key-or-auth-in-your-backend">API key or auth in your backend</h3>
<p>Even a simple auth token adds a security layer.</p>
<h3 id="heading-logging-token-usage">Logging + token usage</h3>
<p>Helps detect abuse and control monthly billing.</p>
<h2 id="heading-5-calling-nestjs-from-flutter-using-dio">5. Calling NestJS from Flutter Using Dio</h2>
<p>Here's a clean service class in Flutter:</p>
<h3 id="heading-aiservicedart">ai_service.dart</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AiService</span> </span>{
  <span class="hljs-keyword">final</span> dio = Dio(BaseOptions(baseUrl: <span class="hljs-string">'https://your-backend.com'</span>));

  Future&lt;<span class="hljs-built_in">String</span>&gt; generate(<span class="hljs-built_in">String</span> prompt) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> body = {<span class="hljs-string">'prompt'</span>: prompt};
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> _dio.post(<span class="hljs-string">'/gemini/generate'</span>, data: jsonEncode(body));
      <span class="hljs-keyword">return</span> response.data;
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> <span class="hljs-string">'Error: <span class="hljs-subst">$e</span>'</span>;
    }
  }
</code></pre>
<h2 id="heading-6-simple-flutter-ui-example">6. Simple Flutter UI Example</h2>
<h3 id="heading-chatscreendart">chat_screen.dart</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'ai_service.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChatScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> ChatScreen({<span class="hljs-keyword">super</span>.key});

  <span class="hljs-meta">@override</span>
  State&lt;ChatScreen&gt; createState() =&gt; _ChatScreenState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ChatScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ChatScreen</span>&gt; </span>{
  <span class="hljs-keyword">final</span> controller = TextEditingController();
  <span class="hljs-keyword">final</span> ai = AiService();
  <span class="hljs-built_in">String</span> output = <span class="hljs-string">''</span>;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Gemini + Flutter'</span>)),
      body: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16.0</span>),
        child: Column(
          children: [
            TextField(controller: controller),
            <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">16</span>),
            ElevatedButton(
              onPressed: () <span class="hljs-keyword">async</span> {
                <span class="hljs-keyword">final</span> text = <span class="hljs-keyword">await</span> ai.generate(controller.text);
                setState(() {
                  output = text;
                });
              },
              child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Generate'</span>),
            ),
            <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">16</span>),
            Text(output),
          ],
        ),
      ),
    );
  }
}
</code></pre>
<h2 id="heading-7-my-own-experience-using-this-structure">7. My Own Experience Using This Structure</h2>
<p>In one of my recent projects, I'm following this exact architecture. It allows me to:</p>
<ul>
<li>Auto-generate text on a UI interface</li>
<li>Detect objects in user-uploaded photos using Gemini Vision</li>
<li>Generate suggestions, summaries, and metadata</li>
<li>Run lightweight agents for business logic</li>
</ul>
<p>Flutter’s role is simpler: it just sends some input, whether text or an image.</p>
<p>NestJS handles everything else — calling Gemini safely, cleaning the data, and returning structured results.</p>
<p>The app feels lighter and faster, and you don't worry about exposing keys or AI logic in the client.</p>
<h2 id="heading-8-some-good-practices-for-production-ai">8. Some Good Practices for Production AI</h2>
<p>Here are a few things that can save you some headaches in production:</p>
<ul>
<li>Put a limit on how big prompts can be</li>
<li>Cache common or repeated AI responses  </li>
<li>Track input/output tokens so you don’t get billing surprises  </li>
<li>Version your models as you upgrade (gemini-pro-1.5 → 2.0, etc.)  </li>
<li>Handle loading states properly — AI can take a moment  </li>
<li>Keep every model parameter and key on the server, never in the app</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>So there you have it. Using Flutter + NestJS + Gemini behind a backend isn’t just a “nice-to-have” architecture — it’s a solid, production-ready setup with real advantages:</p>
<ul>
<li>Your keys and logic never reach the mobile app</li>
<li>You can change models or parameters instantly from your backend without deploying a new version of your app</li>
<li>You control rate limits and usage because you have more control over your API usage</li>
<li>One backend can power multiple apps or platforms</li>
</ul>
<p>Thank you so much for reading this far, and let me know if you have any questions in the comment section.</p>
<p>Happy Coding. :D</p>
]]></content:encoded></item><item><title><![CDATA[Why you should avoid using Method Helpers in Flutter and use Widget classes instead.]]></title><description><![CDATA[Why should you avoid using Method Helpers in Flutter and use Widget classes instead?
Everything is a widget in Flutter, and we use widgets for everything from very easy components to very complex UI screens. However, developers commonly use method he...]]></description><link>https://roydeveloper.com/why-you-should-avoid-using-method-helpers-in-flutter-and-use-widget-classes-instead</link><guid isPermaLink="true">https://roydeveloper.com/why-you-should-avoid-using-method-helpers-in-flutter-and-use-widget-classes-instead</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Flutter Widgets]]></category><category><![CDATA[flutter]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Rodrigo Guzman]]></dc:creator><pubDate>Thu, 09 May 2024 03:02:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/dEczYX6WbJU/upload/a8427de8604a2e85a9abe531923b1358.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-why-should-you-avoid-using-method-helpers-in-flutter-and-use-widget-classes-instead">Why should you avoid using Method Helpers in Flutter and use Widget classes instead?</h1>
<p>Everything is a widget in Flutter, and we use widgets for everything from very easy components to very complex UI screens. However, developers commonly use method helpers for building UI components. I get it; I used to do it as well. They are easier to implement and, thus, faster to create complex UI elements. But we have to understand that this also comes with several downsides, especially performance issues.</p>
<p>In this article, I'm going to explain why using Method Helpers is not a good idea and why we should use Widget Classes instead.</p>
<h2 id="heading-understanding-method-helpers-and-widget-classes">Understanding Method Helpers and Widget Classes</h2>
<p>Before diving into comparisons, I want all of us to be on the same page and explain what is a method helper in the context of Flutter.</p>
<h2 id="heading-so-whats-a-method-helper">So what's a Method Helper?</h2>
<p>A method helper is a method that returns a widget, for example:</p>
<pre><code class="lang-dart">  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'My Favorite Books'</span>),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          _getItemOne(),
          _getItemTwo(),
        ],
      ),
    );
  }

  <span class="hljs-comment">// This is a method helper</span>
  Widget _getItemOne() {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> ListTile(
      title: Text(<span class="hljs-string">'The Great Gatsby'</span>),
      subtitle: Text(<span class="hljs-string">'F. Scott Fitzgerald'</span>),
      leading: Icon(Icons.book),
      trailing: Icon(Icons.favorite),
    );
  }

  <span class="hljs-comment">// This is another method helper</span>
  Widget _getItemTwo() {
    <span class="hljs-keyword">return</span> Container(
      margin: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
      padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
      decoration: BoxDecoration(
        color: Colors.grey[<span class="hljs-number">200</span>],
        borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
      ),
      child: <span class="hljs-keyword">const</span> Column(
        children: [
          Text(<span class="hljs-string">'The Catcher in the Rye'</span>),
          Text(<span class="hljs-string">'J.D. Salinger'</span>),
        ],
      ),
    );
  }
</code></pre>
<p>If we refactor this code and create Widget classes instead, we can have the following:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_FirstPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">FirstPage</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'My Favorite Books'</span>),
      ),
      body: <span class="hljs-keyword">const</span> Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          ItemOne(), <span class="hljs-comment">// We have now a reference to a widget class</span>
          ItemTwo(),
        ],
      ),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ItemTwo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> ItemTwo({
    <span class="hljs-keyword">super</span>.key,
  });

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Container(
      margin: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
      padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
      decoration: BoxDecoration(
        color: Colors.grey[<span class="hljs-number">200</span>],
        borderRadius: BorderRadius.circular(<span class="hljs-number">10</span>),
      ),
      child: <span class="hljs-keyword">const</span> Column(
        children: [
          Text(<span class="hljs-string">'The Catcher in the Rye'</span>),
          Text(<span class="hljs-string">'J.D. Salinger'</span>),
        ],
      ),
    );
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ItemOne</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> ItemOne({
    <span class="hljs-keyword">super</span>.key,
  });

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> ListTile(
      title: Text(<span class="hljs-string">'The Great Gatsby'</span>),
      subtitle: Text(<span class="hljs-string">'F. Scott Fitzgerald'</span>),
      leading: Icon(Icons.book),
      trailing: Icon(Icons.favorite),
    );
  }
}
</code></pre>
<p>We are writing a lot more code when creating Widget Classes. So, at first, using a method helper seemed more convenient because they are easier and faster to code, but there are some issues with Method Helpers:</p>
<h2 id="heading-issues-with-method-helpers">Issues with method helpers</h2>
<ul>
<li><p><strong>Lack of reusability and Scalability</strong>: Method helpers are usually not reusable outside the widget they are defined in, limiting their utility when we need to reuse them in other UI components.</p>
</li>
<li><p><strong>Debugging and Testing Challenges</strong>: Debugging can also be more complicated because method helpers don't appear as a separe component in the widget tree, making them more difficult to be accesssed from a test.</p>
</li>
<li><p><strong>Poor Performance issues</strong>: So here's the catch: Widgets that are returned from methods do not maintain their state and are rebuilt every time the parent widget rebuilds. Why this can be troublesome?</p>
</li>
</ul>
<p>Suppose we have the following widget:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FirstPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> FirstPage({<span class="hljs-keyword">super</span>.key});

  <span class="hljs-meta">@override</span>
  State&lt;FirstPage&gt; createState() =&gt; _FirstPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_FirstPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">FirstPage</span>&gt; </span>{
  <span class="hljs-built_in">bool</span> switchValue = <span class="hljs-keyword">false</span>;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'My Favorite Books'</span>),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          _getBooks(),
        ],
      ),
    );
  }

  Widget _getItemList({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> bookName,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> authorName,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">bool</span> isFavorite,
  }) {
    <span class="hljs-keyword">return</span> Padding(
      padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">15</span>, vertical: <span class="hljs-number">5</span>),
      child: Card(
        child: ListTile(
          title: Text(bookName),
          subtitle: Text(authorName),
          trailing: IconButton(
            icon: Icon(isFavorite ? Icons.favorite : Icons.favorite_border),
            onPressed: () {},
          ),
        ),
      ),
    );
  }

  Widget _getBooks() {
    <span class="hljs-keyword">return</span> Expanded(
      child: ListView(
        children: [
          _getItemList(
              bookName: <span class="hljs-string">"Ender's Game"</span>,
              authorName: <span class="hljs-string">"Orson Scott Card"</span>,
              isFavorite: <span class="hljs-keyword">true</span>),
          _getItemList(
              bookName: <span class="hljs-string">"The Hitchhiker's Guide to the Galaxy"</span>,
              authorName: <span class="hljs-string">"Douglas Adams"</span>,
              isFavorite: <span class="hljs-keyword">false</span>),
          _getItemList(
              bookName: <span class="hljs-string">"The Hobbit"</span>,
              authorName: <span class="hljs-string">"J. R. R. Tolkien"</span>,
              isFavorite: <span class="hljs-keyword">true</span>),
          _getItemList(
              bookName: <span class="hljs-string">"Station Eleven"</span>,
              authorName: <span class="hljs-string">"Emily St. John Mandel"</span>,
              isFavorite: <span class="hljs-keyword">true</span>),
          _getItemList(
              bookName: <span class="hljs-string">'The Nightingale'</span>,
              authorName: <span class="hljs-string">'Christin Hannah'</span>,
              isFavorite: <span class="hljs-keyword">true</span>),
        ],
      ),
    );
  }

  Widget _toggleFavorite() {
    <span class="hljs-keyword">return</span> Switch(
      value: switchValue,
      onChanged: (value) {
        setState(
          () {
            switchValue = value;
          },
        );
      },
    );
  }
}
</code></pre>
<p>I know it could be a more practical Widget, but bear with me. We have a Method Helper that is called <em>toggleFavorite()</em>; if you paid more attention, you could see the following:</p>
<pre><code class="lang-dart">
  onChanged: (value) {
    setState(
      () {
        switchValue = value;
      },
    );
  },
</code></pre>
<p>That <em>setState()</em> method belongs to the parent Widget which we called <em>FirstPage</em>. Every time we're triggering this method, we are updating the closest Widget Class this Method Helper is implemented in, in this case the complete <em>First Page</em> widget, which contains some components that do not require to be updated. This can lead to performance issues in our Flutter application when we have a widget tree with multiple UI components.</p>
<p>If we refactor this code and use the following:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FirstPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> FirstPage({<span class="hljs-keyword">super</span>.key});

  <span class="hljs-meta">@override</span>
  State&lt;FirstPage&gt; createState() =&gt; _FirstPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_FirstPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">FirstPage</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'My Favorite Books'</span>),
      ),
      body: <span class="hljs-keyword">const</span> Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          ToggleFavorite(), <span class="hljs-comment">// &lt;- Using the ToggleFavorite widget</span>
        ],
      ),
    );
  }

}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ToggleFavorite</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> ToggleFavorite({<span class="hljs-keyword">super</span>.key});

  <span class="hljs-meta">@override</span>
  State&lt;ToggleFavorite&gt; createState() =&gt; _ToggleFavoriteState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ToggleFavoriteState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ToggleFavorite</span>&gt; </span>{
  <span class="hljs-built_in">bool</span> switchValue = <span class="hljs-keyword">false</span>;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Switch(
      value: switchValue,
      onChanged: (value) {
        setState(
          () {
            switchValue = value;
          },
        );
      },
    );
  }
}
</code></pre>
<p>Now the <em>setState</em> is isolated inside the ToggleFavorite Widget class that extends a Stateful Widget, updating only this isolated widget instead of the parent Widget, leading to a more performant application.</p>
<p>Now that we have a bit of a better understanding, we can list more benefits of using Widget Classes:</p>
<ul>
<li><strong>Enhanced Reusability</strong>: By encapsulating our UI components, we have reuse them on different part of our applications.</li>
<li><strong>Easier Debugging and Testing</strong>: Since Widget Classes are isolated, create a Widget Test is also very easy as we only need the instance of the isolated widget and perform testing on it separately from other UI components.</li>
<li><strong>Improved Performance</strong>: Widget Classes mantain their state and context, which helps in optimizing the app performance overall.</li>
</ul>
<h1 id="heading-conclusion">Conclusion</h1>
<p>So there you have it: performance issues are the main reason why we should avoid using Method Helpers. I understand that we are constantly under time pressure as developers and fall into doing what can be done faster. But by doing so, we can get ourselves into a more problematic situation that will be harder to solve if we keep doing this practice as the project grows. So remember always to use Widget Classes in order to create more performant applications. Thank you so much for reading this far, and let me know if you have any questions in the comment section.</p>
<p>Happy Coding. :D</p>
]]></content:encoded></item></channel></rss>