<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Serrano</title>
    <description>The latest articles on DEV Community by David Serrano (@svprdga).</description>
    <link>https://dev.to/svprdga</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F833821%2Fbb8f5451-a4a3-458b-8dcb-a70f53be3db3.png</url>
      <title>DEV Community: David Serrano</title>
      <link>https://dev.to/svprdga</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/svprdga"/>
    <language>en</language>
    <item>
      <title>My (Dark) Prediction for Android</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Sun, 31 Aug 2025 10:38:38 +0000</pubDate>
      <link>https://dev.to/svprdga/my-dark-prediction-for-android-5emc</link>
      <guid>https://dev.to/svprdga/my-dark-prediction-for-android-5emc</guid>
      <description>&lt;p&gt;This article is a more detailed follow-up to a Reddit post I recently published called &lt;a href="https://www.reddit.com/r/degoogle/comments/1n25vvn/my_prediction_for_android/" rel="noopener noreferrer"&gt;“My prediction for Android”&lt;/a&gt;, which reflects my personal views as an Android developer, based on publicly available information and my own interpretation of current trends.&lt;/p&gt;

&lt;p&gt;As most of you probably know by now, Google has recently &lt;a href="https://developer.android.com/developer-verification" rel="noopener noreferrer"&gt;announced&lt;/a&gt; that any Android app will need to be verified by Google before it can be installed on a certified Android device, even if the installation happens outside Google Play.&lt;/p&gt;

&lt;p&gt;I have been developing Android applications for more than a decade, and I have witnessed firsthand all the &lt;em&gt;“small”&lt;/em&gt; changes Google has rolled out over the years. In my view, these changes are slowly but surely destroying the idea of a truly open Android as we once knew it.&lt;/p&gt;

&lt;p&gt;Beyond this concerning requirement, we are starting to see evidence that &lt;strong&gt;bootloaders may no longer be unlockable in the future&lt;/strong&gt;. On top of that, &lt;strong&gt;Google has stopped releasing the device trees for the Pixel 10 series devices&lt;/strong&gt;, which makes it much harder for custom ROMs to be ported to these devices. And then there is the &lt;strong&gt;growing push for Play Integrity&lt;/strong&gt;, a technology that essentially forces you to run an app on a certified device with Play Services installed. When you put all these pieces together, the picture becomes very, very concerning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The recent controversy
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;By making Android safer, we're protecting the open environment that allows developers and users to confidently create and connect. Android's new developer verification is an extra layer of security that deters bad actors and makes it harder for them to spread harm.&lt;/p&gt;

&lt;p&gt;Starting in September 2026, Android will require all apps to be registered by verified developers in order to be installed on certified Android devices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is how Google introduced its &lt;a href="https://developer.android.com/developer-verification" rel="noopener noreferrer"&gt;new policy&lt;/a&gt; to every Android developer, regardless of whether they publish on the Play Store or not. From that moment on, &lt;strong&gt;if you want to install an app on a certified Android device, it will first have to pass Google’s verification process&lt;/strong&gt;. And let me be clear here: that’s the overwhelming majority of Android devices in existence.&lt;/p&gt;

&lt;p&gt;The verification process itself is very similar to what already exists inside Google Play. Developers basically have to provide identity documents and personal information so that Google can confirm who you are as an individual, or verify your legal identity if you are representing a company.&lt;/p&gt;

&lt;p&gt;Google says this measure is &lt;em&gt;“for user safety”&lt;/em&gt;. And while I do think that on some level this can help reduce the distribution of malware on Android devices, I think there’s more to this story than what we’re being told. &lt;/p&gt;

&lt;p&gt;First, let’s clarify what a &lt;em&gt;“certified Android device”&lt;/em&gt; actually is: it’s any version of Android that has been authorized by Google, where Google Play Services and the Play Store are pre-installed. In other words, almost every phone and tablet you can buy. You can see the list of certified brands &lt;a href="https://www.android.com/certified/partners/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So yes, I can see how this might reduce the spread of malicious apps, since knowing the real identity of a developer can discourage bad actors. But at the same time, this effectively ends the privacy of many legitimate developers who contribute to the Android ecosystem, people who don’t write malware and who simply don’t want to hand over this type of personal information.&lt;/p&gt;

&lt;p&gt;It also raises serious concerns about which types of apps may get banned. Think of apps that provide functionality outside of what Google officially allows, like some emulators or similar. This could easily extend to any app that Google &lt;em&gt;—or anyone powerful enough to pressure Google—&lt;/em&gt; decides is &lt;em&gt;“unacceptable”&lt;/em&gt;. And that takes us into the realm of political and geopolitical pressure, censorship, and ultimately turns Android into a closed ecosystem... almost exactly like Apple’s.&lt;/p&gt;

&lt;p&gt;Yes, it’s true that many custom ROMs such as GrapheneOS or CalyxOS would remain outside of this system, meaning that users of these ROMs could still sideload any app they want without Google’s verification. But the real issue is that many developers may choose not to (or may not be allowed to) pass through this verification process. And in doing so, they risk losing exposure to a massive user base, along with the incentive to keep building apps for Android at all.&lt;/p&gt;

&lt;p&gt;In other words, &lt;strong&gt;if a developer suddenly loses 99% of their potential audience, what real incentive do they have to keep building apps? This is why I believe the measure could indirectly hurt the entire custom ROM ecosystem as well.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  No more unlockable bootloaders?
&lt;/h2&gt;

&lt;p&gt;This trend could already harm custom ROMs by reducing the amount of software available to run on them, making life harder for their users. But the challenges for custom ROMs do not end there.&lt;/p&gt;

&lt;p&gt;For those who may not know, in order to install a custom ROM such as LineageOS or GrapheneOS on an Android device, the bootloader must be unlockable. If the manufacturer decides to disable this option, then that device will only ever be able to run the stock operating system and nothing else. And as I mentioned earlier, some manufacturers, like &lt;a href="https://sammyguru.com/breaking-samsung-removes-bootloader-unlocking-with-one-ui-8/" rel="noopener noreferrer"&gt;Samsung, are already moving in this direction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are several reasons for this. It is very likely that manufacturers are under pressure from large institutions: banks, streaming services, and content providers with strict licensing requirements, to make sure their devices cannot easily be modified. A phone that allows custom ROMs makes it more difficult to enforce DRM and protect licensed content. There are also commercial motivations: manufacturers do not want to deal with warranty claims from users who bricked their phones while flashing custom software. And of course, there are the strategic interests of controlling the ecosystem itself.&lt;/p&gt;

&lt;p&gt;In any case, if this trend continues and more manufacturers ship devices without unlockable bootloaders, custom ROMs could eventually cease to exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Device trees and components removed from AOSP
&lt;/h2&gt;

&lt;p&gt;As if the situation were not already bad enough, there is more. In recent years Google has been steadily removing more and more components from AOSP. AOSP stands for &lt;em&gt;Android Open Source Project&lt;/em&gt;, which is the open-source foundation of Android and the basis for all ROMs—including the “certified” ones. What Google has been doing is moving components out of AOSP and into their own proprietary repositories, which forces custom ROM developers to find their own replacements.&lt;/p&gt;

&lt;p&gt;More recently, with the launch of the Pixel 10 series, Google &lt;a href="https://9to5google.com/2025/06/12/android-open-source-project-pixel-change/" rel="noopener noreferrer"&gt;announced that it would no longer be releasing the device trees for these models&lt;/a&gt;. A device tree is essentially the source code that describes the hardware: Android itself, drivers, and other key components that are required to run the operating system in that device. Without it, any ROM that wants to support the Pixel 10 will need to rely on &lt;em&gt;reverse engineering&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To give you an idea of how complex reverse engineering can be: if I hand you a text file with clear instructions, all you need to do is follow them. But if I give you the same file scrambled with a secret code, and I don’t give you that code, you first have to decipher it before you can even start following the instructions. As you can imagine, the task suddenly becomes much, much more difficult.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the adoption of Play Integrity
&lt;/h2&gt;

&lt;p&gt;Finally, I want to talk about &lt;a href="https://developer.android.com/google/play/integrity/overview" rel="noopener noreferrer"&gt;Play Integrity&lt;/a&gt;. This is a Google Play API that Android developers can currently choose to implement. What it does is basically ensure that your app only runs on a certified Android device with Play Services properly functioning.&lt;/p&gt;

&lt;p&gt;Many apps already use this protection. For instance, the official ChatGPT app by OpenAI has it enabled, and it simply won’t run on a device without Play Services.&lt;/p&gt;

&lt;p&gt;Right now this technology is optional, and it is up to each developer to decide whether or not to adopt it. But I would not be surprised if, over time, more and more pressure is applied to make adoption widespread, or perhaps even mandatory for all apps distributed through Google Play. To be clear, &lt;strong&gt;this is not the case today, and I am speculating here&lt;/strong&gt;. Still, if this scenario were to materialize, it would mean that custom ROMs could no longer run any app that relies on this protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As you can see, the outlook is quite grim. The only realistic way I see this being stopped is through regulatory intervention. In Europe, for example, there are already laws like the Digital Services Act and especially the Digital Markets Act, which require so-called “gatekeepers” such as Google to allow the distribution of apps through alternative stores and sideloading. If Google’s verification process were to become too restrictive, it could very well attract the attention of European regulators. &lt;/p&gt;

&lt;p&gt;In any case, &lt;strong&gt;I predict a very dark future for Android&lt;/strong&gt;. I know my perspective may sound pessimistic, and I sincerely hope the future turns out brighter than what I am imagining. But as of now, I fear that &lt;strong&gt;Android is on the verge of abandoning the very freedom that once defined it&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>android</category>
      <category>mobile</category>
      <category>google</category>
      <category>androiddev</category>
    </item>
    <item>
      <title>🛠️ How to Use Multiple Windows in Flutter Desktop</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Tue, 24 Jun 2025 14:14:06 +0000</pubDate>
      <link>https://dev.to/svprdga/how-to-use-multiple-windows-in-flutter-desktop-2o3j</link>
      <guid>https://dev.to/svprdga/how-to-use-multiple-windows-in-flutter-desktop-2o3j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;One of Flutter’s key strengths is its ability to run on virtually all major platforms today. And while I often focus on mobile and web examples here on this blog, it’s important not to overlook the fact that Flutter is also a perfectly viable option for Windows, macOS, and Linux.&lt;/p&gt;

&lt;p&gt;In fact, the latest major release, Flutter 3.32, introduced some quality-of-life improvements that are definitely worth exploring. So today, we’re going to take a look at working with multiple windows in Flutter desktop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/I-7ccZiPx6c" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This won’t be a traditional tutorial. I’m trying out a more relaxed format where I’ll walk you through small but essential code snippets that show how to work with multiple windows. That said, if you’re looking for a more step-by-step guide, don’t worry, &lt;a href="https://github.com/svprdga/cds_2025_04_multi-window" rel="noopener noreferrer"&gt;here you can access the complete source code&lt;/a&gt;, so you can run it and study it in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  The desktop_multi_window plugin
&lt;/h2&gt;

&lt;p&gt;Before we dive into the implementation, let’s take a moment to understand how Flutter currently handles multiple windows on desktop.&lt;/p&gt;

&lt;p&gt;Out of the box, Flutter doesn’t offer a fully abstracted or official solution for managing multiple windows. If you want to do it manually, you will need to write platform-specific code: creating new native windows and manually instantiating a second FlutterEngine for each one. It’s not exactly beginner-friendly, and definitely not ideal for cross-platform apps trying to stay within Dart code as much as possible.&lt;/p&gt;

&lt;p&gt;That’s where the &lt;a href="https://pub.dev/packages/desktop_multi_window" rel="noopener noreferrer"&gt;desktop_multi_window&lt;/a&gt; plugin comes in. This package wraps all of that complexity and gives us a clean API for creating and managing windows on Linux, macOS, and Windows, without having to dive into C++, Objective-C, or Windows APIs.&lt;/p&gt;

&lt;p&gt;Under the hood, though, each of those windows runs its own separate Flutter engine, and they still rely on native code to communicate with each other. That’s why we use Flutter’s familiar &lt;em&gt;MethodChannel&lt;/em&gt; system to pass messages between them. So, throughout the video, you’ll see things like &lt;em&gt;MethodCall&lt;/em&gt;, &lt;em&gt;invokeMethod&lt;/em&gt;, and similar patterns being used to send data back and forth.&lt;/p&gt;

&lt;p&gt;Hopefully, the Flutter team will eventually introduce a more official and streamlined way to handle multi-window setups directly from Dart. But until then, this plugin is quite mature and works well for most practical use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a class that represents a window
&lt;/h2&gt;

&lt;p&gt;The first thing I recommend when working with multiple windows is to create a class that holds the properties related to each individual window. In my sample project, I created a class called &lt;em&gt;WindowInfo&lt;/em&gt;, and here’s its basic structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WindowInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;WindowController&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;WindowInfo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the very least, you’ll want to store the window’s id and a &lt;em&gt;WindowController&lt;/em&gt;, which will let you later close it, bring it to focus, or perform any other actions. I also added a name field to make identification easier. Depending on the kind of app you’re building, you can extend this class with any additional data you find useful to better manage your open windows.&lt;/p&gt;

&lt;p&gt;In my project, I keep an array of these objects in memory so I can easily work with them later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Windows
&lt;/h2&gt;

&lt;p&gt;Let’s start by looking at how we can create new secondary windows from a main window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_createWindow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Set window details&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'Window &lt;/span&gt;&lt;span class="si"&gt;$_windowCounter&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;windowConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="c1"&gt;// Create the new window&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;windowController&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;DesktopMultiWindow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;jsonEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;windowConfig&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Configure the window using the controller&lt;/span&gt;
      &lt;span class="n"&gt;windowController&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// Add to our tracking list&lt;/span&gt;
      &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_windows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;WindowInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;windowController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nl"&gt;controller:&lt;/span&gt; &lt;span class="n"&gt;windowController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_windowCounter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ScaffoldMessenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Failed to create window: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We begin by wrapping the whole operation in a try-catch block. That’s because anything related to window management can potentially fai, so better to be safe.&lt;/p&gt;

&lt;p&gt;We’re going to prepare the data for the new window. Since we’re ultimately working with the native layer, &lt;em&gt;DesktopMultiWindow&lt;/em&gt; relies heavily on plain JSON strings.&lt;/p&gt;

&lt;p&gt;So first, we store the window’s name in a variable, and then we create a map that includes a "name" property.&lt;/p&gt;

&lt;p&gt;Then we call &lt;em&gt;DesktopMultiWindow.createWindow()&lt;/em&gt;, which launches the new window. This returns a &lt;em&gt;WindowController&lt;/em&gt; object, which we can use to interact with the window later on.&lt;/p&gt;

&lt;p&gt;Next, we configure some properties, position, size, title, and finally call &lt;em&gt;show()&lt;/em&gt; to display it.&lt;/p&gt;

&lt;p&gt;The final part of the method stores the window’s data in an array, so we can interact with it later. In my example, I’m using &lt;em&gt;setState()&lt;/em&gt; just to keep things simple, but I recommend using your favorite state management solution in a real-world project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing windows
&lt;/h2&gt;

&lt;p&gt;Now let’s take a look at how we can close a secondary window when it’s no longer needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_closeWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Find the window controller for this window ID&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;windowInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_windows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;firstWhere&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Use the controller's close method&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_windows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeWhere&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ScaffoldMessenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Failed to close window: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start with a try-catch block, just like we did when creating windows. Then we look for the &lt;em&gt;WindowInfo&lt;/em&gt; object that matches the window ID we want to close. This gives us access to the corresponding controller.&lt;/p&gt;

&lt;p&gt;Once we have that, we simply call its &lt;em&gt;close()&lt;/em&gt; method. That’s all it takes to close the actual native window.&lt;/p&gt;

&lt;p&gt;After closing it, we also want to keep our internal list of windows clean, so we remove the corresponding &lt;em&gt;WindowInfo&lt;/em&gt; from the array.&lt;/p&gt;

&lt;p&gt;And finally, if anything goes wrong during the process, we show a quick message to the user using a SnackBar, or whatever error handling method you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Send messages to a secondary window
&lt;/h2&gt;

&lt;p&gt;Let’s take a look at how to send messages from the main window to a secondary one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sendMessageToWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;DesktopMultiWindow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'message_from_main'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'Hello from main window!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ScaffoldMessenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Response: &lt;/span&gt;&lt;span class="si"&gt;$response&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ScaffoldMessenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Failed to send message: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by using &lt;em&gt;DesktopMultiWindow.invokeMethod()&lt;/em&gt;, which allows us to send a message directly to another window by its ID.&lt;/p&gt;

&lt;p&gt;The first argument is the ID of the target window. The second is the method name, in this case, "message_from_main". And the third is the data we want to send, which is just a string message.&lt;/p&gt;

&lt;p&gt;This method returns a response from the secondary window, if any. In this example, we display that response using a &lt;em&gt;SnackBar&lt;/em&gt;, but you could use it however you like.&lt;/p&gt;

&lt;p&gt;To receive messages in the secondary window, we first register a method handler during the &lt;em&gt;initState()&lt;/em&gt; of our widget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="nd"&gt;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Listen for messages from the main window&lt;/span&gt;
    &lt;span class="n"&gt;DesktopMultiWindow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMethodHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_handleMethodCall&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up the method that will handle incoming calls from other windows, typically from the main window.&lt;/p&gt;

&lt;p&gt;Now here’s what the actual handler method looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_handleMethodCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MethodCall&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromWindowId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'message_from_main'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="n"&gt;ScaffoldMessenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'Message received by secondary window &lt;/span&gt;&lt;span class="si"&gt;${widget.windowId}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside this function, we check if the method is "message_from_main". If it is, we extract the message, display it using a &lt;em&gt;SnackBar&lt;/em&gt;, and return a response indicating that the message was received.&lt;/p&gt;

&lt;p&gt;This is the response that the main window gets back when it calls &lt;em&gt;invokeMethod()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This simple setup lets each secondary window listen for commands or data from the main window, and respond if needed. And of course, you can add as many method types as your app requires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Send messages to the main window from a secondary window
&lt;/h2&gt;

&lt;p&gt;We have already seen how the main window can send messages to secondary ones. Now let’s quickly complete the picture by allowing secondary windows to send messages back to the main window.&lt;/p&gt;

&lt;p&gt;This is all done in the same way, using &lt;em&gt;DesktopMultiWindow.invokeMethod()&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;DesktopMultiWindow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 0 is usually the ID of the main window&lt;/span&gt;
  &lt;span class="s"&gt;'message_from_secondary'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;'Hello from window &lt;/span&gt;&lt;span class="si"&gt;${widget.windowId}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only difference here is that we’re targeting window ID 0, which represents the main window.&lt;/p&gt;

&lt;p&gt;On the main window side, we handle this message with a method handler registered during &lt;em&gt;initState()&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="nd"&gt;@override&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Listen for messages from secondary windows&lt;/span&gt;
  &lt;span class="n"&gt;DesktopMultiWindow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMethodHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_handleMethodCall&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s the handler itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_handleMethodCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MethodCall&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromWindowId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'message_from_secondary'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;ScaffoldMessenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'Message received by main window'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, you now have full two-way communication between windows using simple method calls and arguments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focusing a window
&lt;/h2&gt;

&lt;p&gt;Let’s wrap up this overview by showing how to bring a specific window to the front.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_focusWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Find the window controller for this window ID&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;windowInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_windows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;firstWhere&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;windowId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Use the controller's show method to bring window to front&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ScaffoldMessenger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SnackBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;content:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Failed to focus window: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we search for the &lt;em&gt;WindowInfo&lt;/em&gt; object that matches the ID of the window we want to focus. That gives us access to its controller.&lt;/p&gt;

&lt;p&gt;Then we simply call &lt;em&gt;show()&lt;/em&gt; on the controller. Internally, this brings the window to the foreground if it’s already open. It’s an easy way to programmatically focus or activate any window you’ve previously created.&lt;/p&gt;

&lt;p&gt;And that’s all it takes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Today we explored how to manage multiple windows in a Flutter desktop app using the &lt;a href="https://pub.dev/packages/desktop_multi_window" rel="noopener noreferrer"&gt;desktop_multi_window&lt;/a&gt; plugin. We looked at how to create and close windows, how to send messages back and forth between them, and how to bring any window into focus when needed.&lt;/p&gt;

&lt;p&gt;Hopefully this gave you a solid overview of how multi-window support works in Flutter and how to integrate it into your own projects.&lt;/p&gt;

&lt;p&gt;Thank you very much for reading this article to the end. I hope you have a wonderful rest of your day, goodbye.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>5 Tips to Optimize Your Flutter App 🔥🚀</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Wed, 23 Apr 2025 16:37:42 +0000</pubDate>
      <link>https://dev.to/svprdga/5-tips-to-optimize-your-flutter-app-10f4</link>
      <guid>https://dev.to/svprdga/5-tips-to-optimize-your-flutter-app-10f4</guid>
      <description>&lt;p&gt;Flutter is a fantastic tool for building cross-platform applications quickly and effectively, as it allows you to launch your app for Android, iOS, Windows, macOS, and Linux — all from a single codebase. However, one of the most common criticisms of Flutter throughout its history has been its performance, especially on certain platforms like Apple’s devices. That’s why it’s crucial for you, as a developer, to have the right skills to optimize your app as much as possible, ensuring it runs fast and smoothly on the user’s device. Ideally, the user shouldn’t even be able to tell the difference between a native app and one built with Flutter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/qSXfSPV4yDw" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve been working with Flutter since the framework was still in its beta phase, and over the years, I’ve picked up quite a few tricks that help me achieve this goal. In this article, I want to share with you the five most important aspects I believe you should keep in mind when it comes to optimizing your Flutter application.&lt;/p&gt;

&lt;p&gt;So without further ado, let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid creating methods that return Widgets
&lt;/h2&gt;

&lt;p&gt;One of the most common mistakes I see among Flutter developers —especially those who come from other frameworks or are just getting started— is creating methods that return widgets. While this might seem like a harmless shortcut to keep your code organized, it actually has a direct negative impact on your app’s performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="nf"&gt;createTextWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Widget created inside a method.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason is simple: every time Flutter’s build method runs and hits that method, it will create a brand-new widget instance. Even if nothing has changed, Flutter has no way of knowing that the widget is the same, because you’re giving it a freshly created object every single time. As a result, you lose Flutter’s ability to optimize the widget tree and avoid unnecessary rebuilds.&lt;/p&gt;

&lt;p&gt;Let’s take a look at an example of this problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_ExampleWidgetState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ExampleWidgetState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_buildListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Item &lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;_buildListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this code looks clean and functional. However, every time Flutter rebuilds &lt;em&gt;ExampleWidget&lt;/em&gt;, it will call &lt;em&gt;_buildListItem()&lt;/em&gt; for every single item in the list, generating new widget instances even if the list hasn’t changed at all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your app refreshes this layout 60 times per second, you might think that Flutter would need to build 100 widgets for each refresh — that is, a total of 6,000 builds. However, Flutter applies plenty of optimizations under the hood. In this example, each list item ends up being built around 300 times.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The proper solution: &lt;strong&gt;extract the widget into a dedicated class.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of building the list items through a method, it’s better to define a separate widget class for each item. This allows Flutter to compare widget instances properly and optimize rebuilds.&lt;/p&gt;

&lt;p&gt;Let’s refactor the previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_ExampleWidgetState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ExampleWidgetState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_ListItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Item &lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ListItem&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;_ListItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, Flutter can treat each &lt;em&gt;_ListItem&lt;/em&gt; as an independent widget. If the parent rebuilds, Flutter won’t recreate every list item unnecessarily — it will only update what’s needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If we go back to how many times Flutter builds each widget in the list, previously, Flutter was building each one around 300 times. Now, thanks to this small change, Flutter only needs to build the items in the list about 57 times. It’s also worth noting that other optimizations are happening under the hood here, especially with the ListView widget.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;👉 As a rule of thumb, &lt;strong&gt;you should almost never create widgets inside methods&lt;/strong&gt;. Instead, extract them into their own classes, even private ones within the same file; to help Flutter do its job efficiently. Your widget tree will be cleaner, your app will perform better, and you’ll make life easier for both yourself and the framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prefer using ListView.builder() over passing a list of widgets
&lt;/h2&gt;

&lt;p&gt;When rendering lists in Flutter, a common approach is to pass an array of widgets directly to the children parameter of ListView. While this works fine for small lists, it quickly becomes a problem as your list grows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;// Long list of widgets...&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s because Flutter will instantiate all those widgets in memory immediately, even if most of them aren’t visible. This increases memory usage and slows down your initial build.&lt;/p&gt;

&lt;p&gt;It’s true that Flutter won’t call the build() method for every widget right away — it only builds what’s visible plus a small buffer for smooth scrolling. However, since all widget instances already exist in memory, you’re still paying the cost upfront.&lt;/p&gt;

&lt;p&gt;Let’s take a look at an example of this problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WorkingWithListsWrong&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'Item &lt;/span&gt;&lt;span class="si"&gt;$index&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we’re generating 10,000 items and passing them directly to ListView. Flutter will try to instance all 10,000 widgets immediately, even though the user will only see a small handful of them at any given time. This is a recipe for performance issues, especially on lower-end devices.&lt;/p&gt;

&lt;p&gt;The proper solution: &lt;strong&gt;use ListView.builder()&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To avoid this problem, simply use the builder constructor. Flutter will only create the widgets as they’re needed, keeping memory usage low and scroll performance smooth.&lt;/p&gt;

&lt;p&gt;Here’s how you can refactor the previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'Item &lt;/span&gt;&lt;span class="si"&gt;$index&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;itemCount:&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;itemBuilder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ListTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, instead of instantiating 10,000 widgets up front, Flutter will only instantiate the ones that are currently visible. This simple change massively improves performance, especially with large lists.&lt;/p&gt;

&lt;p&gt;👉 As a rule of thumb, whenever you work with lists of dynamic or large data sets, &lt;strong&gt;you should always prefer ListView.builder()&lt;/strong&gt; over passing an explicit list of widgets. It’s a small change that has a big impact, keeping your app fast and responsive, even as your data grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid heavy logic and repeated calculations inside build()
&lt;/h2&gt;

&lt;p&gt;One of the best ways to keep your Flutter app fast and responsive is to keep the &lt;em&gt;build()&lt;/em&gt; method as light as possible. Remember: &lt;em&gt;build()&lt;/em&gt; can be called many times during the lifecycle of your widget — much more often than you might expect. If you place heavy operations inside build(), you’re forcing Flutter to repeat that work over and over again.&lt;/p&gt;

&lt;p&gt;But it doesn’t stop there. Even lightweight operations can become problematic if you repeat them unnecessarily inside the same &lt;em&gt;build()&lt;/em&gt; method. Flutter executes everything inside build() line by line, so if you call the same calculation multiple times, it will recompute it every single time.&lt;/p&gt;

&lt;p&gt;Let’s look at an example of this problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_ExampleWidgetState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ExampleWidgetState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Max number: &lt;/span&gt;&lt;span class="si"&gt;${_findMax()}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Is 5000 in the list? &lt;/span&gt;&lt;span class="si"&gt;${numbers.contains(5000)}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Max number again: &lt;/span&gt;&lt;span class="si"&gt;${_findMax()}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_findMax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;_findMax()&lt;/em&gt; is called twice in the same build(), causing the calculation to run two times unnecessarily.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;numbers.contains(5000)&lt;/em&gt; is an expensive operation for large lists.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every time the widget rebuilds, these calculations will happen again.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The proper solution: &lt;strong&gt;compute once and reuse&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The solution is simple: compute your values once, store them in local variables, and reuse them throughout &lt;em&gt;build()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the improved version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_ExampleWidgetState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ExampleWidgetState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;maxNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_findMax&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;contains5000&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Max number: &lt;/span&gt;&lt;span class="si"&gt;$maxNumber&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Is 5000 in the list? &lt;/span&gt;&lt;span class="si"&gt;$contains5000&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Max number again: &lt;/span&gt;&lt;span class="si"&gt;$maxNumber&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_findMax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, even if &lt;em&gt;build()&lt;/em&gt; is called multiple times, each expensive operation only runs once per build. This keeps your UI fast and avoids redundant work.&lt;/p&gt;

&lt;p&gt;But, we can do it even better: move calculations to &lt;em&gt;initState()&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_ExampleWidgetState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ExampleWidgetState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxNumber&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;contains5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;maxNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_findMax&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;contains5000&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Max number: &lt;/span&gt;&lt;span class="si"&gt;$maxNumber&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Is 5000 in the list? &lt;/span&gt;&lt;span class="si"&gt;$contains5000&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Max number again: &lt;/span&gt;&lt;span class="si"&gt;$maxNumber&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_findMax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this approach, the heavy calculations happen only once during the widget’s lifecycle. Even if the widget rebuilds multiple times (for example, because of unrelated UI updates), the values are already stored and reused, keeping your build() method clean and extremely fast.&lt;/p&gt;

&lt;p&gt;👉 As a rule of thumb, &lt;strong&gt;avoid placing heavy logic inside build()&lt;/strong&gt;, and always compute your values once per build, or even better, once per widget lifecycle (if possible).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Isolates for heavy computations
&lt;/h2&gt;

&lt;p&gt;Flutter runs your app’s code in a single thread called the main isolate. This thread handles both your app’s logic and its UI rendering. If you perform heavy computations on the main isolate, such as image processing, large data parsing, or complex mathematical calculations, you risk blocking the UI thread, leading to janky animations, delayed interactions, or even complete freezes.&lt;/p&gt;

&lt;p&gt;To avoid this, you can offload heavy tasks to a separate &lt;em&gt;Isolate&lt;/em&gt;. Flutter provides this mechanism specifically to help you run CPU-intensive work in parallel without impacting the smoothness of your app.&lt;/p&gt;

&lt;p&gt;Imagine you’re applying a filter to an image inside your build() or right after loading it on the main thread:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_ExampleWidgetState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ExampleWidgetState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;filteredImage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_applyFilter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_applyFilter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loadImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Simulated image loading&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;applyHeavyFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Heavy operation on main thread&lt;/span&gt;
    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;filteredImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;filteredImage&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
          &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;filteredImage&lt;/span&gt;
          &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Simulated functions&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;loadImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'assets/image.png'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;applyHeavyFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inMilliseconds&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Simulate heavy work&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;em&gt;applyHeavyFilter()&lt;/em&gt; simulates a heavy synchronous operation running on the main isolate. During this time, the app’s UI is blocked, and the user cannot interact with it.&lt;/p&gt;

&lt;p&gt;The proper solution: &lt;strong&gt;use an Isolate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s improve this by moving the heavy operation to a separate isolate. This way, the UI remains smooth while the processing happens in the background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleWidget&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_ExampleWidgetState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ExampleWidgetState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleWidget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;filteredImage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;_applyFilter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_applyFilter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;applyHeavyFilter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'assets/image.png'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;filteredImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;filteredImage&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
          &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;filteredImage&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
          &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Top-level function for compute&lt;/span&gt;
&lt;span class="n"&gt;Image&lt;/span&gt; &lt;span class="nf"&gt;applyHeavyFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;assetPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inMilliseconds&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Simulate heavy work&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assetPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the &lt;em&gt;compute()&lt;/em&gt; function, you offload the heavy task to a background isolate. Your main UI thread stays free to handle animations and user interactions.&lt;/p&gt;

&lt;p&gt;👉 When your app needs to handle heavy operations like image processing, data parsing, or complex calculations, &lt;strong&gt;offload that work to an isolate&lt;/strong&gt;. It keeps your app responsive and smooth, providing a much better user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize your images for performance
&lt;/h2&gt;

&lt;p&gt;Images are often one of the biggest factors affecting your app’s performance. Poorly optimized images can lead to long loading times, high memory usage, and even frame drops if they’re not handled carefully.&lt;/p&gt;

&lt;p&gt;There are three main things to consider when using images in your Flutter app:&lt;/p&gt;

&lt;h3&gt;
  
  
  Use multiple resolutions of your images
&lt;/h3&gt;

&lt;p&gt;Flutter supports 1x, 2x, and 3x image variants. This allows you to provide lower-resolution images for low-density screens and higher-resolution images for high-density screens. Flutter automatically selects the correct image based on the device’s pixel density.&lt;/p&gt;

&lt;p&gt;If you don’t provide these variants, Flutter will scale your image, but it might load unnecessarily large images on devices that don’t need them, wasting memory and processing power.&lt;/p&gt;

&lt;p&gt;For example, if you only include a large image in your assets like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Image.asset('assets/images/large_image.png')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And your image is 3000x3000 pixels, even devices that display it at much smaller sizes will load the full-resolution asset. This increases memory usage and slows down your app.&lt;/p&gt;

&lt;p&gt;The correct approach is to provide proper resolution variants following Flutter’s conventions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assets/images/large_image.png       // 1x — 300x300 px
assets/images/2.0x/large_image.png  // 2x — 600x600 px
assets/images/3.0x/large_image.png  // 3x — 900x900 px
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flutter will automatically pick the right image based on the device’s screen density, giving you better performance without sacrificing image quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the right image format
&lt;/h3&gt;

&lt;p&gt;Different formats serve different needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PNG for images requiring transparency or sharp edges (like icons).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JPEG for photos and complex images with lots of colors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebP for modern, highly compressed images that support both transparency and photos.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing the right format ensures a good balance between quality and file size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compress and size your images properly
&lt;/h3&gt;

&lt;p&gt;No matter the format, always compress your images and use the correct resolution. Avoid using images straight from design tools or cameras at full resolution. For example, if your design only displays an image at 300x300 pixels, there’s no need to load a 3000x3000 pixel asset. Oversized images consume unnecessary memory and slow down your app.&lt;/p&gt;

&lt;p&gt;👉 Images are a critical part of your app’s performance. By providing &lt;strong&gt;proper resolution variants, choosing the right formats, and compressing your assets&lt;/strong&gt;, you’ll reduce your app’s memory usage and improve loading times, all while keeping your UI crisp and fast on every device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;And that’s my top 5 tips to help you improve your Flutter app’s performance. If you found this article helpful, feel free to check out &lt;a href="https://www.youtube.com/@DavidSerranoIO" rel="noopener noreferrer"&gt;my YouTube channel&lt;/a&gt; as well, where I talk about Flutter development, new versions of the framework, and plenty of other topics that you’ll definitely find interesting!&lt;/p&gt;

&lt;p&gt;Thank you for reading all the way to the end, I hope you have a beautiful rest of your day!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Your home address exposed on Google Play 😰</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Mon, 16 Sep 2024 15:37:01 +0000</pubDate>
      <link>https://dev.to/svprdga/privacy-nightmare-as-google-plans-to-publish-your-home-address-1g3d</link>
      <guid>https://dev.to/svprdga/privacy-nightmare-as-google-plans-to-publish-your-home-address-1g3d</guid>
      <description>&lt;p&gt;In recent months, Google Play has rolled out a new account verification process that applies to all developers. &lt;a href="https://support.google.com/googleplay/android-developer/answer/14986433" rel="noopener noreferrer"&gt;These policies&lt;/a&gt; require developers to provide verified information to continue distributing their apps on the platform. The objective, according to Google, is to ensure that the apps available on Google Play meet high standards of quality and reduce the risk of malicious software being distributed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/26rlNTr7SIA" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While these changes are intended to improve the platform's security, they have caused concerns for some developers, especially independent developers with personal accounts, myself included. The most significant worry is related to the new requirement for personal developers to publicly disclose their home address in their developer profile, which can be seen by anyone. For indie developers and individuals who value their privacy, this is understandably alarming.&lt;/p&gt;

&lt;p&gt;In this article, I will try to explain in a simple way what this whole issue is about, what potential problems it implies for small developers, and finally I will try to give some clues on how to try to handle this situation better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Google Play verification process
&lt;/h2&gt;

&lt;p&gt;The Google Play account verification process is designed to identify developers and make apps within the platform safer and more trustworthy.&lt;/p&gt;

&lt;p&gt;For individual developers with a personal account, Google mandates that you verify your identity by submitting documentation such as an official ID and a proof of address. The key challenge lies in the fact that personal developers must use their residential address for this verification. This address is then made publicly available if your app includes in-app purchases, which can be unsettling for many.&lt;/p&gt;

&lt;p&gt;Here’s a breakdown of how the process works:&lt;/p&gt;

&lt;h4&gt;
  
  
  Personal Accounts:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Identity Verification:&lt;/strong&gt; You are required to provide a government-issued identification document (e.g., a passport or driver’s license).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Address Verification:&lt;/strong&gt; You must submit a document showing your residential address, such as a utility bill, bank statement, or lease agreement. Unfortunately, virtual office addresses or PO boxes are not accepted for personal accounts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public Disclosure:&lt;/strong&gt; If you offer in-app purchases, both your name and full home address will be visible on your Google Play profile. If not, only your name and country will be shown.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Organization Accounts:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;For developers using organization accounts, the process is slightly different. Businesses can use a registered business address rather than a personal address, providing an extra layer of privacy. This option makes it easier for companies to maintain privacy while complying with Google's policies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In contrast to other platforms like Apple’s App Store, which allows more flexibility with virtual office addresses in some cases, Google Play has adopted a stricter stance. As a result, many personal developers are now in a difficult position, needing to expose sensitive information or explore costly alternatives to comply with the rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The problem: why personal developers are worried&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One of the biggest concerns for developers with personal accounts with this new verification process is the &lt;a href="https://support.google.com/googleplay/android-developer/answer/10841920#developer-information" rel="noopener noreferrer"&gt;public disclosure of sensitive information&lt;/a&gt;. For developers using a personal account, Google requires them to verify their home address, which, in some cases, is made visible to the public. This can be alarming, as it raises privacy and safety issues, especially for individuals who work from home.&lt;/p&gt;

&lt;p&gt;Many developers feel uncomfortable with their home addresses being available online for anyone to see. This is especially problematic for indie developers, freelancers, or those who prefer to keep their personal and professional lives separate. In online forums and developer communities, there have been multiple &lt;a href="https://www.reddit.com/r/androiddev/comments/1ery2et/googles_new_verification_is_a_violation_of_privacy/" rel="noopener noreferrer"&gt;discussions about these concerns&lt;/a&gt;, with some developers sharing their frustrations about the lack of alternatives, such as using virtual offices or PO boxes.&lt;/p&gt;

&lt;p&gt;While organization accounts allow developers to use a business address, personal accounts don’t offer this flexibility. This forces many developers to either disclose their home address or consider other costly solutions, such as forming a business just to meet the verification requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Alternatives to using a home address&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Based on my own experience and the experience of other developers I have discussed this issue with, it appears that this new personal account verification process does not allow the use of alternative addresses, such as virtual offices or PO boxes. These services allow you to rent a professional address for business correspondence without revealing your home location. However, sending to the review team your contract with one of these services, or any document that proves that it is actually legitimate for you to operate with that address, will cause your document to be rejected due to not being one of the documents accepted in the personal account verification process.&lt;/p&gt;

&lt;p&gt;So the question is, is there any alternative?&lt;/p&gt;

&lt;p&gt;Unfortunately, if you're using a personal account, the options are limited. Some developers have attempted to redirect utility bills to alternative addresses, but this approach depends on your location and whether it's feasible in your case. Otherwise, forming an organization or business may be the only way to use a business address legally for verification purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Converting to an Organization account&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Switching to an organization account can provide a key benefit for developers who are concerned about privacy: it allows you to use a business address instead of your home address, which is required for personal accounts. This can help protect your personal information from being publicly visible on your Google Play profile. However, forming a business comes with its own set of challenges. Depending on where you live, the cost of starting a company can be quite high, which may not be practical for indie developers or those working on a small scale.&lt;/p&gt;

&lt;p&gt;Additionally, running a business introduces legal responsibilities like handling taxes and managing paperwork, which can add complexity and administrative burdens.&lt;/p&gt;

&lt;p&gt;It’s also worth noting that in the near future, a &lt;a href="https://support.google.com/googleplay/android-developer/answer/10788890?visit_id=638620895716432677-4180324833&amp;amp;rd=1" rel="noopener noreferrer"&gt;new policy&lt;/a&gt; will come into effect: for certain types of services, developers are required to register as an organization. This includes apps related to financial services (e.g., banking, loans, cryptocurrency), health and medical apps, VPN services, and government apps. If your app falls into one of these categories, you must create an organization account to comply with Google’s policies. In these cases, switching to an organization account is not just a matter of privacy but also a regulatory requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Practical workarounds for personal developers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For developers who can’t or don’t want to switch to an organization account, the options for protecting your privacy while complying with Google’s policies are limited but not impossible. Although Google doesn’t currently allow virtual offices or PO boxes for personal accounts, there are still a few workarounds you can try to avoid using your home address.&lt;/p&gt;

&lt;p&gt;One potential workaround is to redirect certain types of utility bills or other official documentation to an alternative address. For example, if you have access to a secondary property or a family member’s address that you can legally use, you might be able to switch your billing details to that location. Once you have a utility bill or another acceptable document tied to this alternative address, you can submit it for Google’s verification process.&lt;/p&gt;

&lt;p&gt;Another option could be renting a co-working space or shared office that provides you with an address and utility services. In some cases, co-working spaces issue utility statements or receipts that can be used for verification.&lt;/p&gt;

&lt;p&gt;These workarounds might not be feasible for everyone, and while they don’t offer the same long-term solution as converting to an organization account, they could provide a solution to meet Google’s requirements while protecting your privacy as a personal developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In summary, Google Play’s new verification requirements have introduced challenges for personal developers, particularly around the public disclosure of home addresses. While these policies aim to improve transparency and safety on the platform, they have raised valid privacy concerns. For many indie developers, the options are limited—either comply with the rules as they are or explore costly alternatives like switching to an organization account.&lt;/p&gt;

&lt;p&gt;However, practical workarounds, such as using alternative addresses for documentation, may offer some relief for those who are not ready to make the leap to an organization account. Regardless of the approach you choose, it’s important to stay informed and prepared if you still have to start this verification process.&lt;/p&gt;

&lt;p&gt;I hope this article has been helpful to you, and I regret that I can't offer more definitive solutions to this issue. I hope that in the future, Google will reconsider its policies to better balance transparency and security with the privacy and well-being of developers.&lt;/p&gt;

&lt;p&gt;I hope you have a beautiful rest of your day, goodbye.&lt;/p&gt;

</description>
      <category>google</category>
      <category>privacy</category>
      <category>mobile</category>
      <category>android</category>
    </item>
    <item>
      <title>Learn Flutter by creating your first Flutter app!</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Sun, 05 May 2024 16:49:55 +0000</pubDate>
      <link>https://dev.to/svprdga/learn-flutter-by-creating-your-first-flutter-app-gfb</link>
      <guid>https://dev.to/svprdga/learn-flutter-by-creating-your-first-flutter-app-gfb</guid>
      <description>&lt;p&gt;In this article, I'm going to introduce you to your first Flutter app. We will explore the parts of a Flutter project, their roles, and we'll cover some fundamental concepts of state, including the differences between StatelessWidgets and StatefulWidgets.&lt;/p&gt;

&lt;p&gt;If you haven't installed Flutter yet, here are some step-by-step videos that will guide you through the process of installing Flutter on &lt;a href="https://youtu.be/4pKUdxA49UY" rel="noopener noreferrer"&gt;Mac&lt;/a&gt;, &lt;a href="https://youtu.be/l2TvAVkCgIE" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;, and &lt;a href="https://youtu.be/RFSF4t5FQhg" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/tFbN5_a7_ho" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Creating a basic Flutter app
&lt;/h2&gt;

&lt;p&gt;Let's start by creating a basic application. You can do it from the menus of your favorite code editor, although I always prefer to do it from the terminal:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flutter create flutter_test_app&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command will create a basic counting app that, when executed, allows us to increment a numerical value by clicking on a button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkue6tqpjrvw3c0n342sj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkue6tqpjrvw3c0n342sj.png" alt="Basic counter app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal of this sample code is to give you a first introduction to Flutter. We are going to go little by little to understand this template that is already given to us.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pubspec.yaml Configuration File
&lt;/h3&gt;

&lt;p&gt;The first thing we are going to look at is the pubspec.yaml file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1712400771180%2F4d3f2380-70d9-492a-8c07-ab6d7ea12f1e.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1712400771180%2F4d3f2380-70d9-492a-8c07-ab6d7ea12f1e.png%2520align%3D"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This file contains essential application metadata, enumerates all dependencies, and includes various configuration settings. If you open it, you will find comments for each section that explain their purpose. However, for clarity, we'll remove these comments to keep the file straightforward and briefly overview each segment:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter_test_app&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;can&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;add&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;project&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;here."&lt;/span&gt;
&lt;span class="na"&gt;publish_to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;none'&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0+1&lt;/span&gt;

&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;   sdk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=3.2.6&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;4.0.0'&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let's clarify the meaning of each parameter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: This represents the project's name. It serves as an "internal" identifier and is not the name presented to your users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;description&lt;/strong&gt;: This field allows you to provide a brief outline of your project's purpose.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;publish_to&lt;/strong&gt;: This setting is primarily relevant for package development. Since this article focuses on basic concepts, we'll leave it unchanged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;version&lt;/strong&gt;: Here, you can specify your project's version using semantic versioning followed by an additional integer. This practice enables version control directly from this file, which then applies across platform-specific projects. The trailing integer typically corresponds to the &lt;em&gt;versionCode&lt;/em&gt; in Android projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;environment&lt;/strong&gt;: This specifies the compatible Dart SDK version range for your application. If in the future you want to use new versions of the Dart SDK you might be interested in modifying it, but for now, we leave it as is.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;sdk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter&lt;/span&gt;
  &lt;span class="na"&gt;cupertino_icons&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.2&lt;/span&gt;

&lt;span class="na"&gt;dev_dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;flutter_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;sdk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter&lt;/span&gt;
  &lt;span class="na"&gt;flutter_lints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^2.0.0&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The next part focuses on the application's dependencies, which are categorized into two types: those incorporated into the app's final package (&lt;code&gt;dependencies&lt;/code&gt;) and those used during development but not included in the final app package (&lt;code&gt;dev_dependencies&lt;/code&gt;). To grasp this distinction, let's examine the &lt;a href="https://pub.dev/packages/flutter_lints" rel="noopener noreferrer"&gt;flutter_lints&lt;/a&gt; package as an example. This package aids in static code analysis, which is performed locally on your machine. There's no need for it to be part of the final app package distributed to users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After adding &lt;code&gt;flutter_lints&lt;/code&gt; to your &lt;code&gt;dev_dependencies&lt;/code&gt; and running &lt;code&gt;flutter pub get&lt;/code&gt;, you can analyze your code according to the lint rules specified by &lt;code&gt;flutter_lints&lt;/code&gt; using the command &lt;code&gt;flutter analyze&lt;/code&gt;. This command checks your code for issues based on the linting rules defined by the package.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For finding new packages, Flutter developers often visit &lt;a href="https://pub.dev/" rel="noopener noreferrer"&gt;https://pub.dev&lt;/a&gt;. Say you want to do a network request to a remote server using the &lt;a href="https://pub.dev/packages/http" rel="noopener noreferrer"&gt;http&lt;/a&gt; package, you can simply add it to your dependencies and then execute &lt;code&gt;flutter pub get&lt;/code&gt; to fetch the package and make it ready to use.&lt;/p&gt;

&lt;p&gt;Alternatively, you can use the command &lt;code&gt;flutter pub add http&lt;/code&gt; to not only download but also automatically add the &lt;code&gt;http&lt;/code&gt; package to your &lt;code&gt;dependencies&lt;/code&gt;. To add a package to &lt;code&gt;dev_dependencies&lt;/code&gt;, you would use &lt;code&gt;flutter pub add --dev package_name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Experimenting with these methods can help you determine the most comfortable way to manage packages in your Flutter application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;uses-material-design&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Towards the end of the file, there's a section labeled "flutter," featuring the &lt;code&gt;uses-material-design: true&lt;/code&gt; setting. This particular setting informs Flutter that our application will utilize the &lt;a href="https://m3.material.io/" rel="noopener noreferrer"&gt;Material Design style&lt;/a&gt;, providing a suite of visual, interaction, and motion design guidelines developed by Google.&lt;/p&gt;

&lt;p&gt;As you advance more into Flutter development, you'll encounter a range of additional configurations that can be applied within this file to further personalize themes and other aspects of your app.&lt;/p&gt;

&lt;p&gt;Additionally, it's worth mentioning the &lt;code&gt;pubspec.lock&lt;/code&gt; file, a crucial component of Flutter projects. This file is automatically generated by Flutter when you run commands like &lt;code&gt;flutter pub get&lt;/code&gt; or &lt;code&gt;flutter pub add&lt;/code&gt;. Its primary purpose is to record the exact versions of each dependency used in your project at the time these commands are executed. This ensures that your project remains consistent and stable, even if dependencies are updated in the future. By tracking these versions, the &lt;code&gt;pubspec.lock&lt;/code&gt; file helps to prevent the "it works on my machine" problem by ensuring that every developer working on the project uses the same versions of dependencies, thus minimizing conflicts and compatibility issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Platform-Specific Projects in Flutter
&lt;/h3&gt;

&lt;p&gt;Within a Flutter project, beyond the &lt;code&gt;pubspec.yaml&lt;/code&gt; and &lt;code&gt;pubspec.lock&lt;/code&gt; files, you'll notice several directories named after platforms. These directories are &lt;code&gt;android&lt;/code&gt;, &lt;code&gt;ios&lt;/code&gt;, &lt;code&gt;macos&lt;/code&gt;, &lt;code&gt;linux&lt;/code&gt;, and &lt;code&gt;windows&lt;/code&gt;. These aren't just folders; they're complete native projects for their respective platforms.&lt;/p&gt;

&lt;p&gt;Flutter's strength lies in its ability to provide a multi-platform development framework, hiding the complexities of platform-specific implementation details. Yet, the existence of these platform-specific projects is crucial for Flutter to operate seamlessly across different environments.&lt;/p&gt;

&lt;p&gt;There are instances when you'll need to dip into native development within these directories. This is often the case when certain functionality can only be achieved with platform-specific code. It's in these scenarios that modifications to the native parts of a project become necessary.&lt;/p&gt;

&lt;p&gt;It's important to note that you're not required to maintain all these directories if your app doesn't target all supported platforms. For instance, if your focus is solely on Android and iOS, you can safely remove the &lt;code&gt;macos&lt;/code&gt;, &lt;code&gt;linux&lt;/code&gt;, and &lt;code&gt;windows&lt;/code&gt; directories.&lt;/p&gt;

&lt;p&gt;Conversely, should you decide to expand your app's availability to additional platforms not initially included in your project, Flutter simplifies this expansion. Using the &lt;code&gt;flutter create&lt;/code&gt; command with the &lt;code&gt;--platforms&lt;/code&gt; option, you can add the necessary platform projects. For example, if you start with a project only supporting Android and iOS and later decide to include support for macOS, Linux, and Windows, you can execute &lt;code&gt;flutter create --platforms=macos,linux,windows .&lt;/code&gt; This command creates the required directories for the new platforms to be supported.&lt;/p&gt;

&lt;h1&gt;
  
  
  Your app's source code: the lib directory
&lt;/h1&gt;

&lt;p&gt;We have already seen the main files and directories of a Flutter project, although it is true that I have not listed them all, for now I have described the most relevant ones that you should know from the beginning. Now let's jump to the lib directory, the place where all the Dart code that makes up your application resides.&lt;/p&gt;

&lt;p&gt;When you create a new project, Flutter automatically generates the &lt;code&gt;main.dart&lt;/code&gt; file within the &lt;code&gt;lib&lt;/code&gt; folder. This file contains the source code responsible for the counter app that you see if you run the project. If you enter, you will see some comments that explain each section. As before, we are going to eliminate these comments and we are going to explain each part little by little:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The first line introduces an import of &lt;code&gt;material.dart&lt;/code&gt;. This import is essential because, by default, we use Material widgets to construct the user interface.&lt;/p&gt;

&lt;p&gt;Following that, we encounter the &lt;code&gt;main()&lt;/code&gt; method. Every Dart application, Flutter included, requires an entry point, which is provided by the &lt;code&gt;main()&lt;/code&gt; function. Within this function, we call &lt;code&gt;runApp()&lt;/code&gt;, enabling the application to launch. We pass it an instance of &lt;code&gt;MyApp&lt;/code&gt;, which is the next widget we come across in the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Flutter Demo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;theme:&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;colorScheme:&lt;/span&gt; &lt;span class="n"&gt;ColorScheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromSeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seedColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deepPurple&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;useMaterial3:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Flutter Demo Home Page'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This snippet introduces the &lt;code&gt;MyApp&lt;/code&gt; widget, serving as the foundational element of your application. It's at the top of the widget hierarchy, essentially acting as the root from which all other widgets will branch out. Flutter apps are structured as a vast tree of widgets, where each widget may be a parent or a child to others. Here, &lt;code&gt;MyApp&lt;/code&gt; stands as the initial node in this interconnected structure.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;MyApp&lt;/code&gt; is defined as a class that extends &lt;code&gt;StatelessWidget&lt;/code&gt;. &lt;code&gt;StatelessWidgets&lt;/code&gt; are characterized by their lack of internal state—they don't manage any data that changes over time. Consequently, a &lt;code&gt;StatelessWidget&lt;/code&gt; does not rebuild itself in response to internal data changes. Further details on this will be provided as we progress.&lt;/p&gt;

&lt;p&gt;Every &lt;code&gt;StatelessWidget&lt;/code&gt; must implement the &lt;code&gt;Widget build(BuildContext context)&lt;/code&gt; method. This method is where the app's user interface is constructed. In this example, we create a &lt;code&gt;MaterialApp&lt;/code&gt; widget within this method. &lt;code&gt;MaterialApp&lt;/code&gt; facilitates the development of an app following Material Design guidelines, including aspects like the app's title and theme.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;home&lt;/code&gt; attribute specifies the widget that will be displayed when the app starts. Here, it's set to &lt;code&gt;MyHomePage&lt;/code&gt;, which is the widget that comes next in the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_MyHomePageState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;colorScheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inversePrimary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;mainAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;MainAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;[&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s"&gt;'You have pushed the button this many times:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;$_counter&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headlineMedium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;floatingActionButton:&lt;/span&gt; &lt;span class="n"&gt;FloatingActionButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="n"&gt;_incrementCounter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;tooltip:&lt;/span&gt; &lt;span class="s"&gt;'Increment'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;MyHomePage&lt;/code&gt; is a widget similar to &lt;code&gt;MyApp&lt;/code&gt;, but it inherits from &lt;code&gt;StatefulWidget&lt;/code&gt; instead of &lt;code&gt;StatelessWidget&lt;/code&gt;. This distinction introduces two related classes: &lt;code&gt;MyHomePage&lt;/code&gt; itself, which sets up the widget, and &lt;code&gt;_MyHomePageState&lt;/code&gt;, a class that manages the widget's state, extending &lt;code&gt;State&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The reason &lt;code&gt;_MyHomePageState&lt;/code&gt; starts with the underscore symbol (_) is to indicate that this class must be private within this file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the state class, we're obliged to implement the &lt;code&gt;Widget build(BuildContext context)&lt;/code&gt; method again. However, this time it's within the state class, where we define a widget tree that composes the counter interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Initially, a &lt;code&gt;Scaffold&lt;/code&gt; widget lays out the basic structure of our screen, considering elements like system navigation bars.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;code&gt;AppBar&lt;/code&gt; acts as the top navigation bar, where we specify a title and modify the background color using themes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;Scaffold&lt;/code&gt;'s body comprises a &lt;code&gt;Center&lt;/code&gt; widget, which ensures its content is centered on the screen. Inside the &lt;code&gt;Center&lt;/code&gt;, we place a &lt;code&gt;Column&lt;/code&gt; for vertical arrangement of widgets. This column contains two &lt;code&gt;Text&lt;/code&gt; widgets: one displays a static message, and the other shows the dynamic &lt;code&gt;_counter&lt;/code&gt; value, styled with the current theme.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;floatingActionButton&lt;/code&gt; property of the &lt;code&gt;Scaffold&lt;/code&gt; employs a &lt;code&gt;FloatingActionButton&lt;/code&gt; widget. Positioned at the bottom right, this button is tasked with increasing the counter each time it's pressed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Understanding State Management in Flutter
&lt;/h3&gt;

&lt;p&gt;Now that we have seen roughly everything that is in the &lt;code&gt;main.dart&lt;/code&gt; file, let's understand in a simple way how Flutter manages the state.&lt;/p&gt;

&lt;p&gt;As I said before, the &lt;code&gt;_MyHomePageState&lt;/code&gt; class is responsible for managing the state of the &lt;code&gt;MyHomePage&lt;/code&gt; widget. In this case, we have an application with a numerical value that increments when a button is pressed. This is the state. Specifically, the variable that is defined at the beginning of the class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;_incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;When we invoke the &lt;code&gt;_incrementCounter()&lt;/code&gt; method, it calls the &lt;code&gt;setState&lt;/code&gt; method and within it the value of the &lt;code&gt;_counter&lt;/code&gt; variable is incremented. By calling the &lt;code&gt;setState&lt;/code&gt; method we are updating the state, telling Flutter that it has changed and causing the &lt;code&gt;build&lt;/code&gt; method to run again, but this time with the updated state. Later, in the second Widget of type &lt;code&gt;Text&lt;/code&gt;, the &lt;code&gt;_counter&lt;/code&gt; variable is read to display the value on the screen.&lt;/p&gt;

&lt;p&gt;This is a very simplified explanation of state management in Flutter, let's perform an experiment, add the following line just before build returns the &lt;code&gt;Scaffold&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'State refresh'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Add this new line&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now run the application again and look at the output log. You will see the String "State refresh" printed when you run the application, but also when you modify the value of the counter.&lt;/p&gt;

&lt;p&gt;As you can see, this is the simplest way to manage the state of your application in Flutter. In addition, you have also been able to observe the role of the build method and its importance when composing the interface based on state changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your first app in Flutter: key concepts
&lt;/h2&gt;

&lt;p&gt;If this is your first contact with Flutter, it is possible that at this point you are a little saturated with so much information, don't worry, I am going to list the key points that we have seen throughout the article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A Flutter project contains a &lt;code&gt;pubspec.yaml&lt;/code&gt; file. It defines several configuration parameters as well as the list of packages that are used by the project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Flutter project can contain several subdirectories named after a platform such as &lt;code&gt;android&lt;/code&gt; or &lt;code&gt;ios&lt;/code&gt;, these are the native projects that Flutter uses to run on each platform.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the &lt;code&gt;lib&lt;/code&gt; directory we find the source code of the project, this is where we will write our files in &lt;em&gt;Dart&lt;/em&gt; code to create our application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At its core, a Flutter app is structured as an extensive hierarchy of widgets. Widgets can be of two types: &lt;code&gt;StatefulWidget&lt;/code&gt;, which holds state (variables that can change over time), and &lt;code&gt;StatelessWidget&lt;/code&gt;, which does not hold state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can alter the state of a &lt;code&gt;StatefulWidget&lt;/code&gt; by using the &lt;code&gt;setState&lt;/code&gt; method.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are broadly the main basic points that you should know if you are getting started in developing applications with Flutter.&lt;/p&gt;

&lt;p&gt;I hope this article has been useful to you. Do not hesitate to follow this blog and my &lt;a href="https://www.youtube.com/@DavidSerranoIO" rel="noopener noreferrer"&gt;YouTube channel&lt;/a&gt; if you want to continue learning about application development with Flutter, as well as stay up to date with news and other topics of interest about this magnificent framework.&lt;/p&gt;

&lt;p&gt;Thanks for reading this far, happy coding!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>tutorial</category>
      <category>mobile</category>
      <category>learning</category>
    </item>
    <item>
      <title>Is it worth learning Flutter in 2024?</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Fri, 19 Jan 2024 16:04:35 +0000</pubDate>
      <link>https://dev.to/svprdga/is-it-worth-learning-flutter-in-2024-16i</link>
      <guid>https://dev.to/svprdga/is-it-worth-learning-flutter-in-2024-16i</guid>
      <description>&lt;p&gt;Flutter, the great cross-platform framework that allows you to create applications for mobile, web, and desktop with a single codebase. However, despite all the benefits it conveys, is it a good decision to invest time and energy in learning this technology in 2024?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/d173ldnXLzo"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have asked yourself this question lately, it is because in some way you are interested in Flutter, either for the mere fact of learning, because perhaps you are evaluating it for a possible project, or perhaps because you wonder if you could make a living as a Flutter developer working for companies.&lt;/p&gt;

&lt;p&gt;My name is David, I have been a Flutter programmer since this framework was in beta, and in this channel, I teach and inform on everything related to Flutter development.&lt;/p&gt;

&lt;p&gt;Before we start let me answer the question of whether it is worth learning Flutter in 2024, and the answer is that it depends. It depends a lot on what you are looking for. If you do it to learn new things, or if you are evaluating it for a possible project of yours, then yes, it is worth it. Now, if all you are interested in is finding a job, then perhaps you could consider other options.&lt;/p&gt;

&lt;p&gt;Now that I have answered the main question in short, let me expand this information in more detail to understand if it is really a good idea to learn Flutter in 2024.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes Flutter different?
&lt;/h2&gt;

&lt;p&gt;Let's understand for a moment the strengths and distinctive points of Flutter to be able to make a future projection of where this framework is headed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Flutter allows you to develop for Android, iOS, web, Linux, macOS, and Windows with a single codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flutter has a feature called "hot reload", which allows you to see the changes you make to the code instantly on your device, significantly shortening development times.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flutter contains a rich library of visual elements, many of them based on the Material design guidelines and many others based on Cupertino, the visual style of iOS and macOS. Additionally, by installing third-party packages it is possible to emulate the visual style of Linux or Windows, among others.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flutter compiles to native code, which significantly increases its performance. The result is fast and light applications, which have nothing to envy of purely native development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flutter uses Dart, a programming language that is easy to learn, powerful, strongly typed, null-safe, and with many interesting capabilities for all types of environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flutter has a strong and growing community due to its recent popularity and also Google's efforts to promote its use. This means that developers have a rich ecosystem of resources, including documentation, tutorials, YouTube channels like this one, forums, etc. where to ask for advice if you have any problem.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we are clear about the strengths of Flutter, let me tell you in which cases I recommend learning Flutter in 2024 and in which cases I don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you should learn Flutter
&lt;/h2&gt;

&lt;p&gt;Let's consider several scenarios in which I encourage you to learn Flutter:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→ To learn new skills&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Diving into Flutter is a fantastic way to broaden your technical horizons. If what interests you is simply the joy of learning, then Flutter will meet your expectations. With its unique approach to UI design, efficient code structure, and the use of Dart, learning Flutter can significantly enhance your understanding of mobile app development, opening doors to other facets of the industry, and helping you improve your programming skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→ To evaluate it for a potential project&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you're considering various frameworks for an upcoming project, getting hands-on with Flutter can be incredibly insightful. Its ability to deliver high-performance, visually appealing apps across multiple platforms from a single codebase makes it a strong contender. By learning Flutter, you can effectively assess its fit for your project's specific needs, such as user interface requirements, development timeline, and overall performance expectations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→ To create prototypes quickly&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When it comes to turning ideas into tangible prototypes quickly, Flutter is a game-changer. Thanks to its vast widget library and hot reload feature, you can rapidly iterate designs and functionalities. This speed and flexibility are crucial in the early stages of development, where visualizing and modifying ideas quickly is key to successful project development and stakeholder communication.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→ To cut costs migrating from native to cross-platform&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Transitioning from native app development to a cross-platform approach can be a strategic move to reduce costs and development time. Flutter stands out in this transition due to its single codebase feature, which eliminates the need to maintain separate codes for iOS and Android. This consolidation not only reduces initial development costs but also simplifies ongoing maintenance and updates, making it a cost-effective solution for long-term app development.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→ For education purposes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Whether you’re a student, educator, or self-learner, Flutter is an excellent educational tool. It’s designed to be intuitive and easy to learn, making it accessible for beginners. At the same time, its comprehensive capabilities offer depth for advanced learners. Flutter’s growing popularity also ensures that learners are gaining skills that are in demand in the current job market, providing a practical edge to their educational pursuits.&lt;/p&gt;

&lt;p&gt;In each of these scenarios, Flutter's versatility, efficiency, and comprehensive features make it a standout choice for anyone looking to expand their app development skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you shouldn't learn Flutter
&lt;/h2&gt;

&lt;p&gt;In the real world there are no generalities but rather each case is particular, for that same reason after seeing the specific cases in which I recommend learning Flutter, let's now see in which cases I recommend thinking carefully about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→&lt;/strong&gt; To make a living... in regions where there is no demand
&lt;/h3&gt;

&lt;p&gt;Flutter is a great framework for creating applications, but it competes with other very good technologies such as React Native, which has been on the market longer, and also competes in a way against native development. If your only intention is to learn Flutter as a means of earning a living, then at least make sure that the region you live in is in demand for this type of technology. In some cases you may find that the demand for Flutter developers is low compared to the demand for native developers, for example. In this specific case, I would recommend that you choose wisely in which technology you are going to invest your time and energy.&lt;/p&gt;

&lt;p&gt;If, on the other hand, after having done a search for the current demand you see that there are quite a few companies that are demanding Flutter programmers, then in that case it could be a good career path.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→&lt;/strong&gt; For applications requiring a high level of performance
&lt;/h3&gt;

&lt;p&gt;This point sounds contradictory given that in the previous section I told you that Flutter has practically native performance. Let me qualify it:&lt;/p&gt;

&lt;p&gt;For "normal" applications, without very specific performance requirements other than the mere fact that it runs fast and smoothly, then Flutter is a great candidate. However, at this point I am not talking about those types of applications. At this point I am referring to applications that have performance requirements well above average, we are talking for example about video editors, graphics engines, etc.&lt;/p&gt;

&lt;p&gt;In this specific case, if your application is going to require this intensive level of performance, it is preferable that you consider building it natively, perhaps programming the most critical parts in C++ or Rust.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→&lt;/strong&gt; For apps that require a lot of native integrations or low-level features
&lt;/h3&gt;

&lt;p&gt;Flutter offers a fantastic bridge to connect the parts written in Dart with the native part of the application. In this way, even if we are creating an application with Flutter, it is possible to code some parts directly in the native language, then we can take advantage of its performance and integrations that are only available at the native level.&lt;/p&gt;

&lt;p&gt;Now, if your application is going to be full of these integrations, to the point that we are going to have to use this communication bridge all the time, I think it would be better to write it directly natively.&lt;/p&gt;

&lt;p&gt;If the native bridge is required frequently, then the primary benefits of Flutter are largely negated. In such scenarios, you're not truly engaging in cross-platform development, nor are you fully committing to native development. Therefore, in these situations, I would advise opting for a fully native approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;→&lt;/strong&gt; If there is already in-house knowledge of native programming
&lt;/h3&gt;

&lt;p&gt;If your team already possesses significant expertise in native development, it might be more prudent to leverage this existing knowledge rather than immediately adopting a new technology.&lt;/p&gt;

&lt;p&gt;While Flutter is user-friendly and relatively easy to master, it still demands time and dedication to learn. If your company or organization already has the capability to develop applications using native languages, it could be beneficial to carefully consider the advantages and disadvantages of introducing Flutter. This thoughtful evaluation can guide a more informed decision about whether to integrate Flutter into your development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As you can see, the decision to invest time and effort into learning a technology like Flutter depends on your specific goals and needs. If, after conducting your research, you choose to learn Flutter, remember that this blog offers articles on Flutter development that could assist you during your learning journey.&lt;/p&gt;

&lt;p&gt;Thank you for reading this article. I hope you found it informative and helpful. Should you wish to share any thoughts, I would be delighted to read them in the comments section.&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>development</category>
    </item>
    <item>
      <title>Flutter 3.16 released! Android Impeller preview, Game Toolkit Updates, iOS extensions and more!</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Thu, 16 Nov 2023 18:54:58 +0000</pubDate>
      <link>https://dev.to/svprdga/flutter-316-released-android-impeller-preview-game-toolkit-updates-ios-extensions-and-more-1c7n</link>
      <guid>https://dev.to/svprdga/flutter-316-released-android-impeller-preview-game-toolkit-updates-ios-extensions-and-more-1c7n</guid>
      <description>&lt;p&gt;We now have the new version of Flutter available, &lt;strong&gt;Flutter 3.16&lt;/strong&gt;, and it's packed with cool new stuff like Impeller in preview mode for Android, support for Predictive Back Navigation for Android 14 and Material 3 as the default visual style. Also, you can now use Flutter widgets in some iOS extensions. And for the game devs, there are some sweet updates in the Flutter Games Toolkit. This is the last major update we're getting this year, and it's all about making our development faster and our projects better, so let's see in detail the most notable new features of Flutter 3.16.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/gNhlGpYggVA"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Impeller in preview mode for Android
&lt;/h2&gt;

&lt;p&gt;The new rendering engine of Flutter, Impeller, is now available in preview mode for Android on the stable channel. This version introduces a Vulkan backend designed to enhance the performance of Flutter apps on Vulkan-capable devices. The update shows significant improvements in frame rasterization times, leading to less jank and higher framerates.&lt;/p&gt;

&lt;p&gt;However, Impeller is still not expected to work well on older devices that do not have Vulkan support. The Flutter team is still working on the backend for OpenGL so we will have to wait a little longer to see good performance on these types of devices.&lt;/p&gt;

&lt;p&gt;You can now try Impeller on devices that support Vulkan by passing the &lt;code&gt;--enable-impeller&lt;/code&gt; flag to the &lt;code&gt;flutter run&lt;/code&gt; command or by adding the following section to the project's &lt;code&gt;AndroidManifest.xml&lt;/code&gt; under the &lt;code&gt;&amp;lt;application&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt;
  &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"io.flutter.embedding.android.EnableImpeller"&lt;/span&gt;
  &lt;span class="na"&gt;android:value=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides working on the Vulkan Backend, the Flutter team has been working hard to improve text performance in Impeller for both Android and iOS. They've made changes that help manage text better and reduce slowdowns, especially in apps with a lot of text. This has made these apps smoother and more responsive. Additionally, the team has focused on fixing various issues and enhancing overall quality and stability for users on both platforms. They've been really proactive, addressing a lot of user feedback and making numerous updates to improve the overall experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Material 3 is now the default visual style
&lt;/h2&gt;

&lt;p&gt;Starting with the 3.10 release in May 2023, Flutter's Material library has been updated to align with the latest Material Design guidelines. This update includes new components, themes, and refreshed visuals for existing components. Previously, to use these updates, developers had to opt-in by setting a theme flag. But now, with this new release, the Material 3 visual style is the default setting on Flutter apps.&lt;/p&gt;

&lt;p&gt;If you prefer the older Material 2 version, you can still opt out of Material 3 by setting the &lt;code&gt;useMaterial3&lt;/code&gt; property in your &lt;code&gt;MaterialApp&lt;/code&gt; widget to &lt;code&gt;false&lt;/code&gt;. However, it's worth mentioning that &lt;strong&gt;Material 2 will eventually be deprecated&lt;/strong&gt;. Some widgets in Material 3 are completely new implementations, so when upgrading, you might notice changes in your app's UI. To address this, you should manually migrate to the new widgets.&lt;/p&gt;

&lt;p&gt;Remember that Flutter also offers a demo application where you can experiment with all the Material 3 components and compare them with the previous version. The look of Material 3 components is mainly defined by the color and text themes set in the app's ThemeData. Developers can create a Material 3 color scheme easily with tools that generate pleasing and accessible color schemes, either from a base color or even from the dominant colors in an image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Predictive Back Navigation in Android and iOS app extensions
&lt;/h2&gt;

&lt;p&gt;In this latest Flutter update, there are exciting enhancements for both Android and iOS platforms. On the Android side, a significant improvement has been made to the mouse scroll wheel support. Before this update, using a mouse scroll wheel on Android tablets or foldables was a bit clunky, requiring considerable movement for any scrolling action on the screen. Now, Flutter has enhanced this feature, ensuring that scrolling with a mouse feels more natural and matches the scroll speed typical on Android devices.&lt;/p&gt;

&lt;p&gt;Another key update for Android is the introduction of predictive back navigation, a feature from Android 14. This feature allows users to use the back gesture on their device to quickly see the home screen behind the current screen. Flutter's integration of this feature brings a more intuitive and consistent user experience, aligning with the latest Android functionalities.&lt;/p&gt;

&lt;p&gt;On the iOS front, Flutter has expanded its capabilities to include some iOS app extensions. This means that developers can now use Flutter widgets to design the UI for certain types of iOS app extensions. However, this functionality is not universal across all types of app extensions, as some might have API limitations or memory restrictions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exciting news about the Flutter Games Toolkit
&lt;/h2&gt;

&lt;p&gt;Flutter has seen a significant rise in its use for casual game development over the past few years. A large number of games, ranging from simple puzzles to complex arcade games, have been created using Flutter.&lt;/p&gt;

&lt;p&gt;To further support game developers, Flutter is now releasing a major update to its &lt;a href="https://flutter.dev/games"&gt;Flutter Casual Games Toolkit&lt;/a&gt;. This update includes a variety of new resources aimed at helping developers progress from initial concepts to fully launched games. The toolkit now offers more genre-specific templates, such as those for card games and endless runner games, and integrates various services like Play Games Services, in-app purchases, ads, achievements, Crashlytics, and multiplayer support.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Flutter Favorites packages!
&lt;/h2&gt;

&lt;p&gt;Flutter's community and tools are always growing and changing, and with that, the Flutter Favorite program has been refreshed and updated! This is an exciting time for developers as the Flutter Ecosystem Committee has recently recognized a selection of packages as new Flutter Favorites. These include our beloved &lt;a href="https://pub.dev/packages/flame"&gt;flame&lt;/a&gt; game engine, the &lt;a href="https://pub.dev/packages/macos_ui"&gt;macos_ui&lt;/a&gt; package to make your macOS applications look native, and the wonderful &lt;a href="https://pub.dev/packages/riverpod"&gt;riverpod&lt;/a&gt; state manager package.&lt;/p&gt;

&lt;p&gt;Keep an eye out for future Flutter Favorites as this program continues to spotlight exceptional packages and plugins. If you want to nominate a package or plugin to this program to become a Flutter Favorite you can do so by sending an email to the committee at &lt;a href="//mailto:flutter-committee@googlegroups.com"&gt;flutter-committee@googlegroups.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in Flutter DevTools
&lt;/h2&gt;

&lt;p&gt;Flutter's DevTools has introduced a new extensions framework that offers several exciting possibilities. Now, package authors can create custom tools for their packages, which will show up directly in DevTools. They can also make use of existing DevTools frameworks and utilities to build powerful tools. For Dart and Flutter developers, this means they'll have access to specialized tools that are tailored to their specific app development needs, based on the app's dependencies. Authors of popular packages like Provider, Drift, and Patrol have started building this ecosystem, and you can use their DevTools extensions right now.&lt;/p&gt;

&lt;p&gt;In addition to these extensions, the latest DevTools release brings a bunch of improvements and new features. Highlights include the addition of support for DevTools extensions and a new "Home" screen that offers a summary of your connected app. Other improvements focus on enhancing overall performance, the robustness of hot restarts, text selection and copying behavior, as well as polishing the network profiler response viewer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;These are just a few of the key updates in Flutter version 3.16. With the Material 3 update almost fully implemented, and Impeller showing great performance on iOS, we're now looking forward to seeing how it performs on Android and other platforms. Meanwhile, the Flutter team is continuously working hard, adding new features and making performance enhancements. We will also keep an eye on the latest developments in Flutter's packages and plugin ecosystem.&lt;/p&gt;

&lt;p&gt;For more in-depth information on all these updates and more, you can check out the &lt;a href="https://medium.com/flutter/whats-new-in-flutter-3-16-dba6cb1015d1"&gt;official blog post&lt;/a&gt;. Thanks for sticking with this summary to the end, and I hope you have a wonderful rest of your day.&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

</description>
      <category>flutter</category>
    </item>
    <item>
      <title>Insane new App/Game launch requirements on Android 😰</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Mon, 13 Nov 2023 18:11:19 +0000</pubDate>
      <link>https://dev.to/svprdga/insane-new-appgame-launch-requirements-on-android-564i</link>
      <guid>https://dev.to/svprdga/insane-new-appgame-launch-requirements-on-android-564i</guid>
      <description>&lt;p&gt;Google announced the other day its new plans regarding changes to the policy that governs developers and applications published on Google Play. In its tireless fight against malware, fraud and low-quality applications, Google has decided to introduce a fundamental change that can harm the dreams of many indie game or app developers, if not end those dreams completely.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/01xI01GXzlo"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;a href="https://android-developers.googleblog.com/2023/11/ensuring-high-quality-apps-on-google-play.html"&gt;this article&lt;/a&gt; published on the official Android Developers Blog, we can see a summary of these changes. To start, Google is going to start asking for identity verification information for all developers who publish games or applications on Google Play, but this is not the particular change I want to talk about in this article.&lt;/p&gt;

&lt;p&gt;If we go to the next point we can read the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Developers with newly created personal Play Console accounts will soon be required to test their apps with at least 20 people for a minimum of two weeks before applying for access to production. This will allow developers to test their app, identify issues, get feedback, and ensure that everything is ready before they launch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means that if you have created a developer account on Google Play after November 13, 2023, or if you plan to create one, you will necessarily have to go through a two-week testing phase in which you will be responsible for finding 20 people who want to test your application or game. This will be an essential requirement to be able to launch your application or game in production, that is, make it available to all Google Play users.&lt;/p&gt;

&lt;p&gt;Now, what exactly is this minimum of 20 testers? Well, 20 people will have to give you their Google email address so that you can add them as testers of your application, they will have to download your app or game, and they will have to test it. And how much will they have to test it? Does it help if each of those 20 people downloads it and tries it for 5 minutes once? As far as I know, the Google Play team has not yet provided more details about it, but if we go to this &lt;a href="https://support.google.com/googleplay/android-developer/answer/14151465"&gt;official documentation&lt;/a&gt; page we can read the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you have a newly created personal developer account, you must run a closed test for your app with a minimum of 20 testers who have been opted-in for at least the last 14 days continuously.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The question here is: what exactly does this &lt;em&gt;continuously&lt;/em&gt; mean? Does this mean that these 20 testers will have to be testing your app or game each day of those 2 weeks? Does this mean that there has to be a certain number of openings for it to count as valid testing?&lt;/p&gt;

&lt;p&gt;These are all questions that unfortunately we do not know for now. And why does Google do this? Well, in the statement that I mentioned previously, we can read:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Developers who regularly use Play's app testing tools before publishing release higher-quality apps and games, which can lead to higher ratings and more success on Google Play. In fact, apps that use our testing tools have on average 3 times the amount of app installs and user engagement compared to those that don't.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, it seems that what Google intends here is to put an end to the appearance of hundreds of applications that do not contribute anything to the ecosystem and that, supposedly, reduce the general quality of the software that we can find on Google Play.&lt;/p&gt;

&lt;p&gt;And now I want to focus on an important factor: this will only affect the applications or games of developers who have a personal account. &lt;strong&gt;If you have a company account, this restriction will not affect you&lt;/strong&gt;, you will be able to deploy applications and games without them having gone through any mandatory testing phase.&lt;/p&gt;

&lt;p&gt;Some individuals have started to highlight the concern that the current efforts might undermine the capacity of independent developers to launch new apps and games. While I acknowledge that numerous releases may be of subpar quality, it's important to remember that there are very good applications and games that started precisely this way, with very humble beginnings but that little by little managed to cover an existing need in the market.&lt;/p&gt;

&lt;p&gt;Furthermore, although we are told that this will only affect newly created personal accounts, I would not be surprised to see this policy also extended in the future to any product from any developer, old or new, who wants to launch a new application or game on Google Play.&lt;/p&gt;

&lt;p&gt;I don't know about you, but I can't easily have 20 people to ask them to test my applications continuously for two weeks; and probably most indie developers neither. It's quite likely that if this policy is implemented in this way, businesses will begin to emerge, I cannot say whether legitimate and safe or not, but for a modest price will provide us with a group of &lt;em&gt;paid&lt;/em&gt; testers with whom we can test new applications. In the end, it will be a new stopper for new developers, who will have to take into account this initial investment without knowing if their application or game will have any kind of success or not.&lt;/p&gt;

&lt;p&gt;In my opinion, I completely understand that Google wants to have safe and useful applications in its store, however, this does not seem like the right way to achieve it. If you want to discourage the creation of junk apps then you could do what Apple does: ask for an annual fee for developers who want to publish in your store. Right now to be a Google Play developer you have to pay an initial fee of $25. This is a one-time payment that is made at the beginning and gives you the possibility of being a developer without ever paying anything else. To publish in Apple's AppStore you have to pay a fee of $99 annually, meaning that every year you will have to pay that money. This means that only those who have a minimum of interest and desire to create good products agree to publish in the store, even if they are small indie developers.&lt;/p&gt;

&lt;p&gt;The truth is that I feel that being an Android developer is getting harder and harder every day, every year more and more policies are approved that restrict the ability we have to innovate and create new products. Furthermore, we have decisions like this that make creating applications or games on Google Play an exhausting task as time passes. I predict a future on Google Play dominated by corporations, those large companies with the capacity to cope with all the dedication that is necessary to stay up to date with new policies and to adapt applications to the increasingly numerous restrictions. All this while small developers go elsewhere. It is this group of indie developers who provide creativity, who provide novelty, and keep the ecosystem fresh. I have been doing this for more than 10 years and it is a shame to see the decline that Google Play and Android in general are having.&lt;/p&gt;

&lt;p&gt;However, this is just my personal opinion. Tell me what you think of these changes in the comments section if you want. Thank you for reading until the end, I hope you have a nice rest of your day.&lt;/p&gt;

</description>
      <category>android</category>
      <category>indie</category>
      <category>google</category>
    </item>
    <item>
      <title>Build your knowledge base with Spreading: The AI-Powered Knowledge Base Platform for Developers</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Tue, 24 Oct 2023 11:05:09 +0000</pubDate>
      <link>https://dev.to/svprdga/build-your-knowledge-base-with-spreading-the-ai-powered-knowledge-base-platform-for-developers-7a8</link>
      <guid>https://dev.to/svprdga/build-your-knowledge-base-with-spreading-the-ai-powered-knowledge-base-platform-for-developers-7a8</guid>
      <description>&lt;p&gt;Technology advances very quickly, and that implies that the entire flow of information, good practices, systems, and know-how that circulate within a development team, within a sector, or an entire organization also circulates quickly and dynamically.&lt;/p&gt;

&lt;p&gt;Finding the necessary information quickly and accurately can become a challenge if you do not have the necessary tools, and that is why in today's article I am going to talk to you about &lt;a href="https://app.spreading.ai/"&gt;Spreading&lt;/a&gt;: an AI-powered knowledge base platform for developers, which makes it easy to build a self-service knowledge base for your customers and developers.&lt;/p&gt;

&lt;p&gt;This article is sponsored by &lt;a href="https://www.zegocloud.com/"&gt;ZEGOCLOUD&lt;/a&gt;, a leading company in the development of high-quality products for businesses and developers. Spreading is its new product based on artificial intelligence that can help you build your knowledge base quickly and easily, and the best part is that is &lt;strong&gt;100% free, so you can instantly build a self-service knowledge base with Spreading to your customers now!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But first of all, let's see what exactly a knowledge base is and what functions it performs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a knowledge base and why do I need one?
&lt;/h2&gt;

&lt;p&gt;At its core, a knowledge base is a centralized repository for information, a structured compendium of insights and answers. Think of it as an encyclopedia for your product, service, or platform, where all pertinent details, from FAQs to intricate technical explanations, reside.&lt;/p&gt;

&lt;p&gt;A knowledge base serves as a pivotal resource not only for developers but also as a strategic advantage for product owners. By equipping developers with essential information, it paves the way for superior and more robust applications, elevating the user experience. This centralized information hub leads to a noticeable drop in support-related queries, enabling the support team to address difficult challenges. It further creates a channel for user feedback, aiding product enhancement based on real-world insights. Additionally, by presenting a thorough knowledge base, brands position themselves as industry frontrunners, emphasizing their dedication to the developer community and their commitment to excellence.&lt;/p&gt;

&lt;p&gt;Now that we have a basic understanding of what a knowledge base is and what it is for, let's see how Spreading can help us create one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Spreading: the all-in-one AI knowledge base builder
&lt;/h2&gt;

&lt;p&gt;To start seeing how Spreading can help us, let's first go to its &lt;a href="https://app.spreading.ai/login/"&gt;registration page&lt;/a&gt; to create a 100% free account with which we can start testing the product.&lt;/p&gt;

&lt;p&gt;We are going to create a page by clicking on the button located at the top right titled &lt;em&gt;Add a page&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R6qj5dez--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7dhh708gfaglyturilox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R6qj5dez--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7dhh708gfaglyturilox.png" alt='Press the "Add a page" button to start writing a page for our knowledge base.' width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new file will appear, we click on it to open it, and then we click on the button located at the top right titled &lt;em&gt;Edit&lt;/em&gt; to start editing it.&lt;/p&gt;

&lt;p&gt;At this point, there are different ways in which Spreading can assist us in creating our document. Let's imagine that we want to create an explanation document on how to generate a sales report on our platform. In order to work on this document, we will first need a base template of the points to explain.&lt;/p&gt;

&lt;p&gt;To do this, let's click on the &lt;em&gt;Generate outline&lt;/em&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9341puUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjuqovjfibwuo0gchnms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9341puUg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjuqovjfibwuo0gchnms.png" alt='Click on "Generate outline" to create a template to work on.' width="304" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we will have to choose the type of documentation to generate, we are going to choose &lt;em&gt;User guide&lt;/em&gt; since what we want is for this document to serve as an explanation of how to do something to a specific user. As for the target audience, let's imagine that this feature of our product is sales-oriented, so we will choose &lt;em&gt;Sales&lt;/em&gt; so the AI adapts the language appropriately:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2SHagqpP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gl7dk9ldy4v472uai09x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2SHagqpP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gl7dk9ldy4v472uai09x.png" alt='Choose the "User guide" and "Sales" option.' width="524" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step is to give the artificial intelligence a little more context so that it knows what it has to explain. We are going to add context information and click on the &lt;em&gt;Generate&lt;/em&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hVgAPnXG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sk1kbxrfuip8gsly08ov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hVgAPnXG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sk1kbxrfuip8gsly08ov.png" alt="Let's give the AI a little more context so it understands what we want to create." width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And in this simple way, we now have a base template on which to start working on our document:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6YXs_JnO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e98gkgvwvtam5wuj03sn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6YXs_JnO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e98gkgvwvtam5wuj03sn.png" alt="Spreading has generated an outline on which to start working." width="800" height="744"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring more features Spreading has for document generation
&lt;/h2&gt;

&lt;p&gt;We now have a base on which to start working. This document is still a text document, so we could just start typing. However, we are going to save some time by having the integrated AI auto-generate more content for us.&lt;/p&gt;

&lt;p&gt;Continuing with the example, suppose we want to explain what benefits generating a sales report can have. For the AI to help us, we can simply enter a guide text, then select it and click on the AI button to see what options we have:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1gMLm3mU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ifcbkgzvbjq3oujz9456.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1gMLm3mU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ifcbkgzvbjq3oujz9456.png" alt="Enter some guidance text, select it and press the AI button to see the auto-generation options." width="445" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, I have chosen the option of expanding the information provided. In this way, the AI will generate a richer text with more details:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B9CgqNnh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mksvvl3claw282kzzvwx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B9CgqNnh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mksvvl3claw282kzzvwx.png" alt="The artificial intelligence has expanded the guide text that we have written, providing more details." width="800" height="757"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this text is generated, we can make it longer if we consider it necessary and insert it into the document&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of Spreading for creating developer documentation
&lt;/h2&gt;

&lt;p&gt;Another quite interesting function of Spreading is the help it gives us to generate documentation with code examples aimed at software developers.&lt;/p&gt;

&lt;p&gt;Let's see it better with an example. We are going to generate a blank document and we are going to write the &lt;strong&gt;/&lt;/strong&gt; character so we can see the list of available options. Let's choose &lt;em&gt;Code Block&lt;/em&gt;. A blank code block will open:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k7CqIdkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pfmw7qpyannfz2b5ewo3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k7CqIdkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pfmw7qpyannfz2b5ewo3.png" alt="A block of code appears." width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, we can help ourselves with the integrated AI to perform various functions. For example, one of the most important features of a developer-oriented knowledge base is to expose a code example and explain what it is for. To do this we can copy the code example within the block, then enable the AI in the button located at the top right and ask it to explain to us what that code is for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U9nbSDmd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aaismevvr3pe1x64qifv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U9nbSDmd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aaismevvr3pe1x64qifv.png" alt="We copy the code into the block and enable the AI." width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The AI suggests an explanation that we could add to improve the understanding of the code block:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mGI_w4dv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aeah1g2i3pegjf4ar7lv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mGI_w4dv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aeah1g2i3pegjf4ar7lv.png" alt="The AI offers us an explanation of the provided code." width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we click on the &lt;em&gt;insert&lt;/em&gt; button, we will have auto-generated an entire explanation of the code provided automatically:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aAhaQvN8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anseex0lotilnj2yh3kj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aAhaQvN8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anseex0lotilnj2yh3kj.png" alt='We click on "Insert" and we now have the completely auto-generated explanation.' width="800" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As you can see, creating a knowledge base is very quick and easy with &lt;a href="https://app.spreading.ai/"&gt;Spreading&lt;/a&gt;. From its ease of use to the variety of built-in features it includes, both text customization and styling, to the built-in artificial intelligence features, as well as the fact that it is &lt;strong&gt;completely free&lt;/strong&gt;, make this product very important to take into account if you want to be as efficient as possible when you have to create your knowledge database.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://www.zegocloud.com/"&gt;ZEGOCLOUD&lt;/a&gt; for sponsoring this article, I hope you found it useful and informative.&lt;/p&gt;

&lt;p&gt;See you in the next article.&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

</description>
      <category>knowledge</category>
      <category>knowledgbase</category>
      <category>ai</category>
    </item>
    <item>
      <title>What Godot devs need to know about this new EU law (Cyber Resilience Act)</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Fri, 20 Oct 2023 14:38:23 +0000</pubDate>
      <link>https://dev.to/svprdga/what-godot-devs-need-to-know-about-this-new-eu-law-cyber-resilience-act-5hdl</link>
      <guid>https://dev.to/svprdga/what-godot-devs-need-to-know-about-this-new-eu-law-cyber-resilience-act-5hdl</guid>
      <description>&lt;p&gt;The Cyber Resilience Act, or &lt;em&gt;CRA&lt;/em&gt;, is a new legislation that aims to establish new cybersecurity requirements for devices and software marketed in the European Union.&lt;/p&gt;

&lt;p&gt;But what impact can the CRA have in terms of video game development, and more specifically, what impact can it have on Godot or your game if you consider selling it in any European country?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/TWv-LweJaco"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article I am going to try to delve a little deeper into this topic, but, before going into detail, I would like to clarify two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First: at the time of writing this article, all we know is what is written in a &lt;a href="https://digital-strategy.ec.europa.eu/en/library/cyber-resilience-act"&gt;draft&lt;/a&gt; of this new law, so everything discussed here is subject to change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Second: I am not a legal expert, I am simply going to try to explain in simple terms what I have been able to find out and give my opinion about how it could affect the video game development industry.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So without further ado let's try to figure out what CRA is, and what impact it could have on Godot and in games published in the EU.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Cyber Resilience Act, or CRA?
&lt;/h2&gt;

&lt;p&gt;Introduced by the European Union, the CRA seeks to elevate cybersecurity standards for digital devices and software intended for the EU market.&lt;/p&gt;

&lt;p&gt;Both manufacturers and developers who market devices or software in the European Union, without taking into account their global location, must ensure the security of such digital products so they reach the market with fewer vulnerabilities and ensure that relevant quality standards are taken seriously. In addition, it's also intended that users can take this quality into account when consuming products with digital elements.&lt;/p&gt;

&lt;p&gt;A significant shift of the CRA is its focus on software developers rather than the companies that use this software. The rationale? Developers are in a prime position to address vulnerabilities at their source, making software inherently safer.&lt;/p&gt;

&lt;p&gt;However, not all software is seen as equal under the CRA. Based on its use and potential risks, software is divided into three categories.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Non-critical products: It is estimated that the vast majority of products will be in this group. In my opinion, both Godot and video games would fall into this group.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Critical Class 1: This level includes software with higher-risk functions such as password managers, VPNs, network management systems, or remote access software.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Critical Class 2: Reserved for products that have the highest level of risk: operating systems, public key infrastructure and digital certification, general-purpose microprocessors or routers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on its classification, software may face varying obligations. These range from comprehensive risk assessments to ensure software doesn't have known vulnerabilities and is configured for security by default, to extensive documentation detailing the product's design, vulnerabilities, and compliance with EU cybersecurity standards.&lt;/p&gt;

&lt;p&gt;Additionally, while non-critical software developers can self-certify their products, those in the Critical Class categories must undergo assessments by external EU-certified auditors.&lt;/p&gt;

&lt;p&gt;When vulnerabilities are detected, especially those under active exploitation, developers have a tight 24-hour window to report them to the European Union Agency for Cybersecurity.&lt;/p&gt;

&lt;p&gt;Now that we have a general idea about what the CRA is and what new obligations it imposes, let's see what impact it can have on Godot and games released in European territory.&lt;/p&gt;

&lt;h2&gt;
  
  
  How will the CRA impact Godot and games published in the EU?
&lt;/h2&gt;

&lt;p&gt;In my opinion, based on the three groups of products discussed above, everything would seem to indicate that both video game engines and the video games themselves would fall within the group of non-critical products; given that its impact in terms of risk seems to be less pronounced than the products considered critical by this new rule.&lt;/p&gt;

&lt;p&gt;But even as a non-critical product, both the Godot development team and any video game maker might not be exempt from the obligations that the CRA imposes. This act implies that developers, even of non-critical software, carry out a compliance self-assessment.&lt;/p&gt;

&lt;p&gt;This self-assessment is essentially a commitment to ensure that their software complies with the cybersecurity standards outlined by the CRA. It means ensuring there are no known vulnerabilities, providing security patches, and maintaining a &lt;em&gt;secure by default&lt;/em&gt; approach.&lt;/p&gt;

&lt;p&gt;However, given the open-source nature of Godot, much like other free projects, this can generate additional complexities. This is because this type of software is distributed freely, without exhaustive control of who it is distributed to or for what reasons, therefore it can be harmful to impose this level of obligations on someone who simply delivers software freely without any type of cost.&lt;/p&gt;

&lt;p&gt;For other open-source projects considered higher risk such as Linux distributions, the implications of the CRA could be more profound. Given their consideration as critical products, these projects could face much stricter regulations; which could discourage a company from delivering software as free software given this level of complexity and responsibility.&lt;/p&gt;

&lt;p&gt;So far, everything seems to indicate that those projects considered "non-commercial" would be exempt from this rule, however, there are those who consider that an open-source project, if it receives financing even through donations on a regular basis, could be considered as commercial software and would have to comply with the CRA. Without a doubt Godot, given that it is frequently financed through altruistic donations, could fall into this group if this were confirmed.&lt;/p&gt;

&lt;p&gt;As for video games marketed in Europe, they would undoubtedly also fall within the scope of the CRA as non-critical products, so they would be under the obligations discussed above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As we reach the end of our deep dive into the CRA's potential implications, it's essential to remember that what we've discussed is based on the current draft of the act.&lt;/p&gt;

&lt;p&gt;This means the provisions, classifications, and requirements can still change before the act is finalized and put into effect. The legislative process is both dynamic and responsive, and feedback from stakeholders, including the open-source community, can shape the final form of the act.&lt;/p&gt;

&lt;p&gt;Throughout our exploration, we've uncovered the potential challenges the Godot team might face, from the intricacies of self-assessments to the added layers brought on by its open-source nature. But with every challenge, there lies an opportunity.&lt;/p&gt;

&lt;p&gt;In this context, it’s an opportunity for the Godot community to rally together, employ enhanced security practices, and continue producing the remarkable software that countless developers rely on.&lt;/p&gt;

&lt;p&gt;We'll continue to watch the development of the CRA, always hopeful and confident that the spirit of open-source collaboration will thrive, adapt, and lead the way into a secure, innovative future.&lt;/p&gt;

</description>
      <category>godot</category>
      <category>godotengine</category>
      <category>gamedev</category>
      <category>law</category>
    </item>
    <item>
      <title>Game Programming Patterns in Godot: The Command Pattern</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Sat, 14 Oct 2023 13:44:04 +0000</pubDate>
      <link>https://dev.to/svprdga/game-programming-patterns-in-godot-the-command-pattern-5aff</link>
      <guid>https://dev.to/svprdga/game-programming-patterns-in-godot-the-command-pattern-5aff</guid>
      <description>&lt;p&gt;Have you ever been playing an adventure game and suddenly lost control of your character because a cutscene starts, revealing more of the story? Or in a strategy game or map editor, have you had the option to undo a recent action?&lt;/p&gt;

&lt;p&gt;You've probably come across these situations many times, but have you ever wondered how they're coded? Is all that extra logic stuffed into the Player class?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/nWUngckUbe8"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this article, I'll dive into a popular design pattern that's super handy for these scenarios: &lt;strong&gt;the Command pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This design pattern lets us wrap a request as an object, offering a range of flexibilities and capabilities I'll walk you through. I have created this example in which we can control a character, making him move and attack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YGz3-JFh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on6vkdy5g23lffos8gr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YGz3-JFh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on6vkdy5g23lffos8gr9.png" alt="The example shows a character that the player controls" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I want that when he meets this wolf, I want an AI to take over, approach the wolf, and attack. Think of this as a cutscene where we want the main character to act out a specific part of the story. This could be followed by some kind of dialogue or anything else.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F-OkxttS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anwuzb5rhz0v8zm1faka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F-OkxttS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anwuzb5rhz0v8zm1faka.png" alt="We want an AI to take control onces we find this wolf" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you might think about cramming all this logic into the Player class. But that's not ideal since it mixes responsibilities and further bloats one of the typically largest classes in gaming, which is the Player class.&lt;/p&gt;

&lt;p&gt;This design pattern is eloquently detailed in a book called "Game Programming Patterns". I highly recommend reading it, since it provides an exhaustive review of several design patterns in software development applied to video game development, with code examples, explanations and insights on each pattern. While &lt;a href="https://gameprogrammingpatterns.com/"&gt;it's available online&lt;/a&gt; if you prefer physical copies as I do, here's a &lt;a href="https://amzn.to/3QhtkSY"&gt;link for you to purchase&lt;/a&gt; it in case you are interested (&lt;em&gt;Disclosure: As an Amazon Associate I earn from qualifying purchases&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;So if you are interested in learning how to use this design pattern in Godot to achieve cleaner, tidier, and more efficient code, keep reading!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Commands
&lt;/h2&gt;

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

&lt;p&gt;In the example I created, I am using the classic method to control the character, for example, if I want to get the input value to move it left or right:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movement_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_action_strength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"move_right"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_action_strength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"move_left"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if I want to detect if the attack key has been pressed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_action_just_pressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"attack"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea is to replace all of this so that this logic is outside of the Player class.&lt;/p&gt;

&lt;p&gt;To do this, the first thing we must create is the Command class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt;
&lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class defines an execute() method in which it receives a Player instance and an optional "data" argument. The goal of this method is to execute the appropriate action on the Player. Note that at this point we are coupling this Command to the Player, making it unable to be used by any other entity. I do this to keep this tutorial as simple as possible, but in a real case instead of the Player, you could receive a base class from which all your actors inherit.&lt;/p&gt;

&lt;h3&gt;
  
  
  MovementCommand
&lt;/h3&gt;

&lt;p&gt;Now we are going to create the command that will allow the player to move:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="n"&gt;MovementCommand&lt;/span&gt;
&lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;

    &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create the MovementCommand class that inherits from Command, and we set a Params class where we are going to indicate the direction of movement using a float value, -1 for left, 0 for staying still, and 1 for moving to the right.&lt;/p&gt;

&lt;p&gt;In the execute method what we have to do is call the appropriate Player method to perform the move.&lt;/p&gt;

&lt;h3&gt;
  
  
  AttackCommand
&lt;/h3&gt;

&lt;p&gt;We do the same for the attack command, but in this case it won't be necessary to create any class parameters since we do not need to know anything else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="n"&gt;AttackCommand&lt;/span&gt;
&lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Command&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attack&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the controllers
&lt;/h2&gt;

&lt;p&gt;Now that we have the necessary commands created, we need something to issue them. I'm going to call this concept "Controller". The idea is to have a base class, and extend it to allow the player to control the character, and another to make the AI control it.&lt;/p&gt;

&lt;h3&gt;
  
  
  PlayerController
&lt;/h3&gt;

&lt;p&gt;Let's start by creating the PlayerController base class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="n"&gt;PlayerController&lt;/span&gt;
&lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;


&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt;

&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movement_command&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MovementCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;attack_command&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AttackCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this class maintains a reference to Player and instantiates each of the relevant commands. We make this class inherit from Node since we are going to have to interact with the game loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  HumanController
&lt;/h3&gt;

&lt;p&gt;This is when you will see how everything takes shape. We create HumanController with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="n"&gt;HumanController&lt;/span&gt;
&lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;PlayerController&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;_physics_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_delta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movement_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_action_strength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"move_right"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_action_strength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"move_left"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_action_just_pressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"attack"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attack_command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;movement_command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MovementCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movement_input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea will be to add this node as a child of the Player, this way we can detect the user's input and execute the appropriate command. To do this, we will first have to create a container for this or any other controller:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VzAI8GNt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zrlcjypuvdw24m0g3cq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VzAI8GNt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zrlcjypuvdw24m0g3cq.png" alt="Create a container for the controller in the Player scene" width="253" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we will add the following logic to the Player class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Maintain a reference to the current controller
&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PlayerController&lt;/span&gt;

&lt;span class="c1"&gt;# Reference to the container that will contain the controller node
&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;onready&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_controller_container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ControllerContainer&lt;/span&gt;

&lt;span class="c1"&gt;# By default, we create a HumanController
&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;_ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;set_controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HumanController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# We added this method to allow changing the active controller
&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;set_controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PlayerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Delete all previous controllers
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_controller_container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_children&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue_free&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;    
    &lt;span class="n"&gt;_controller_container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, if everything has gone well, we should be able to run the game and control the main character as we did before.&lt;/p&gt;

&lt;h3&gt;
  
  
  AiController
&lt;/h3&gt;

&lt;p&gt;Next, we are going to create another class that inherits from PlayerController, but this time we will create a custom AI that will send the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="n"&gt;AiController&lt;/span&gt;
&lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;PlayerController&lt;/span&gt;


&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_init_time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_is_attack_end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;_ready&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;_init_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_ticks_msec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;_physics_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_delta&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_ticks_msec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;_init_time&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;movement_command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MovementCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;current_time&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3900&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;movement_command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MovementCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;_is_attack_end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;attack_command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;movement_command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MovementCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# In this example, the player class will tell this controller that 
# the attack has ended. As I say, it's a quick example, 
# there are better ways to manage this.
&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;on_attack_end&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;_is_attack_end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the code that I have put inside &lt;code&gt;_physics_process()&lt;/code&gt; is completely orientational. I recommend that you use more sophisticated mechanisms so that your AI is accurate, but I think you can already see the benefit that the Command pattern brings us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let AI take control
&lt;/h3&gt;

&lt;p&gt;The last step we have left is to decide at what point the AI will take control of the character. For example, we could create an Area2D and add the following script to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Area2D&lt;/span&gt;


&lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="n"&gt;_on_body_entered&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AiController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the character touches this area, the controller will be changed from HumanController to AiController, and from that point on it will be our logic that will control the character's actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benefits of the Command pattern
&lt;/h2&gt;

&lt;p&gt;As you have seen, we have just managed to decouple the control of the character from the Player class. Through this mechanism we can obtain other benefits, not only being able to control the player in the cutscenes, but also for example that the player begins to control an enemy, or conversely that an enemy begins to control the player.&lt;/p&gt;

&lt;p&gt;Furthermore, one of the most widespread uses of this pattern is to be able to redo actions. If you think about it, we're encapsulating each action in a Command object, so we could store all of these objects and then be able to go back and redo actions already performed.&lt;/p&gt;

&lt;p&gt;I hope you found this article useful, we'll see in the next one.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>godot</category>
      <category>godotengine</category>
      <category>gamedev</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Can Godot screw us like Unity did?</title>
      <dc:creator>David Serrano</dc:creator>
      <pubDate>Mon, 18 Sep 2023 19:10:55 +0000</pubDate>
      <link>https://dev.to/svprdga/can-godot-screw-us-like-unity-did-49h1</link>
      <guid>https://dev.to/svprdga/can-godot-screw-us-like-unity-did-49h1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Unity has just destroyed itself&lt;/strong&gt; and along with it an entire community of video game developers who have put their effort, money, time and hope into it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📽 Video version available on &lt;a href="https://youtu.be/6ZOSQsiU0DY"&gt;YouTube&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think that by now we are all aware of the &lt;a href="https://www.theverge.com/2023/9/12/23870547/unit-price-change-game-development"&gt;magnitude of the tragedy&lt;/a&gt;: Unity is going to start charging a &lt;a href="https://unity.com/runtime-fee"&gt;runtime fee&lt;/a&gt; of $0.2 every time a player installs a game. If the player installs your game 10 times? You pay 10 times that amount. If the player installs a pirate copy of your game? You pay for that pirated copy. If you have a mobile game and your profit margin is less than this 0.2$, which is not uncommon in the mobile industry, you're going bankrupt.&lt;/p&gt;

&lt;p&gt;Studios of all sizes, as well as indie developers, are looking for other video game engines to be able to port their current games or as potential candidates for future projects. All this in search of a technology that can be trusted to continue working in a safe, predictable and sustainable way.&lt;/p&gt;

&lt;p&gt;However, Unity has not only destroyed itself and its community but has managed to sow a wave of distrust toward the corporations and groups that control these engines. If Unity has done it and I decide to move to another engine, what stops the group that controls that new engine from doing the same to me in the future too? Will we always be subject to the decisions of a small group of greedy people who will control our livelihood without being able to do anything about it?&lt;/p&gt;

&lt;p&gt;In this article, I intend to address this topic, especially concerning the open-source Godot video game engine. If you chose this engine over Unity, stay with me because I'm going to explain &lt;strong&gt;why Godot is probably miles away from being able to do as much damage to the community as Unity has done&lt;/strong&gt;. Let me explain to you why.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Godot's case different from Unity's?
&lt;/h2&gt;

&lt;p&gt;To answer the question of why Godot is less likely to screw you than Unity, we must first understand what the contractual relationship is between a Unity user, and Unity Software Inc., the company that develops the engine.&lt;/p&gt;

&lt;p&gt;When you download Unity Hub and proceed to install any of the versions of the engine, you accept the &lt;a href="https://unity.com/es/legal/terms-of-service"&gt;terms of service&lt;/a&gt; that they provide you. This document defines the contractual relationship between you, as a Unity user, and the company that develops it.&lt;/p&gt;

&lt;p&gt;These terms of service include a provision that allows Unity Software Inc. to alter these terms. This may include the option to alter their rates for using the engine. It remains to be seen if how they have made these changes is completely legal, but it could be said that, unless you have agreed to a special contract with Unity and you are governed by the terms of the standard service, they can make these types of modifications unilaterally. So to be clear: &lt;strong&gt;yes, they can screw you any time they feel like it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To make matters worse, the terms of service before the current version allowed you to take advantage of the terms of service in force concerning the year of release of a certain version of the Unity editor, that is, one could continue using last year's Unity version, along with the terms of service in effect last year, and that new changes to the current terms will not affect you. However, &lt;a href="https://www.gamerbraves.com/unity-silently-deletes-github-repo-that-tracks-terms-of-service-changes-and-updated-its-license/"&gt;this clause was eliminated&lt;/a&gt;, so we could say that the current situation has been premeditated and what they've done has been done so that all developers have to pay the new runtime fee without being able to do anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the contractual relationship between you and Godot?
&lt;/h2&gt;

&lt;p&gt;Now let's see under what legal basis you are offered and allowed to use the Godot open-source game engine.&lt;/p&gt;

&lt;p&gt;When you download and use Godot, you do so under the project's open-source license, in this case, the MIT license; which can easily be found in the &lt;a href="https://github.com/godotengine/godot/blob/master/LICENSE.txt"&gt;open repository in the LICENSE.md file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The MIT license is known as, if not the most, one of the most permissive and least restrictive licenses in the entire open-source software ecosystem. It only takes up 20 lines and allows you to do practically anything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use the engine for free.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make video games or any other type of software with it and then distribute it, sell it, or for whatever.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It does not force you to maintain the same license in the derivative works (that is, the games you make with it). You can make completely proprietary games with it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can even take the engine, rename it and sell it as is.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, the MIT license is designed so that you can do almost anything in almost any way. So as long as Godot is distributed under this license, one can safely use it without being afraid of hidden fees, price changes, or anything else that could turn your studio upside down from one day to the next.&lt;/p&gt;

&lt;p&gt;Now, the question that arises next is: what if the license changes? How likely is it that Godot will no longer have the MIT license? What if it stops being open-source? Can a company acquire it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I doubt Godot will deviate from the MIT license
&lt;/h2&gt;

&lt;p&gt;First, let's understand what the implications are of changing the license of an open-source project: to begin with, each of the contributions that each contributor has made throughout the life of the project belongs to them. That is, the copyright of the code that you provide when you contribute to an open-source project belongs to you, which in turn you license to the end user through the license chosen by the project.&lt;/p&gt;

&lt;p&gt;This means that from a copyright point of view, Godot belongs to every one of its contributors. Therefore, for the project to change licenses, every one of them would have to accept that license change.&lt;/p&gt;

&lt;p&gt;At the time of writing, there are a total of 2203 people who have contributed to the engine, so all of these 2203 people should agree and accept a new license for the engine.&lt;/p&gt;

&lt;p&gt;This is something that can certainly happen. In fact, this is exactly what happened with the &lt;a href="https://bevyengine.org/"&gt;Bevy engine&lt;/a&gt;, in which a total of 246 contributors agreed to relicense the engine, as explained in this &lt;a href="https://bevyengine.org/news/bevy-0-6/#dual-mit-apache-2-0-license"&gt;blog post&lt;/a&gt;. However, in this case it was done for the good of the engine and its users.&lt;/p&gt;

&lt;p&gt;Imagining a situation in which more than 2,000 Godot contributors agree to change the engine license and start screwing the community and its users seems quite surreal to me and unlikely to happen. Let's understand that we are not talking about greedy executives looking for money, but about technology enthusiasts who enjoy using the engine for free and contributing code to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can fork the engine and create a copy for yourself
&lt;/h2&gt;

&lt;p&gt;However, let's put ourselves for a moment in the worst-case scenario: all Godot contributors agree and decide to change the license to one that is much more restrictive or that somehow prevents you from continuing to create commercial games with the engine.&lt;/p&gt;

&lt;p&gt;The solution in this case would also be relatively easy: &lt;strong&gt;fork the engine, create a copy for yourself and bypass the new license. It's that simple.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It would not be ideal, since you would not be able to access the new contributions and new features that would be released, but at least it would give you a window of time in which to maneuver and look for alternatives.&lt;/p&gt;

&lt;p&gt;In any case, I think the probability of something like this happening is very small.&lt;/p&gt;

&lt;p&gt;Godot, like many other open-source projects, is run by people with a very different profile from that of an executive who joins a company to get rich. Don't get me wrong, I'm not saying that using Godot is 100% safe and that in no case can anything bad happen to you, I'm just giving my opinion and my arguments as to why I think it's unlikely that a similar situation like the one that is happening with Unity will occur, and if it did happen, I believe that the resulting damages would be smaller and much more controllable.&lt;/p&gt;

&lt;p&gt;In any case, this is my opinion, and as a Godot user, if I have to highlight something positive about the current horrible Unity situation, it is the boost this is going to give to the engine.&lt;/p&gt;

&lt;p&gt;When one door closes, another opens, and I think the door that is opening now with Godot is very hopeful and will be beneficial for everyone in the years to come.&lt;/p&gt;

</description>
      <category>godot</category>
      <category>unity3d</category>
      <category>gamedev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
