<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Obum - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Obum - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 22 May 2026 17:39:12 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/obumnwabude/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Implement RBAC in a Community Dashboard with Nuxt ]]>
                </title>
                <description>
                    <![CDATA[ Role Based Access Control (RBAC) is a useful authorization model for users with different access levels, such as those in a community dashboard. In this article, you’ll learn how to integrate this type of authorization with Permit.io in Nuxt. Table o... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rbac-community-dashboard-with-nuxt/</link>
                <guid isPermaLink="false">6740bf4cd3bb67c73837c3ef</guid>
                
                    <category>
                        <![CDATA[ authorization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Nuxt.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Fri, 22 Nov 2024 17:28:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732043730534/d88680d7-2590-4541-9120-40a43c3724ef.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Role Based Access Control (RBAC) is a useful authorization model for users with different access levels, such as those in a community dashboard.</p>
<p>In this article, you’ll learn how to integrate this type of authorization with Permit.io in Nuxt.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-table-of-contents">Table of Contents</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-difference-between-authentication-and-authorization">What is the Difference Between Authentication and Authorization?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-role-based-access-control-rbac">What is Role Based Access Control (RBAC)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-the-benefits-of-using-authorization-as-a-service">What are the Benefits of Using Authorization As A Service?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-well-be-building">What We’ll Be Building</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-plan-rbac-with-permitio">How to Plan RBAC with</a> <a target="_blank" href="http://Permit.io">Permit.io</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-setup-permitio-in-nuxt">How to Setup</a> <a target="_blank" href="http://Permit.io">Permit.io</a> <a class="post-section-overview" href="#heading-how-to-setup-permitio-in-nuxt">in Nuxt</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-control-api-access-with-nuxt-middleware">How to Control API Access with Nuxt Middleware</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-rbac-in-the-community-dashboard">How to Test RBAC in the Community Dashboard</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-difference-between-authentication-and-authorization">What is the Difference Between Authentication and Authorization?</h2>
<p>When building applications, we often carry out authentication together with authorization. However, these two concepts are essentially different.</p>
<p>Authentication is verifying who a user is. During the authentication process, the user usually needs to log in with some identifier like email, phone, username, <em>with</em> Google, <em>with</em> Microsoft, and so on.</p>
<p>Authorization specifies the resources an authenticated user can view and what they can do in the application. Authorization tells a user's access rights after that user has been successfully authenticated.</p>
<p>For example, authentication is a user logging in with email and password or verifying their phone number with SMS. On the other hand, authorization is a writer creating and editing posts while only admins can approve and publish those posts.</p>
<p>The primary purpose of authentication is to establish a user's identity before granting them access to the system. The main goal of authorization is to control user actions and protect sensitive data or resources.</p>
<h2 id="heading-what-is-role-based-access-control-rbac">What is Role Based Access Control (RBAC)?</h2>
<p>Role-Based Access Control (RBAC) is an authorization model that you can use to manage and restrict access to system resources. It is based on the responsibilities, duties, or roles of users.</p>
<p>In RBAC, roles represent predefined sets of permissions that tell which actions a user can execute in an application. These roles are then assigned to users based on their job functions or responsibilities.</p>
<p>In common systems, anyone can assign permissions to individual users. In RBAC, we group permissions into roles. In turn, we assign these roles to users. For example, in a community dashboard, users might have roles like “Admin”, “Mentor”, or “Member”.</p>
<p>Aside from Role-Based Access Control, other popular authorization models exist such as Attribute-Based (ABAC) and Relationship-based (ReBAC) access controls. Attribute-Based Access Control uses a wide range of attributes and fits constantly changing systems. You could also combine it with Role-Based Access Control. For more info, see the <a target="_blank" href="https://www.permit.io/blog/rbac-vs-abac">Permit.io article on RBAC vs. ABAC</a>.</p>
<h2 id="heading-what-are-the-benefits-of-using-authorization-as-a-service">What are the Benefits of Using Authorization As A Service?</h2>
<p>You can build authorization models yourself in your application. But it may be time-consuming and cost-ineffective in the long run.</p>
<p>Using an external provider for authorization allows you to focus on the business logic in your applications. The benefits of outsourcing authorization are similar to using a third party like Auth0 or Firebase for Authentication.</p>
<p><a target="_blank" href="https://www.permit.io/blog/authorization-as-a-service">Authorization as a Service</a> provides a solution for managing user access and permissions in applications. When you use such an authorization solution, you enjoy enhanced security, granular access policies’ control, auto-scaling policies, reduced maintenance burden, faster upgrades, robust logging, and so on.</p>
<p>Permit.io is free to use for <a target="_blank" href="https://docs.permit.io/manage-your-account/workspace-usage/#maus--tenants-usage">up to a 1000 Monthly Active Users</a>, and has a UI and API for RBAC, ABAC, and ReBAC.</p>
<p>To Get Started with Permit.io:</p>
<ul>
<li><p>Go to <a target="_blank" href="https://app.permit.io">app.permit.io</a></p>
</li>
<li><p>Create an account</p>
</li>
<li><p>Create a workspace (in your account)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037452876/4604ad72-c5cc-4e07-8f04-70ff4f3dbb8c.gif" alt="Demo of Getting Started with Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<h2 id="heading-what-well-be-building">What We’ll Be Building</h2>
<p>A community dashboard connects members within a community or forum. It is a platform where they can interact and access resources.</p>
<p>For demo purposes of RBAC, we’ll build a simple community dashboard that will include 3 types of content (entities): posts, materials, and announcements<em>.</em></p>
<p>The code we will be using is at <a target="_blank" href="https://github.com/obumnwabude/rbac-community-dashboard">https://github.com/obumnwabude/rbac-community-dashboard</a>. It consists of a Nuxt repo with its server configured for API calls and its front end built with Vue. Clone it with git and explore the code in an editor/IDE.</p>
<p>In the server, we will expose <em>GET, POST,</em> and <em>DELETE</em> endpoints for each entity (posts, materials, and announcements). In Nuxt, you can use the HTTP verb in the file name for the endpoint handler. So we can have <strong>posts.get.ts*</strong>,<em> <strong>posts.delete.ts</strong></em>,<em> <strong>materials.post.ts</strong></em>,* and so on, with each file containing the respective handler for the involved API endpoint.</p>
<p>In addition, the server files store and retrieve entities from JSON files. You should have a robust database setup in your product. For this project, we’ll use local JSON files to build a minimum reproducible example with focus on roles and authorization.</p>
<p>In the front end, we have four pages: three for the entities and a settings page. There is also a simple navigation: a bottom bar on smaller screens and a sidebar on wider ones. Each entity page shows a list of its items and a small form to create new ones.</p>
<p>Furthermore, the demo code uses <a target="_blank" href="https://tailwindcss.com/">tailwindcss</a> to style everything quickly. The settings page contains hardcoded user examples and roles for the demo. When testing, toggle the current user and see the roles in action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732031161755/36a4981c-bc1c-4156-afb8-05fab7d45ee7.gif" alt="Demo of Exploring the Community Dashboard" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>This article focuses on the parts of the code that deal with authorization. For the backend and the UI specifics of the community dashboard, we will only overview them. After that, we will deep dive into RBAC touchpoints.</p>
<h2 id="heading-how-to-plan-rbac-with-permitio">How to Plan RBAC with Permit.io</h2>
<p>Overall, planning authorization means mapping out “who” can carry out an action on “what<em>”</em>. In RBAC, we define roles and then assign them to users. The roles and users combo is the “who” part of the authorization.</p>
<p>The “what” side refers to the entities or resources that your application provides or manages. For this article’s example, we’ve chosen our resources as Posts, Materials, and Announcements.</p>
<p>Actions are user activity. The most common actions are “create<em>”, “</em>read<em>”, “</em>update<em>”,</em> and, “delete<em>”</em>. Per resource in your application, you can use these four actions, add more, or omit some. In this article, our resources will each have all four actions.</p>
<p>When planning authorization, define resources alongside the <em>“</em>actions” users can execute on each resource. After that, define roles. For each role, specify what actions a user holding that role can carry out on each resource. The mapping of resources, actions, and roles allows you to define the authorization policies of your application.</p>
<p>Permit.io makes it easy to edit policies. In Permit.io, you have an intuitive dashboard where you can create resources and their actions, create roles, and merge both with policy tables.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037510499/2c0acabb-e0b4-4002-8b7b-e2fd4dbb4777.gif" alt="Demo of Creating Resources and Actions in Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>For our Community Dashboard example, we will create three roles with incremental access: member, mentor, and admin. For each role, we’ll allow read access to all resources. However, each role has different management access levels to the resources as follows:</p>
<ul>
<li><p><strong>Members</strong> can view all entities but can only create or delete posts.</p>
</li>
<li><p><strong>Mentors</strong> can view all entities and can create or delete posts and materials.</p>
</li>
<li><p><strong>Admins</strong> can create, view, and delete all entities.</p>
</li>
</ul>
<p>Assigning roles to actions in resources is the same as editing policies.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037619254/e8900675-18c0-429c-9840-3ba8c1c38a6c.gif" alt="Demo of Creating Roles and Editing Policies in Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<h2 id="heading-how-to-setup-permitio-in-nuxt">How to Setup Permit.io in Nuxt</h2>
<p>For our demo project, you need to run <code>npm install</code>, create a <code>.env</code> file, and export your Permit token.</p>
<p>However, if you are building a new project, to setup Permit in Nuxt, first install it with npm.</p>
<pre><code class="lang-bash">npm install permitio
</code></pre>
<p>After that, create a <code>.env</code> file and add your PERMIT_TOKEN. Get the token from the dashboard.</p>
<pre><code class="lang-bash">PERMIT_TOKEN=permit_key_XXXXXXXXXXXXXXXXXXXXX
</code></pre>
<p>To make this token available to the Nuxt <code>runtimeConfig</code>, add it to the <code>nuxt.config.ts</code> file. Also, add <code>permitio</code> (alongside other dependencies) in the <code>transpile</code> array of the build property of the Nuxt config file.</p>
<p>This addition is to account for Nuxt’s specific optimizations. Your <code>nuxt.config.ts</code> file should look like the following:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// https://nuxt.com/docs/api/configuration/nuxt-config</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineNuxtConfig({
  <span class="hljs-comment">// ... other properties</span>

  build: {
    transpile: [<span class="hljs-string">'axios'</span>, <span class="hljs-string">'permitio'</span>, <span class="hljs-string">'pino'</span>]
  },
  runtimeConfig: {
    permitToken: process.env.PERMIT_TOKEN
  }
});
</code></pre>
<p>After these, Permit.io should be available to your server code in Nuxt. You can now use in it middleware code to check for permissions.</p>
<h2 id="heading-how-to-control-api-access-with-nuxt-middleware">How to Control API Access with Nuxt Middleware</h2>
<p>In simple terms, middleware is code that runs before a target handler. In Nuxt, you can add middleware for API endpoints by creating necessary files in a <code>middleware</code> directory contained in the top-level <code>server</code> directory.</p>
<p>Since we are dealing with permissions, we will name our middleware file <code>permissions.ts</code>. Here, you’ll check if a user is permitted to take an action on a given resource.</p>
<p>Permit.io makes this easy with a simple <code>.check</code> method that returns a Boolean indicating if the user is permitted.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">await</span> permit.check(user, action, resource);
</code></pre>
<p>For this simple example community dashboard, our middleware code will first try to determine the user and action from the request properties. The example code achieves that in crude ways. They are enough to explain the concept and you should use more robust industry-standard methods for this.</p>
<p>After that, in the example code below, we construct the Permit object using our permit token and the default public PDP (Policy-Decision-Point microservice)’s endpoint. The <a target="_blank" href="https://github.com/permitio/PDP">Permit PDP is open-source.</a> If you wish, you can set up your local/personal PDP by following the steps <a target="_blank" href="https://docs.permit.io/how-to/deploy/deploy-to-production">here</a>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Permit } <span class="hljs-keyword">from</span> <span class="hljs-string">'permitio'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineEventHandler(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-comment">// Only check permissions if the request is a POST or DELETE request</span>
  <span class="hljs-keyword">const</span> { method, path } = event;
  <span class="hljs-keyword">if</span> (method !== <span class="hljs-string">'POST'</span> &amp;&amp; method !== <span class="hljs-string">'DELETE'</span>) <span class="hljs-keyword">return</span>;

  <span class="hljs-comment">// Ensure authorization header is present</span>
  <span class="hljs-keyword">let</span> authorization = event.node.req.headers[<span class="hljs-string">'authorization'</span>];
  <span class="hljs-keyword">if</span> (!authorization) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unauthorized'</span>);

  <span class="hljs-comment">// Extract the user from the authorization header. This is for example</span>
  <span class="hljs-comment">// purposes only. In a real application, you would use a JWT library or</span>
  <span class="hljs-comment">// better authentication methods in your API.</span>
  <span class="hljs-keyword">const</span> user = authorization.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">1</span>];
  <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unauthorized'</span>);

  <span class="hljs-comment">// Extract the resource from the path. This is for example purposes only.</span>
  <span class="hljs-keyword">let</span> resource = path.split(<span class="hljs-string">'/'</span>).reverse()[<span class="hljs-number">0</span>]; <span class="hljs-comment">// get the last part of the path</span>
  resource = resource.slice(<span class="hljs-number">0</span>, <span class="hljs-number">-1</span>); <span class="hljs-comment">// remove the trailing 's' </span>
  <span class="hljs-comment">// Capitalize the first letter</span>
  resource = resource.charAt(<span class="hljs-number">0</span>).toUpperCase() + resource.slice(<span class="hljs-number">1</span>);

  <span class="hljs-comment">// Set the action on the resource from the request method. </span>
  <span class="hljs-comment">// This is for example purposes only. In a real application, you would</span>
  <span class="hljs-comment">// have a more robust way to determine the action. </span>
  <span class="hljs-keyword">const</span> action = method === <span class="hljs-string">'POST'</span> ? <span class="hljs-string">'create'</span> : <span class="hljs-string">'delete'</span>;

  <span class="hljs-comment">// Construct the Permit object. Use the token from runtime config.</span>
  <span class="hljs-keyword">const</span> config = useRuntimeConfig(event);
  <span class="hljs-keyword">const</span> permit = <span class="hljs-keyword">new</span> Permit({
    pdp: <span class="hljs-string">'https://cloudpdp.api.permit.io'</span>,
    token: config.permitToken
  });

  <span class="hljs-comment">// Check if the user is permitted to create the resource. </span>
  <span class="hljs-comment">// If not, throw an error.</span>
  <span class="hljs-keyword">const</span> isPermitted = <span class="hljs-keyword">await</span> permit.check(user, action, resource);
  <span class="hljs-keyword">if</span> (!isPermitted) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Unauthorized'</span>);
});
</code></pre>
<p>As you can see, if the Permit checker fails, an error will be thrown. This will make Nuxt to prevent unauthorized resource management in your system. Such separation of concerns is efficient, especially in Authorization.</p>
<h2 id="heading-how-to-test-rbac-in-the-community-dashboard">How to Test RBAC in the Community Dashboard</h2>
<p>The settings page works together with the <code>stores/permissions.ts</code> file to complete the flow in the front end. We hardcoded the roles and the “user IDs” to ease toggling and testing. You definitely won’t have this in a production application. You can <a target="_blank" href="https://docs.permit.io/integrations/feature-flagging/casl/">integrate CASL for permission checks in a frontend</a>.</p>
<p>In this demo community dashboard, the UI touchpoints only allow edits of entities for which the acting user has the right roles. In other words, you can only add or delete an entity if the current role in the settings page allows that. Let’s see this in action.</p>
<p>In the Permit.io dashboard, create three test users: “example-member”, “example-mentor”, and “example-admin”. Assign the respective roles to each user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732037709104/0cb7b666-8597-4839-802b-e79e0434572a.gif" alt="Demo of Creating Test Users and assigning Roles in Permit.io" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>Start up the Nuxt app by running <code>npm run dev</code>. Visit <code>localhost:3000</code> in your browser and explore the role-based authorization in the demo community dashboard.</p>
<p>You can see that when you set the Current User to admin, you can create and delete announcements, but when set to guest, you can only view entities and not manage them. With this, we’ve fully implemented authorization.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732031066634/54310991-3ce9-4736-8735-c2776e4f6d82.gif" alt="Demo of Testing Roles in the Community Dashboard" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<h2 id="heading-summary">Summary</h2>
<p>You can be more efficient in building your application by focusing on business logic and outsourcing crucial parts like authorization.</p>
<p>In this article, you learned how you can use an authorization solution (Permit.io) to implement Role-Based Access Control in a demo community dashboard with Nuxt. You can also use Permit in any other kind of application (not just community dashboards).</p>
<p>When planning authorization, define resources alongside the <em>“</em>actions” users can execute on each resource. After that, define roles as the permissions the users can have.</p>
<p>Cheers!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Streams and Services for Flutter State ]]>
                </title>
                <description>
                    <![CDATA[ Among the many state management architectures in Flutter, combining Dart streams with singleton classes (services) is an unpopular yet easy architecture. In this article, we’ll explore how to achieve this combination for app-wide state in Flutter. Ta... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/flutter-streams-and-services/</link>
                <guid isPermaLink="false">66f3dcb00eb033fac26d3822</guid>
                
                    <category>
                        <![CDATA[ Dart ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ State Management  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Wed, 25 Sep 2024 09:49:36 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727130776096/a52147fe-e05a-45e7-af73-9f7a9a8510b5.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Among the many state management architectures in Flutter, combining Dart streams with singleton classes (services) is an unpopular yet easy architecture.</p>
<p>In this article, we’ll explore how to achieve this combination for app-wide state in Flutter.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-app-wide-state-in-flutter">What is App-wide State in Flutter?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-stream-in-dart">What is a Stream in Dart?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-stream-in-dart">How to Create a Stream in Dart</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-singleton-class-instances-or-services">How to Create Singleton Class Instances (or Services)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-manipulate-state-streams-within-services">How to Manipulate State (streams) Within Services</a></p>
</li>
<li><p><a target="_blank" href="https://untitled+.vscode-resource.vscode-cdn.net/Untitled-1#heading-how-to-manipulate-state-streams-within-services">How to Use Dart Streams in Flutter Widgets</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-a-service-depend-on-another">How to Make a Service Depend on Another</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-streams-with-rxdart-classes-and-extensions">How to Improve Streams with rxdart Classes and Extensions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-update-state-in-applifecycle-callbacks">How to Update State in AppLifecycle Callbacks</a></p>
</li>
<li><p><a target="_blank" href="https://untitled+.vscode-resource.vscode-cdn.net/Untitled-1#heading-how-to-improve-streams-with-rxdart-classes-and-extensions">Flexibi</a><a class="post-section-overview" href="#heading-flexibility-in-state-management">l</a><a target="_blank" href="https://untitled+.vscode-resource.vscode-cdn.net/Untitled-1#heading-how-to-improve-streams-with-rxdart-classes-and-extensions">ity in</a> <a class="post-section-overview" href="#heading-flexibility-in-state-management">State Management</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-what-is-app-wide-state-in-flutter">What is App-wide State in Flutter?</h2>
<p>App-wide state comprises all variables that are relevant to multiple widgets at the same time. By app-wide state, we don't mean the state that is attached to <code>StatefulWidgets</code>. Those are ephemeral state. Updating them requires local or scoped calls to <a target="_blank" href="https://api.flutter.dev/flutter/widgets/State/setState.html">setState</a>.</p>
<p>In Flutter, app-wide state usually has a separate logical management from UI code. This separated logic is called a state management architecture. We have many state management architectures with which we can engineer app-wide state. Examples include <a target="_blank" href="https://github.com/obumnwabude/write/blob/main/2024/flutter/get-the-link">Provider</a>, <a target="_blank" href="https://github.com/obumnwabude/write/blob/main/2024/flutter/get-the-link">InheritedWidget</a>, <a target="_blank" href="https://github.com/obumnwabude/write/blob/main/2024/flutter/get-the-link">Riverpod</a>, <a target="_blank" href="https://github.com/obumnwabude/write/blob/main/2024/flutter/get-the-link">Bloc</a>, <a target="_blank" href="https://github.com/obumnwabude/write/blob/main/2024/flutter/get-the-link">Redux</a>, <a target="_blank" href="https://github.com/obumnwabude/write/blob/main/2024/flutter/get-the-link">Stacked</a>, and so on. Each of these state management architectures are efficient, good, and opinionated.</p>
<p>While your choice of architecture could vary based on different factors, consider adopting the following architecture in your projects. It involves using Dart streams and services (singleton classes) for keeping track of your app's state.</p>
<h2 id="heading-what-is-a-stream-in-dart">What is a Stream in Dart?</h2>
<p>A <a target="_blank" href="https://dart.dev/libraries/dart-async#stream">stream</a> continuously emits values. You can listen to a stream and constantly get new values when they are emitted. Streams in Dart are the equivalent of <a target="_blank" href="https://rxjs.dev/guide/observable"><code>Observable</code></a> in JavaScript.</p>
<p>In Dart, streams are different from <a target="_blank" href="https://dart.dev/libraries/dart-async#future">futures</a>. The difference is that while a future resolves to one value, a stream will continuously emit various values during its life.</p>
<p>Let's say we have a <code>counter</code> stream that keeps track of some current integer count. This count could be incremented or decremented. To use the values emitted by this <code>counter</code> stream, you listen to the <code>counter</code>. Listening implies calling the <code>.listen</code> method on the stream and handling the emitted value.</p>
<pre><code class="lang-dart">counter.listen((<span class="hljs-built_in">int</span> value) =&gt; <span class="hljs-built_in">print</span>(<span class="hljs-string">'Got <span class="hljs-subst">$value</span>.'</span>));
</code></pre>
<h2 id="heading-how-to-create-a-stream-in-dart">How to Create a Stream in Dart</h2>
<p>The <a target="_blank" href="https://dart.dev/libraries/dart-async#stream"><code>Stream</code></a> class comes with multiple factory constructors. They allow you to create various streams for various purposes. They include:</p>
<ul>
<li><p><code>Stream.empty</code></p>
</li>
<li><p><code>Stream.value</code></p>
</li>
<li><p><code>Stream.error</code></p>
</li>
<li><p><code>Stream.fromFuture</code></p>
</li>
<li><p><code>Stream.fromFutures</code></p>
</li>
<li><p><code>Stream.fromIterable</code></p>
</li>
<li><p><code>Stream.multi</code></p>
</li>
<li><p><code>Stream.periodic</code></p>
</li>
<li><p><code>Stream.eventTransformed</code></p>
</li>
</ul>
<p>Each constructor serves a specific purpose as its name suggests.</p>
<p>Another technique of creating a <code>Stream</code> is by obtaining it from a <code>StreamController</code>. You will have to create the <code>StreamController</code> yourself. The advantage of doing this is that the controller allows you to <em>add</em> values to it. When you add values to the controller, they get emitted to listeners of its stream.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-keyword">final</span> counterCtrl = StreamController&lt;<span class="hljs-built_in">int</span>&gt;();
  counterCtrl.stream.listen(<span class="hljs-built_in">print</span>);
  counterCtrl.add(<span class="hljs-number">1</span>); <span class="hljs-comment">// prints 1</span>
}
</code></pre>
<p>The problem with the default <code>StreamController</code> from the <code>dart:async</code> library is that it allows only one listener. It is unicast. If you attempt attaching another listener to this stream obtained from <code>StreamController</code>, it will throw a "bad state" error.</p>
<p>This issue is solved by the <code>BehaviorSubject</code> class from the <a target="_blank" href="https://pub.dev/packages/rxdart"><code>rxdart</code></a> package. Technically, <code>BehaviorSubject</code> is a <code>StreamController</code>. The difference is that it has more features like:</p>
<ol>
<li><p>Allows multiple listeners (very important).</p>
</li>
<li><p>Caches the latest emitted value or error.</p>
</li>
<li><p>Emits the latest cached value/error to a new listener once it newly subscribes.</p>
</li>
<li><p>Allows you to synchronously read the current (or last emitted) value from it.</p>
</li>
<li><p>Allows you to add values to it if it doesn't yet have any listener (the default <code>StreamController</code> doesn’t allow this).</p>
</li>
</ol>
<p>The <code>rxdart</code> package extends the capabilities of Dart streams. For example, it provides you with <code>BehaviorSubject</code>. Also, it exposes classes and extensions that allow more stream manipulations. To use the <code>rxdart</code> package, add it to your project's dependencies from pub using the following command:</p>
<pre><code class="lang-bash">flutter pub add rxdart
</code></pre>
<p>Then import it in your project's Dart files. From there, you can create <code>BehaviorSubject</code> (more robust <code>StreamController</code>) that can allow multiple listeners while allowing you to control them (adding values to the streams).</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:rxdart/rxdart.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-comment">// Create a BehaviorSubject.</span>
  <span class="hljs-comment">//</span>
  <span class="hljs-comment">// Asides from creating the BehaviorSubject, we can also  </span>
  <span class="hljs-comment">// immediately add a value to it using Dart's cascade operator.</span>
  <span class="hljs-keyword">final</span> counterBS = BehaviorSubject&lt;<span class="hljs-built_in">int</span>&gt;()..add(<span class="hljs-number">0</span>);

  counterBS.stream.listen(<span class="hljs-built_in">print</span>); <span class="hljs-comment">// prints 0</span>
  counterBS.stream.listen(<span class="hljs-built_in">print</span>); <span class="hljs-comment">// prints 0</span>
  counterBS.add(<span class="hljs-number">1</span>); <span class="hljs-comment">// prints 1 twice</span>
}
</code></pre>
<p>Now that we can create streams (and listen to them), we need the exact same streams to be available to every part of our Flutter apps.</p>
<p>To ensure that it is the same instance of streams that different parts of our Flutter apps are accessing, we can expose the streams from singleton class instances that we create in the project.</p>
<h2 id="heading-how-to-create-singleton-class-instances-or-services">How to Create Singleton Class Instances (or Services)</h2>
<p>When something is called a singleton, it means only one of it exists. For example, we can say the sun is a singleton star because we have only one sun.</p>
<p>When it comes to programming, we use a singleton when we need the same copy of an object everywhere. Already, the <a target="_blank" href="https://en.m.wikipedia.org/wiki/Static_variable"><code>static</code></a> properties of a class are singletons to every instance of that class. When you declare a field or method as <code>static</code>, you're telling the runtime engine to always reuse the same static item.</p>
<p>This explains why <code>static</code> properties are used as constants. It's another reason why we use them without instantiating an object. Furthermore, in Flutter, we conventionally use static properties as a means to obtain new or existing instances of a class. For example, many Flutter classes (<code>MediaQuery</code>, <code>Navigator</code>, <code>ThemeData</code>, and so on) have a static <code>.of</code> method for obtaining their instances.</p>
<p>In this streams and services architecture, we expose only one instance from a class with the <code>static</code> keyword. At the same time, we hide that class constructor. Hiding the constructor ensures that no other Dart code outside the Dart file can create another instance of the same class. Doing this maintains the instance as a singleton.</p>
<p>Following common conventions, we can call this class a service. Any other Dart file in the project can listen to the exposed stream(s) from the service class and always get updated values emitted to it.</p>
<p>Services here are holders of app-wide state. Each service is a logical container of related features. In any other part of the code, through these services, we can access app-wide state variables (in our case, streams). In a production application, we could have an authentication service, another for notifications, another for files, and so on.</p>
<p>To have an app-wide available service (singleton class) with a stream in it:</p>
<ol>
<li><p>Create a service class.</p>
</li>
<li><p>Create a private constructor (so that no other Dart code outside the class can instantiate it).</p>
</li>
<li><p>Create a static private instance of that very class.</p>
</li>
<li><p>Expose this private instance as the singleton.</p>
</li>
<li><p>Create a private <code>BehaviorSubject</code> in that class.</p>
</li>
<li><p>Expose the <code>BehaviorSubject</code> stream as a public static getter from the class.</p>
</li>
</ol>
<pre><code class="lang-dart"><span class="hljs-comment">/* In counter_service.dart file */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:rxdart/rxdart.dart'</span>;

<span class="hljs-comment">// 1. Create a class</span>
<span class="hljs-comment">// </span>
<span class="hljs-comment">// The class name with "Service" appended to it indicates </span>
<span class="hljs-comment">// that it is an app-wide state object.</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterService</span> </span>{
  <span class="hljs-comment">// 2. Create a private constructor.</span>
  <span class="hljs-comment">//</span>
  <span class="hljs-comment">// This "just-underscore" constructor works. If we want, we could  </span>
  <span class="hljs-comment">// still add a name after the underscore. The main thing is that </span>
  <span class="hljs-comment">// underscore makes the constructor to be a private one.</span>
  CounterService._();

  <span class="hljs-comment">// 3. Create a static private instance.</span>
  <span class="hljs-comment">// </span>
  <span class="hljs-comment">// Prefixing underscore (_) to the variable name makes it private.</span>
  <span class="hljs-comment">// By being private, no other Dart code outside this file can directly </span>
  <span class="hljs-comment">// access it.</span>
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> _instance = CounterService._();

  <span class="hljs-comment">// 4. Expose this private instance as the singleton.</span>
  <span class="hljs-keyword">static</span> CounterService <span class="hljs-keyword">get</span> instance =&gt; _instance;

  <span class="hljs-comment">// 5. Create a private BehaviorSubject.</span>
  <span class="hljs-keyword">final</span> _counterBS = BehaviorSubject&lt;<span class="hljs-built_in">int</span>&gt;()..add(<span class="hljs-number">0</span>);

  <span class="hljs-comment">// 6. Expose the BehaviorSubject's Stream.</span>
  Stream&lt;<span class="hljs-built_in">int</span>&gt; <span class="hljs-keyword">get</span> countStream =&gt; _counterBS.stream;

  <span class="hljs-comment">// Also, if need be, expose the BehaviorSubject's current as a getter.</span>
  <span class="hljs-built_in">int</span> <span class="hljs-keyword">get</span> currentCount =&gt; _counterBS.value;
}

<span class="hljs-comment">/* In any other Dart file in the project */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'counter_service.dart'</span>

<span class="hljs-comment">// Attach a listener to the stream</span>
CounterService.instance.countStream.listen((count) {
   <span class="hljs-comment">// Use the count as use wish. Code you write within this </span>
   <span class="hljs-comment">// listener's block will be called whenever count is </span>
   <span class="hljs-comment">// update/re-emitted.</span>

   <span class="hljs-built_in">print</span>(count); <span class="hljs-comment">// prints 0</span>
});

<span class="hljs-comment">// Read the current stream value just once without subscribing</span>
<span class="hljs-built_in">print</span>(CounterService.instance.currentCount); <span class="hljs-comment">// prints 0</span>
</code></pre>
<h2 id="heading-how-to-manipulate-state-streams-within-services">How to Manipulate State (Streams) Within Services</h2>
<p>Most times, each service will have multiple streams. This is as expected, given that, for a given logical state feature, there would be multiple variables affecting it. Therefore, where need be, don't hesitate to declare multiple <code>BehaviorSubject</code> (while exposing their streams) within the same service class.</p>
<p>For each stream, you want to control its data. That's why we are using <code>BehaviorSubject</code>, so that we can add values to it when there is a need to update state.</p>
<p>Different events (whether from the user or your servers) can be the cause of such state updates. You want to trigger state updates (or add values to streams) anytime those events occur.</p>
<p>You could always poll your backend and emit changes to your streams if any event happens. You could also emit values based on changes in other services. In addition, if need be, services should also expose relevant methods that will update their streams. In turn, other parts of the app can call these methods and trigger changes. The obvious advantage is that every listener will respectively get the new stream value emitted to them.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/* In counter_service.dart file */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:rxdart/rxdart.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterService</span> </span>{
  CounterService._();
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> _instance = CounterService._();
  <span class="hljs-keyword">static</span> CounterService <span class="hljs-keyword">get</span> instance =&gt; _instance;

  <span class="hljs-keyword">final</span> _counterBS = BehaviorSubject&lt;<span class="hljs-built_in">int</span>&gt;()..add(<span class="hljs-number">0</span>);
  Stream&lt;<span class="hljs-built_in">int</span>&gt; <span class="hljs-keyword">get</span> countStream =&gt; _counterBS.stream;
  <span class="hljs-built_in">int</span> <span class="hljs-keyword">get</span> currentCount =&gt; _counterBS.value;

  <span class="hljs-comment">// Incrementing/Decrementing the counter will trigger state updates.</span>
  <span class="hljs-keyword">void</span> incrementCount() =&gt; _counterBS.add(currentCount + <span class="hljs-number">1</span>);
  <span class="hljs-keyword">void</span> decrementCount() =&gt; _counterBS.add(currentCount - <span class="hljs-number">1</span>);
}

<span class="hljs-comment">/* In another Dart file in the project */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'counter_service.dart'</span>

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-keyword">final</span> service = CounterService.instance;
  service.countStream.listen(<span class="hljs-built_in">print</span>); <span class="hljs-comment">// prints 0</span>
  service.incrementCount(); <span class="hljs-comment">// causes 1 to be printed</span>
  service.decrementCount(); <span class="hljs-comment">// causes 0 to be printed</span>
}
</code></pre>
<p>For a more concrete example, let's say we have an <code>AuthenticationService</code>. It declares some <code>_userBS</code> and exposes a <code>currentUser</code> stream with type <code>Stream&lt;User?&gt;</code>, the user will be valid if authenticated or <code>null</code> if signed out. This auth service will naturally have <code>signIn</code> and <code>signOut</code> which can both add values to <code>_userBS</code>. The sign-up and login screens can each call <code>signIn</code> whereas the “switch account” and “log out” buttons can each call <code>signOut</code>.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/* In user.dart */</span>
<span class="hljs-comment">// A simple user with only email and username for demo purposes. </span>
<span class="hljs-comment">// Your User model/schema would have more properties.</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> email;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> username;

  <span class="hljs-keyword">const</span> User(<span class="hljs-keyword">this</span>.email, <span class="hljs-keyword">this</span>.username);
}

<span class="hljs-comment">/* In authentication_service.dart */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:rxdart/rxdart.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'user.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthenticationService</span> </span>{
   AuthenticationService._();
   <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> _instance = AuthenticationService._();
   <span class="hljs-keyword">static</span> AuthenticationService instance =&gt; _instance;

   <span class="hljs-comment">// User BehaviorSubject and its stream.</span>
   <span class="hljs-keyword">final</span> _userBS = BehaviorSubject&lt;User?&gt;()..add(<span class="hljs-keyword">null</span>);
   Stream&lt;User?&gt; <span class="hljs-keyword">get</span> currentUser =&gt; _userBS.stream;

   <span class="hljs-comment">// signIn adds a new User to the stream.</span>
   <span class="hljs-keyword">void</span> signIn(<span class="hljs-built_in">String</span> email, <span class="hljs-built_in">String</span> username}) {
     _userBS.add(User(email, username));
   }

   <span class="hljs-comment">// signOut sets the currentUser as null</span>
   <span class="hljs-keyword">void</span> signOut() =&gt; _userBS.add(<span class="hljs-keyword">null</span>);

   <span class="hljs-comment">// signIn and signOut methods that tamper the state could do other </span>
   <span class="hljs-comment">// actions like recording analytics or carrying out navigation.</span>
   <span class="hljs-comment">// Also, they could do some validation or run some checks before</span>
   <span class="hljs-comment">// emitting values. The idea is that you get comfortable with</span>
   <span class="hljs-comment">// updating the values of BehaviorSubject (hence emitting streams) </span>
   <span class="hljs-comment">// from controlled methods within the service.</span>
}
</code></pre>
<p>Another state manipulation point is at initializing services. Some streams may warrant an asynchronous initializer before they should be used. You can define <code>init</code> methods in the services, and call the methods before calling <a target="_blank" href="https://api.flutter.dev/flutter/widgets/runApp.html"><code>runApp</code></a> in the topmost main method in Flutter.</p>
<p><code>init</code> methods may be "localStorage"-saved values from previous app runs. They can make API calls, check permissions, or set up <a target="_blank" href="https://api.flutter.dev/flutter/services/EventChannel-class.html">EventChannel</a> listeners. When you call them before <code>runApp</code>, be sure to call <code>ensureInitialized()</code> from <a target="_blank" href="https://api.flutter.dev/flutter/widgets/WidgetsFlutterBinding-class.html"><code>WidgetsFlutterBinding</code></a> before initializing the services. This is especially mandatory if any of the service <code>init</code> code will access a <a target="_blank" href="https://docs.flutter.dev/platform-integration/platform-channels"><code>PlatformChannel</code></a>.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/* authentication_service.dart */</span>
<span class="hljs-comment">// ... imports</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthenticationService</span> </span>{
  <span class="hljs-comment">// ... other code</span>

  <span class="hljs-comment">// initialize the service and carry-out other setups if need be.</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; init() <span class="hljs-keyword">async</span> =&gt; _userBS.add(<span class="hljs-keyword">await</span> _fetchSavedUser());
}

<span class="hljs-comment">/* main.dart */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'authentication_service.dart'</span>;

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

  <span class="hljs-comment">// Initialize the service to be sure it is up and running before</span>
  <span class="hljs-comment">// launching the app. You could also initialize other services here.</span>
  <span class="hljs-comment">// Only do this if they are carrying out asynchronous executions,</span>
  <span class="hljs-comment">// and the results need to be ready before the UI launches.</span>
  <span class="hljs-keyword">await</span> AuthenticationService.instance.init();

  runApp(<span class="hljs-keyword">const</span> MyApp());
}
</code></pre>
<h2 id="heading-how-to-use-dart-streams-in-flutter-widgets">How to Use Dart Streams in Flutter Widgets</h2>
<p>Flutter comes with a built-in <a target="_blank" href="https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html">StreamBuilder</a> widget. It takes a stream and a builder function. This builder function will get a <code>BuildContext</code> and snapshot data about the stream. The function should always return a widget.</p>
<p>When building UIs, you can wrap UI parts that depend on or display values emitted from app-wide streams in <code>StreamBuilders</code>. That way, once the stream emits a value, Flutter auto-rebuilds the children widget of the <code>StreamBuilders</code> with the latest values.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'counter_service.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> StreamBuilder&lt;<span class="hljs-built_in">int</span>&gt;(
      stream: CounterService.instance.countStream, <span class="hljs-comment">// The stream to listen to</span>
      initialData: CounterService.instance.currentCount, <span class="hljs-comment">// Initial value</span>
      builder: (context, snapshot) {
        <span class="hljs-comment">// Check if the snapshot has data</span>
        <span class="hljs-keyword">if</span> (snapshot.hasData) {
          <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Counter: <span class="hljs-subst">${snapshot.data}</span>'</span>, style: TextStyle(fontSize: <span class="hljs-number">24</span>));
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-comment">// Handle any error or empty state</span>
          <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Loading...'</span>, style: TextStyle(fontSize: <span class="hljs-number">24</span>));
        }
      },
    );
  }
}
</code></pre>
<p><code>StreamBuilders</code> are great tools. However, there are times when it is not suitable to use them. For example:</p>
<ul>
<li><p>When a given UI screen depends on multiple streams that are exposed by the same or different services.</p>
</li>
<li><p>When you want to do some computation on the stream values before rendering them in the UI.</p>
</li>
</ul>
<p>In those cases, we need to listen to the streams separately in <code>initState</code>, set values through <code>setState</code> calls (to update the UI), and dispose of the <code>StreamSubscriptions</code> in the StatefulWidget's <code>dispose</code> method.</p>
<p>Listening to the streams separately allows us to perform any customizations or to merge data when the streams emit values. In addition, we make our UI code easier to read given that we’ve taken out logic-related code from the build method. However, we should do this only when necessary: <code>StreamBuilders</code> will, most of the time, be sufficient.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'counter_service.dart'</span>;

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

  <span class="hljs-meta">@override</span>
  _CounterStatefulWidgetState createState() =&gt; _CounterStatefulWidgetState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_CounterStatefulWidgetState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">CounterStatefulWidget</span>&gt; </span>{
  <span class="hljs-keyword">late</span> StreamSubscription&lt;<span class="hljs-built_in">int</span>&gt; counterSub;
  <span class="hljs-built_in">int</span> count = CounterService.instance.currentCount;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();

    <span class="hljs-comment">// Initialize the stream subscription</span>
    counterSub = CounterService.instance.countStream.listen((count) {
      <span class="hljs-comment">// Update state on new stream value</span>
      setState(() =&gt; <span class="hljs-keyword">this</span>.count = count);
    });
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    <span class="hljs-comment">// Dispose of the stream subscription to avoid memory leaks</span>
    counterSub.cancel();
    <span class="hljs-keyword">super</span>.dispose();
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'Counter: <span class="hljs-subst">$count</span>'</span>, style: TextStyle(fontSize: <span class="hljs-number">24</span>));
  }
}
</code></pre>
<p>The example above demonstrates listening and disposing from outside the build method. The example is not a good use case of when you should do that.</p>
<h2 id="heading-how-to-make-a-service-depend-on-another">How to Make a Service Depend on Another</h2>
<p>In complex applications, it's common to have services that depend on each other. The dependent service can listen to streams and call methods of the independent service. Also, the dependent service can import and reference the independent service just as we’ve been doing in UI code above.</p>
<p>For instance, if we are building an e-commerce app, a <code>CartService</code> may depend on an <code>AuthenticationService</code> to fetch carts and orders for the signed-in user. If the user signs out, some <code>currentUser</code> stream in the <code>AuthenticationService</code> will emit <code>null</code>. In turn, the listening <code>CartService</code> will update the cart. When next a new user signs in, it will fetch the new cart.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:rxdart/rxdart.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'authentication_service.dart'</span>;

<span class="hljs-comment">// Item model representing a cart item.</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CartItem</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> name;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> quantity;

  <span class="hljs-keyword">const</span> CartItem(<span class="hljs-keyword">this</span>.name, <span class="hljs-keyword">this</span>.quantity);
}

<span class="hljs-comment">// CartService to manage the user's shopping cart.</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CartService</span> </span>{
  <span class="hljs-comment">// ...</span>

  <span class="hljs-comment">// Dependency on AuthenticationService.</span>
  <span class="hljs-keyword">final</span> _auth = AuthenticationService.instance;

  <span class="hljs-keyword">final</span> _cartItemsBS = BehaviorSubject&lt;<span class="hljs-built_in">List</span>&lt;CartItem&gt;&gt;();
  Stream&lt;<span class="hljs-built_in">List</span>&lt;CartItem&gt;&gt; <span class="hljs-keyword">get</span> cartStream =&gt; _cartItemsBS.stream;

  CartService() {
    <span class="hljs-comment">// Listen to the currentUser stream in AuthenticationService.</span>
    _auth.currentUserStream.listen((user) {
      <span class="hljs-keyword">if</span> (user == <span class="hljs-keyword">null</span>) {
        <span class="hljs-comment">// User signed out, clear the cart.</span>
        _clearCart();
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// User signed in, fetch their cart.</span>
        _fetchCartForUser(user.email);
      }
    });
  }

  <span class="hljs-comment">// Method to clear the cart (called on sign-out).</span>
  <span class="hljs-keyword">void</span> _clearCart() {
    _cartItemsBS.add([]);  <span class="hljs-comment">// Emit an empty list to clear the cart.</span>
  }

  <span class="hljs-comment">// Method to fetch the cart for a signed-in user (simulated).</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; _fetchCartForUser(<span class="hljs-built_in">String</span> email) <span class="hljs-keyword">async</span> {
    <span class="hljs-comment">// ...</span>
  }
}
</code></pre>
<p>Watch out for <a target="_blank" href="https://en.wikipedia.org/wiki/Circular_dependency">circular dependency</a> problems when your services depend on each other. Circular dependency occurs when two services inter-depend on themselves. This scenario is usually inevitable as business logic grows.</p>
<p>When faced with it, lift the state they want to co-share to a different service and import this new service into the others. Another solution is to use Dart’s <code>late</code> keywords when importing the interdependent services. You can also find ways to ensure that variable accessing is within functions and not at some top-level declaration.</p>
<h2 id="heading-how-to-improve-streams-with-rxdart-classes-and-extensions">How to Improve Streams with rxdart Classes and Extensions</h2>
<p>Asides from having service methods that update streams, you can also have new or improved streams based on existing ones, by using <code>rxdart</code> classes and extensions.</p>
<p>An example class is <a target="_blank" href="https://pub.dev/documentation/rxdart/latest/rx/CombineLatestStream-class.html"><code>CombineLatestStream</code></a>. It takes multiple streams and a combiner function to return a new stream that will re-emit the combined latest values of the source streams (depending on the optional combiner).</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:rxdart/rxdart.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MultipliedCounterService</span> </span>{
  <span class="hljs-comment">// ... </span>

  <span class="hljs-keyword">final</span> _counterBS = BehaviorSubject&lt;<span class="hljs-built_in">int</span>&gt;()..add(<span class="hljs-number">0</span>);
  <span class="hljs-keyword">final</span> _multiplierBS = BehaviorSubject&lt;<span class="hljs-built_in">int</span>&gt;()..add(<span class="hljs-number">2</span>);

  Stream&lt;<span class="hljs-built_in">int</span>&gt; <span class="hljs-keyword">get</span> combinedStream =&gt; CombineLatestStream(
        [_counterBS.stream, _multiplierBS.stream],
        (values) =&gt; values[<span class="hljs-number">0</span>] * values[<span class="hljs-number">1</span>],
      );

  <span class="hljs-keyword">void</span> incrementCounter() =&gt; _counterBS.add(_counterBS.value + <span class="hljs-number">1</span>);
  <span class="hljs-keyword">void</span> changeMultiplier(<span class="hljs-built_in">int</span> mul) =&gt; _multiplierBS.add(mul);
}
</code></pre>
<p>Another good stream method is <a target="_blank" href="https://pub.dev/documentation/rxdart/latest/rx/DebounceExtensions/debounceTime.html"><code>debounceTime</code></a>. This is a stream extension that is useful for ignoring frequent emissions and processing the latest value after a delay (like when searching). An emission will only occur after the set duration and when there is no other emission in between that time. It helps avoid excessive API calls by waiting for a period of inactivity before emitting the latest value.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:rxdart/rxdart.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SearchService</span> </span>{
  <span class="hljs-comment">// ... </span>

  <span class="hljs-keyword">final</span> _searchQueryBS = BehaviorSubject&lt;<span class="hljs-built_in">String</span>&gt;()..add(<span class="hljs-string">''</span>);

  <span class="hljs-comment">// Stream with debouncing to emit values only after a</span>
  <span class="hljs-comment">// 300ms delay. For example: keystrokes will be bundled at once.</span>
  Stream&lt;<span class="hljs-built_in">String</span>&gt; <span class="hljs-keyword">get</span> debouncedSearchQueryStream =&gt;
      _searchQueryBS.stream.debounceTime(<span class="hljs-built_in">Duration</span>(milliseconds: <span class="hljs-number">300</span>));

  <span class="hljs-keyword">void</span> updateSearchQuery(<span class="hljs-built_in">String</span> query) =&gt; _searchQueryBS.add(query);
}
</code></pre>
<p>The <code>rxdart</code> package provides more classes and stream extensions that will be useful to you, even if you don’t use this architecture. Check them out later on.</p>
<h2 id="heading-how-to-update-state-in-applifecycle-callbacks">How to Update State in AppLifecycle Callbacks</h2>
<p>When a user minimizes or leaves your application and comes back, some external things you rely on for data may have changed.</p>
<p>For example, when you prompt a user to grant any permissions, the operating system displays a popup over your application. Programmatically, the displayed popup caused your app to lose focus or go into background mode. When the popup is gone, your app resumes focus and you need to detect whether you got the permissions.</p>
<p>Equally, if you are managing the contents of a specific File Explorer Directory within your application (like converted music, encrypted docs, call logs, and so on), when your app goes in background, there could be changes to that directory from the user, which are worth detecting when the user comes back.</p>
<p>Sometimes, you may want to know when the user comes back to your application for authentication purposes, like terminating a session if they stayed away for too long and they need to re-authenticate. Other times, you may want to refresh app contents, to retain the user, as you can do if building a social media app.</p>
<p>In all these cases, we need a way to programmatically know when our app comes back to the user's focus after the user had left. Luckily, Flutter provides us with <a target="_blank" href="https://api.flutter.dev/flutter/dart-ui/AppLifecycleState.html"><code>AppLifecycleState</code></a> and a way to react to changes to them.</p>
<p>An app’s lifecycle refers to its various states while it is running. In Flutter, <code>AppLifecycleState</code> includes detached, resumed, inactive, hidden, and paused. In the above example cases, anytime the user comes back to the app, the app’s lifecycle state becomes <code>AppLifecycleState.resumed</code>.</p>
<p>We can react to lifecycle changes and call our service methods when a particular state occurs. To listen to lifecycle changes, your service class should add the <code>WidgetsBindingObserver</code> mixin to its declaration. Then you should override <code>didChangeAppLifecycleState</code> with a callback. This callback should handle states it is interested in.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PermissionService</span> <span class="hljs-title">with</span> <span class="hljs-title">WidgetsBindingObserver</span> </span>{
  <span class="hljs-comment">// ...</span>

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

  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; didChangeAppLifecycleState(AppLifecycleState state) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (state == AppLifecycleState.resumed) {
      <span class="hljs-keyword">await</span> checkPermissions();
    }
    <span class="hljs-comment">// you can check for the other states too and handle as expected.</span>
  }
}
</code></pre>
<h2 id="heading-flexibility-in-state-management">Flexibility in State Management</h2>
<p>There are multiple choices and flavors for state management in the Flutter community. Most of the time, the same features can always be built with any state management of choice.</p>
<p>With that in mind, be flexible with state management architectures in Flutter. They are not some hard cast rules. Bend and play around with them to suit your unique app cases as there is no "one size fits all" here.</p>
<p>You can play around with streams and services. You could use <a target="_blank" href="https://pub.dev/packages/get_it">getIt</a> for obtaining singletons. <code>getIt</code> also allows you to obtain scoped singletons, that is, singletons attached to a navigator or a logical part of features (within a search for example).</p>
<p>You can also combine this architecture with others. Like declaring and managing streams as explained here but in providers or cubits. Or bringing in features of other architectures into services you declare as described in this article.</p>
<p>Just be sure you know what you're doing and that you understand how to coordinate the variables representing app state. Preferably, document your choice of architectures in your codebase for future reference.</p>
<h2 id="heading-summary">Summary</h2>
<p>In summary, we have explored an efficient architecture for managing app-wide state in Flutter using Dart streams and singleton services.</p>
<p>We've also seen how to manipulate streams, how to use them in UI code, make services depend on each other, improve streams using <code>rxdart</code>, and handle app lifecycle changes.</p>
<p>Remember that state management in Flutter is flexible, and no one solution fits all. Tailor your choice of state management architecture to fit your specific app needs.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How is Flutter Platform-Agnostic? ]]>
                </title>
                <description>
                    <![CDATA[ Flutter builds applications for multiple platforms (desktop, mobile, and web) from the same codebase. Flutter does this in a pixel-perfect and platform-agnostic manner.  In this article, we will explore how Flutter is platform-agnostic through how it... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-is-flutter-platform-agnostic/</link>
                <guid isPermaLink="false">66b9ff46228e16bed602a872</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Tue, 07 May 2024 07:21:09 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/Screenshot-2024-05-02-225922.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Flutter builds applications for multiple platforms (desktop, mobile, and web) from the same codebase. Flutter does this in a pixel-perfect and platform-agnostic manner. </p>
<p>In this article, we will explore how Flutter is platform-agnostic through how it renders user interfaces and through platform channels.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-platform-agnosticism">What is Platform-Agnosticism?</a></li>
<li><a class="post-section-overview" href="#heading-how-native-and-other-cross-platform-apps-work">How Native and other Cross-Platform Apps work</a></li>
<li><a class="post-section-overview" href="#heading-how-flutter-apps-are-different-from-their-host-apps">How Flutter Apps are Different from their Host Apps</a></li>
<li><a class="post-section-overview" href="#heading-how-does-flutter-render-user-interfaces-uis">How Does Flutter Render User Interfaces (UIs)?</a></li>
<li><a class="post-section-overview" href="#heading-how-do-platform-channels-work-in-flutter">How Do Platform Channels Work in Flutter?</a>  </li>
<li><a class="post-section-overview" href="#heading-method-channels">Method Channels</a>  </li>
<li><a class="post-section-overview" href="#heading-event-channels">Event Channels</a>  </li>
<li><a class="post-section-overview" href="#heading-packages-and-plugins">Packages and Plugins</a></li>
<li><a class="post-section-overview" href="#heading-summary">Summary</a></li>
</ul>
<h2 id="heading-what-is-platform-agnosticism">What is Platform-Agnosticism?</h2>
<p>Platform-agnosticism is a relative measure of how an app works the same way, irrespective of the operating system on which the app is running.</p>
<p>When we say that an app or tool is platform-agnostic, users expect the same experience on that given application across their different devices.</p>
<p>To be platform-agnostic is to be indifferent to the device or the operating system. Users don't know the technical hurdle behind an app's architecture. They want to enjoy your app.  That's why you should give them the same experience across various platforms.</p>
<p>Flutter apps run in a platform-agnostic manner. Flutter apps work the same way on different platforms. This gives users (and developers) a seamless experience. </p>
<p>This article explores how Flutter does this. But before diving in, let's look at how native and other cross-platform tools handle their applications.</p>
<h2 id="heading-how-native-and-other-cross-platform-apps-work">How Native and other Cross-Platform Apps Work</h2>
<p>Operating systems or platforms (Android, iOS, Linux, macOS, Windows, and so on) specify how to build applications on them. They provide developers with the required APIs for apps to work. </p>
<p>When applications run, they constantly communicate with the underlying operating system. These native apps display buttons, icons, text, and other UI elements as platform-provided or OEM widgets. They also have access to device services like audio, Bluetooth, camera, and so on.</p>
<p>To build an application for a specific operating system, you normally use the programming language(s) and SDKs specified by that platform.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/native_apps.png" alt="How Native Apps Work" width="600" height="400" loading="lazy">
<em>How Native Apps Work</em></p>
<p>Initially, if you wanted your app to be available on multiple platforms, you'd have to build the same app for each platform but with separate SDKs as each platform requires. This also implies separate codebases, different developers, and more coding time.</p>
<p>Well, nowadays, we mostly use cross-platform tools to build apps. They use the same codebase to build applications on multiple operating systems. They solve the problem of multiple developer skillset and long development time by using the same codebase.</p>
<p>Cross-platform application frameworks have their rules and languages too. Nevertheless, behind the scenes, they still compile the code you wrote into what each platform needs. Examples of such cross-platform tools include Flutter, React Native, Xamarin, and so on.</p>
<p>Each framework has its underlying mechanisms to compile across multiple operating systems. Other frameworks (outside Flutter) are usually tightly-coupled to the target platform. They use bridges to access platform services. The bridge also renders the app's UI by using the target platform's specific UI components. Furthermore, some cross-platform apps sometimes use webview to render UIs.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/other_cross_platform_apps.png" alt="How Cross-Platform Apps Work" width="600" height="400" loading="lazy">
<em>How Cross-Platform Apps Work</em></p>
<p>The above is not how Flutter works. Flutter uses a different and innovative approach to build cross-platform apps. It does this by ensuring that the app is the same irrespective of the host platform.</p>
<p>Flutter is platform-agnostic. Flutter apps are technically separate from their host app.</p>
<h2 id="heading-how-flutter-apps-are-different-from-their-host-apps">How Flutter Apps are Different from their Host Apps</h2>
<p>When Flutter builds an app for a target platform, it ships an <em>inner Flutter app</em> and the Flutter engine within the built app. For example, when we build a Flutter codebase for Android, we will obtain an APK (an Android "installable"). Within this Flutter-built APK will be the Flutter engine and the Flutter app.</p>
<p>With this, the inner Flutter app performs similarly on each platform. When a user launches the "host" app on their device, the Flutter engine starts up and in turn, launches the inner Flutter app. Given that the engine runs Flutter, it will always run the same thing on whatever platform.</p>
<p>The advantage of separating Flutter apps from their host apps is platform-agnosticism. In other words, having the same app and user experience across every platform.</p>
<p>If the platform was to run the app contents directly or if it was a cross-platform bridge, we would have differences and discrepancies in how the app runs across various operating systems. This is because each operating system has its unique way of running apps.</p>
<p>Because Flutter apps run independently from their host apps, the Flutter framework lifts UI rendering and service access into the Flutter app (through the Flutter engine). That way, the Flutter app is more in charge and has better control over how the app feels.</p>
<p>For rendering UIs, the Flutter app draws Flutter widgets on a "canvas" in the host app. To access services, the Flutter app uses platform channels to interact with the host app when needed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/flutter_apps.png" alt="How Flutter Apps Work" width="600" height="400" loading="lazy">
<em>How Flutter Apps Work</em></p>
<h2 id="heading-how-does-flutter-render-user-interfaces-uis">How Does Flutter Render User Interfaces (UIs)?</h2>
<p>At the core of Flutter's platform-agnostic UI rendering is a pipeline, orchestrated by multiple UI layers. These layers include the embedder, the Flutter engine (with Skia or Impeller), and your Flutter App's UI.</p>
<p>The embedder serves as the bridge between the Flutter engine and the host platform. It provides an agnostic Application Binary Interface (ABI) for the Flutter Engine. It is written in the host platform's native programming language. It is deployed per platform. In other words, each platform has its embedder. Flutter uses the embedder to interface with the underlying operating system. </p>
<p>The Flutter engine executes Dart code, manages assets, handles events, and most importantly, renders UI. It draws (or rasterizes user interfaces) the same way irrespective of the platform. Hence, achieving platform-agnosticism.</p>
<p>The Flutter engine uses <a target="_blank" href="https://skia.org">Skia</a> or <a target="_blank" href="https://docs.flutter.dev/perf/impeller">Impeller</a> for low-level rendering tasks. Skia is an open-source graphics library with a robust set of drawing capabilities. Skia makes it possible to create smooth UIs.</p>
<p>Recently, we've got Impeller. Impeller provides a new rendering runtime for Flutter. It is already the default rendering tool in iOS devices and is coming soon to Android. It is an improvement to and should replace Skia. </p>
<p>On the host platform, Flutter accesses a black "canvas" (or the screen) and renders with its tools. On the web, it is the same thing but with a slight difference. There is no embedder (because there is no operating system). However, we convert UI directly from the Flutter framework into CanvasKit rendering. </p>
<p>Whether we are using the embedder and Flutter engine to render an app on any operating system or relying on Dart to JavaScript transpilation to render on browsers, Flutter apps are the same on the painted UI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/image.png" alt="The &quot;Canvas&quot;es used by Flutter for Each Platform " width="600" height="400" loading="lazy">
<em>The "Canvas"es used by Flutter for Each Platform</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/image-4.png" alt="The rendering layers of Flutter Apps in Operating Systems and Browsers" width="600" height="400" loading="lazy">
<em>The rendering layers of Flutter Apps in Operating Systems and Browsers</em></p>
<p>This entire architecture makes it possible that if a new operating system arises, we will only need to replicate the embedder with the expected Flutter engine ABI for that new OS. Once done, all existing Flutter apps will easily work on the OS.</p>
<p>Aside from UI rendering, Flutter is also platform-agnostic with the help of Platform Channels.</p>
<h2 id="heading-how-do-platform-channels-work-in-flutter">How Do Platform Channels Work in Flutter?</h2>
<p>In Flutter, platform channels serve as important communication routes at runtime between the Flutter framework and the host app (or the underlying native platform). Platform channels act as bridges between platform-agnostic Dart code and platform-specific functionalities. </p>
<p>Platform channels enable seamless integration of platform-specific features into Flutter apps. With them, you can leverage the capabilities of each target platform while maintaining a unified codebase.</p>
<p>With platform channels, Flutter achieves platform-agnosticism by abstracting away platform-specific details. As a result, you can write Flutter code that can seamlessly run on multiple platforms without extraneous modification.</p>
<p>There are two types of platform channels: method channels and event channels.</p>
<h3 id="heading-method-channels">Method Channels</h3>
<p>They are the most used. They facilitate bidirectional communication between Dart code and native platform code. </p>
<p>Through method channels, Flutter apps can invoke platform-specific methods, passing parameters and receiving results asynchronously. The reverse is also obtainable. The host (or native side of the) app can call methods defined inside the Flutter app.   </p>
<p>This allows you to access platform-specific APIs, system services, and hardware functionalities, such as accessing device sensors, interacting with native UI components, or integrating with platform-specific SDKs. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/platform-channels.png" alt="Systematic Diagram of MethodChannels" width="600" height="400" loading="lazy">
<em>Systematic Diagram of MethodChannels</em></p>
<h3 id="heading-event-channels">Event Channels</h3>
<p>Event channels transmit asynchronous events from the native platform to Dart code. They are useful in cases where the native platform needs to notify the Flutter app of asynchronous events or updates. </p>
<p>Good examples of such scenarios include:</p>
<ul>
<li>Receiving sensor data in real time </li>
<li>Handling push notifications</li>
<li>Monitoring system-level events, and so on.</li>
</ul>
<p>Event channels are crucial. With them, you can build reactive, event-driven apps that respond to changes in the underlying platform in a platform-agnostic manner.</p>
<p>Your Dart code needs to listen to event streams. These streams' values are emitted from the underlying platform. But how you handle them within the Dart facing side of the Flutter is usually the same way, hence platform-agnosticism.</p>
<h3 id="heading-packages-and-plugins">Packages and Plugins</h3>
<p>A Flutter package is reusable code that can extend your app. More than 25000 Flutter packages are deployed on <a target="_blank" href="https://pub.dev">pub.dev</a>. There is already a package(s) of platform-specific features that you want to implement in your app.</p>
<p>The Flutter framework itself is relatively small. It is the contribution of the community that builds these packages that makes the entire ecosystem large.</p>
<p>While there are many packages for Flutter-direct features, some provide platform-dependent features (like Camera, Location, Permissions, and so on). These platform-dependent packages use platform channels and plugins behind the scenes.</p>
<p>Plugins in Flutter are packages that encapsulate platform-specific functionality and expose them to Dart code through method and event channels. They act as wrappers around platform channels, providing a unified API surface for accessing platform-specific features. They abstract away the complexities of interacting with platform channels. </p>
<p>However, if there is no plugin for a feature you are building (which is rare), you can use platform channels. They make the Flutter code work the same way, no matter the platform. You will have to write the platform-specific method or event handlers in the programming languages specified by the involved platforms.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/image-5.png" alt="Platform Channels Programming Languages " width="600" height="400" loading="lazy">
<em>Platform Channels Programming Languages</em></p>
<h2 id="heading-summary">Summary</h2>
<p>Understanding Flutter's platform-agnosticism is important in appreciating the innovative architecture powering Flutter.</p>
<p>From how it renders user interfaces the same way on every device, to how it can run device-specific code at runtime, Flutter gives you a smooth developer experience and highly performant apps.</p>
<p>When next you use Flutter, don't be surprised how it achieves the same application on different devices.</p>
<p>Cheers!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Always Have A BuildContext in Flutter Outside of UI Code ]]>
                </title>
                <description>
                    <![CDATA[ The BuildContext provides important app-wide configuration information to all widgets in the widget tree. It is always naturally available in build methods and within State classes.  In this article, we will explore how we can obtain a valid BuildCon... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-always-have-a-buildcontext-in-flutter-outside-ui-code/</link>
                <guid isPermaLink="false">66b9ff49df3c2417440157e6</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Tue, 19 Mar 2024 14:42:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/Untitled-design.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The BuildContext provides important app-wide configuration information to all widgets in the widget tree. It is always naturally available in build methods and within State classes. </p>
<p>In this article, we will explore how we can obtain a valid BuildContext outside the scope of its natural availability.</p>
<h2 id="heading-table-of-contents">Table Of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-introduction">Introduction</a></li>
<li><a class="post-section-overview" href="#heading-the-essence-of-the-buildcontext-in-flutter">The essence of the BuildContext in Flutter</a></li>
<li><a class="post-section-overview" href="#heading-the-need-for-a-buildcontext-outside-ui-code">The need for a BuildContext outside UI code</a></li>
<li><a class="post-section-overview" href="#heading-having-a-globally-available-navigatorkey">Having a globally available navigatorKey</a></li>
<li><a class="post-section-overview" href="#heading-a-note-on-navigator-20">A note on Navigator 2.0</a></li>
<li><a class="post-section-overview" href="#heading-using-navigatorkeys-navigationstate-to-show-toasts">Using navigatorKey's NavigationState to show toasts</a></li>
<li><a class="post-section-overview" href="#heading-summary">Summary</a></li>
</ul>
<h2 id="heading-introduction">Introduction</h2>
<p>Flutter is a UI toolkit for building desktop, mobile, and web apps. Flutter is a framework that allows you to build apps in the Dart programming language.</p>
<p>In Flutter, you build UIs by composing widgets. A widget is any logical piece of what is renderable on the screen like Icon, Image, Text, and so on.</p>
<p>Composing widgets involves setting them as a child or as children of other parent widgets. In other words, you are building a widget tree.  All the widgets in your UI tree have access to the BuildContext.</p>
<h2 id="heading-the-essence-of-the-buildcontext-in-flutter">The Essence of the BuildContext in Flutter</h2>
<p>The BuildContext provides important app-wide configuration information to all widgets in the widget tree.</p>
<p>The BuildContext is to Flutter widgets what water is to our body. Just as water circulates round our system with nutrients and oxygen, so does the BuildContext provide runtime-specific values to every widget in our apps.</p>
<p>In Flutter, you commonly create a widget by extending the StatelessWidget or StatefulWidget (and its State) classes. </p>
<p>In both cases, you have to override the build method. In the build method, you must return another widget. By this way, you keep building the widget tree.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/widget_tree.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image Source: https://groups.google.com/g/flutter-dev/c/jfPgd5FS6EM</em></p>
<p>All build methods take only one argument: the BuildContext. You need the BuildContext in Flutter to render the UI. It is indispensable. The Flutter framework itself provides the BuildContext to each widget's build method.</p>
<p>The BuildContext gives you access to getters and methods like:</p>
<ul>
<li><a target="_blank" href="https://api.flutter.dev/flutter/widgets/BuildContext/findAncestorStateOfType.html"><code>findAncestorStateOfType</code></a>: a <em>typed</em> method that returns the <code>State</code> object of the specified <code>StatefulWidget</code>.</li>
<li><a target="_blank" href="https://api.flutter.dev/flutter/widgets/BuildContext/dependOnInheritedWidgetOfExactType.html"><code>getInheritedWidgetOfExactType</code></a>: a <em>typed</em> method that returns a requested widget type that is found upper in the widget tree.</li>
<li><a target="_blank" href="https://api.flutter.dev/flutter/widgets/BuildContext/mounted.html"><code>mounted</code></a>: a <code>bool</code> which tells whether the widget <em>in context</em> is in view.</li>
</ul>
<p>There are other useful getters and methods available on the BuildContext. Outside them, the BuildContext is also important because through it, we can obtain the app's active instance of specific classes. For example: MediaQuery, Navigator, Theme, and so on.</p>
<p>A common pattern is to call the static <code>.of</code> method on the class of interest and provide our BuildContext (or just "context") to this <code>.of</code> method.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> isLandscape = MediaQuery.of(context).orientation == Orientation.landscape;
<span class="hljs-keyword">final</span> hasConfirmed = Navigator.of(context).pop(<span class="hljs-keyword">false</span>);
<span class="hljs-keyword">final</span> lightTheme = Theme.of(context).copyWith(brightness: Brightness.light);
</code></pre>
<p>The <code>.of</code> is the most common pattern. However, there are other more direct, specific, and rare use cases of such static getters that rely on the context.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> isDarkTheme = Theme.brightnessOf(context) == Brightness.dark;
</code></pre>
<p>You can also create static getters of instances of your classes that rely on the BuildContext. <a target="_blank" href="https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html">You can learn more about how to do that (through InheritedWidget) here</a>.</p>
<p>As we see, the following are why we need the BuildContext:</p>
<ul>
<li>To build widgets,</li>
<li>To access resources,</li>
<li>To obtain instances of classes,</li>
<li>and so on.</li>
</ul>
<h2 id="heading-the-need-for-a-buildcontext-outside-ui-code">The need for a BuildContext outside UI code</h2>
<p>The BuildContext is naturally available inside all build methods and inside the <code>State</code> class of <code>StatefulWidgets</code>. Most method calls with context inside these scopes will work as intended.</p>
<p>In minor occasions, you will need to access the BuildContext from where it is not naturally available. You will want to obtain runtime instances outside of UI code (outside build methods and <code>State</code> classes).</p>
<p>This is usually difficult as we can't instantiate a BuildContext of our own. We only have access to one once our app has started running.</p>
<p>Popular reasons of accessing the BuildContext this way is either when performing navigation, when showing alerts and modals in non-UI code, or when triggering UI updates from changes in the app's state architecture.</p>
<p>Also, when a user opens a notification, we want to navigate the user to the target screen if the app is already open. We need the BuildContext to do this.</p>
<h2 id="heading-having-a-globally-available-navigatorkey">Having a globally available navigatorKey</h2>
<p>A solution to always having a BuildContext in Flutter is by providing one yourself at the start of the application.</p>
<p>The top-most widget of Flutter apps is usually either <a target="_blank" href="https://api.flutter.dev/flutter/cupertino/CupertinoApp-class.html"><code>CupertinoApp</code></a>, <a target="_blank" href="https://api.flutter.dev/flutter/material/MaterialApp-class.html"><code>MaterialApp</code></a>, or <a target="_blank" href="https://api.flutter.dev/flutter/widgets/WidgetsApp-class.html"><code>WidgetsApp</code></a>. In other words, they are the entry-point of our application.</p>
<p>These "app" widgets take an optional <code>navigatorKey</code> parameter. It has the type of <code>GlobalKey&lt;NavigatorState&gt;</code>. This NavigatorState-specific GlobalKey has a BuildContext attached to it once the app is running. </p>
<p>When you create a navigatorKey and give it to the top-most "app" widget, your Flutter app will keep the BuildContext it is using in your navigatorKey. That way, you can have access to the BuildContext wherever you have access to the navigatorKey.</p>
<p>Create and expose the navigatorKey as a <em>static final</em> variable from a global utility class. Provide it to the top-most "app" widget. Then obtain the context from this globally-available class wherever you need it.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/* In services/navigation_service.dart */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NavigationService</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> navigatorKey = GlobalKey&lt;NavigatorState&gt;();

  <span class="hljs-comment">// ... other methods and getters</span>
}

<span class="hljs-comment">/* In main.dart */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'services/navigation_service.dart'</span>;

<span class="hljs-keyword">void</span> main() =&gt; runApp(<span class="hljs-keyword">const</span> MyApp());

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      navigatorKey: NavigationService.navigatorKey, <span class="hljs-comment">// line of interest</span>
      home: <span class="hljs-keyword">const</span> Scaffold(body: Center(child: Text(<span class="hljs-string">'BuildContext'</span>))),
    );
  }
}

<span class="hljs-comment">/* In services/modal_service.dart */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'services/navigation_service.dart'</span>;

Future&lt;T?&gt; showModal&lt;T&gt;(Widget modal) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> showModalBottomSheet&lt;T&gt;(
    context: NavigationService.navigatorKey.currentContext!,
    backgroundColor: Colors.transparent,
    isScrollControlled: <span class="hljs-keyword">true</span>,
    builder: (_) =&gt; modal,
  );
}
</code></pre>
<p>In the example above, you can see how we can call <a target="_blank" href="https://api.flutter.dev/flutter/material/showModalBottomSheet.html"><code>showModalBottomSheet</code></a> from a non-widget file by using the context from the navigatorKey. This will only work if this key has been attached to the top-most "app" widget as shown in the code snippet.</p>
<p>This way of accessing the BuildContext has always been around. <a target="_blank" href="https://github.com/Stacked-Org/stacked/blob/53ef130d36db1d8d6756375cb8e9495f82c1d771/example/navigator_example/lib/main.dart#L20">We can see it in use in the Stacked Architecture</a>.</p>
<p>You could keep the navigatorKey in an entirely different class from some NavigationService. It could also be a standalone global variable in your Flutter project. Furthermore, you could make it available through the state management architecture you are using.</p>
<p>The manner in which you make the navigatorKey globally available is up to you. What is necessary is that you provide one to Flutter when you need the BuildContext outside UI code.</p>
<h2 id="heading-a-note-on-navigator-20">A note on Navigator 2.0</h2>
<p>There is a special configuration that is worthy of mention at this point. </p>
<p>The top-most "app" widgets (CupertinoApp, MaterialApp, and WidgetsApp) all have a <code>.router</code> constructor. This constructor is different from the direct equivalent because it creates an app that uses a <a target="_blank" href="https://docs.flutter.dev/ui/navigation"><code>Router</code> instead of a <code>Navigator</code></a>.</p>
<p>Specifically, the <code>.router</code> apps allow us to perform declarative navigation (<code>router.go</code>) instead of only imperative navigation (<code>navigator.push</code> and <code>navigator.pop</code>). </p>
<p>Declarative navigation works well with deep linking (when mobile apps open URLs) and with the browser history (a plus to Flutter web).</p>
<p>These <code>.router</code> constructors don't take a navigatorKey argument. However, they can accept various router-specific classes as arguments. In these classes, you can provide a navigatorKey in one way or the other.</p>
<p>For example, <a target="_blank" href="https://pub.dev/packages/go_router"><code>go_router</code></a> is a well-known package for Navigator 2.0 in Flutter. It abstracts out the complexities of those router-specific classes. </p>
<p>Now, the <a target="_blank" href="https://pub.dev/documentation/go_router/latest/go_router/GoRouter-class.html"><code>GoRouter</code></a> constructor, as you will expect, accepts a navigatorKey argument. This way, if you are using it, you can still have access to the BuildContext in any non-UI code in your Flutter project.</p>
<h2 id="heading-using-navigatorkeys-navigationstate-to-show-toasts">Using navigatorKey's NavigationState to show toasts</h2>
<p>An added advantage of having an attached navigatorKey is when we want to toast information to the user. </p>
<p>We can do this by adding an <a target="_blank" href="https://api.flutter.dev/flutter/widgets/OverlayEntry-class.html"><code>OverlayEntry</code></a> (with a toast widget) into the <code>overlay</code> of <code>NavigatorState</code> that is attached to our navigatorKey.</p>
<p>With the help of a <code>Timer</code>, the toast gets dismissed after a few seconds:</p>
<pre><code class="lang-dart"><span class="hljs-comment">/* In services/toast_service.dart */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'navigation_service.dart'</span>;

Widget _toast(<span class="hljs-built_in">String</span> text) =&gt; Container(
    padding: <span class="hljs-keyword">const</span> EdgeInsets.fromLTRB(<span class="hljs-number">16</span>, <span class="hljs-number">12</span>, <span class="hljs-number">16</span>, <span class="hljs-number">12</span>),
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(<span class="hljs-number">6</span>),
      color: Colors.teal,
    ),
    child: Text(text),
  );

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ToastService</span> </span>{
  <span class="hljs-keyword">static</span> OverlayEntry? _entry;
  <span class="hljs-keyword">static</span> Timer? _timer;

  <span class="hljs-keyword">static</span> <span class="hljs-keyword">show</span>(<span class="hljs-built_in">String</span> text) {
    _dismiss();
    _entry = OverlayEntry(builder: (context) =&gt; _Toast(text));
    NavigationService.navigatorKey.currentState!.overlay!.insert(_entry!);
    _timer = Timer(<span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">5</span>), _dismiss);
  }

  <span class="hljs-keyword">static</span> _dismiss() {
    <span class="hljs-keyword">try</span> {
      _timer?.cancel();
      _timer = <span class="hljs-keyword">null</span>;
      _entry?.remove();
      _entry = <span class="hljs-keyword">null</span>;
    } <span class="hljs-keyword">catch</span> () {
    }
  }
}

<span class="hljs-comment">/* In services/auth_service.dart */</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'toast_service.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthService</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> login({<span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> email, <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> password}) <span class="hljs-keyword">async</span> {
    <span class="hljs-comment">// ... calling the API</span>

    <span class="hljs-comment">// toast after a successful login</span>
    ToastService.<span class="hljs-keyword">show</span>(<span class="hljs-string">'Welcome Back!'</span>);
  }
}
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>"context" is something you will always use in Flutter. </p>
<p>When you need it outside the build method (or State classes), create a navigatorKey and attach it to the top-most "app" widget. </p>
<p>You can then access the same context that your UI code uses from this key from anywhere within the Flutter project.</p>
<p>Cheers!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Prevent Account Loss When using Two-Factor Authentication ]]>
                </title>
                <description>
                    <![CDATA[ If someone gains unauthorized access to your passwords, two-factor authentication (2FA) can prevent them from accessing your account.  But if you ever lose access to all your 2FA methods, you have lost your account. How do you prevent such loss? In t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-prevent-account-loss-when-using-two-factor-authentication/</link>
                <guid isPermaLink="false">66b9ff4f7a31f91cb02e2f70</guid>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Two-factor authentication ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Wed, 22 Mar 2023 16:28:07 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/03/pexels-karolina-grabowska-4467737.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If someone gains unauthorized access to your passwords, two-factor authentication (2FA) can prevent them from accessing your account. </p>
<p>But if you ever lose access to all your 2FA methods, you have lost your account. How do you prevent such loss?</p>
<p>In this article, we will look at two-factor authentication (2FA), its methods, and how to prevent account loss because of using it.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-two-factor-authentication-2fa">What is Two-Factor Authentication (2FA)</a>?</li>
<li><a class="post-section-overview" href="#heading-methods-of-two-factor-authentication">Methods of Two-Factor Authentication</a></li>
<li><a class="post-section-overview" href="#heading-the-problem-with-two-factor-authentication">The problem with Two-Factor Authentication</a></li>
<li><a class="post-section-overview" href="#heading-how-to-protect-yourself-from-losing-access-to-your-accounts">How to Protect Yourself from Losing Access to Your Accounts</a>  </li>
<li><a class="post-section-overview" href="#heading-1-backup-and-write-down-the-recovery-codes">Backup and write down the recovery code(s)</a>  </li>
<li><a class="post-section-overview" href="#heading-2-backup-and-write-down-the-authenticator-app-seed">Backup and write down the authenticator app seed</a>  </li>
<li><a class="post-section-overview" href="#heading-3-set-up-more-than-one-2fa-method">Set up more than one 2FA method</a>  </li>
<li><a class="post-section-overview" href="#heading-4-always-update-2fa-methods">Always update 2FA methods</a>  </li>
<li><a class="post-section-overview" href="#heading-5-set-up-recovery-email-and-phone">Set up recovery email and phone</a> </li>
<li><a class="post-section-overview" href="#heading-summary">Summary</a></li>
</ul>
<h2 id="heading-what-is-two-factor-authentication-2fa">What is Two-Factor Authentication (2FA)?</h2>
<p>Two-Factor Authentication is an extra security setting for your account that many platforms have enabled. </p>
<p>It is an extra security setting because you already have some "first" way of authenticating yourself. This "first" way could be password sign-in or federated identity sign-in. Federated identity means signing in with another provider, like Apple, Facebook, Google, and so on.</p>
<p>Two-Factor authentication helps reduce unauthorized access to your data or account. It is a second sign-in step to verify that you are the owner of the account. So, in addition to your first sign-in method, you will have to complete this second step (hence two-factor) to access your account.</p>
<p>Two-Factor authentication is not about using and securing your passwords. That one is basic account security. We are talking about an advanced security step where, after entering your password, you still need to enter some code or complete some action before you gain access to your account. </p>
<p>In this article, platform means any place where you can sign in and sign out. freeCodeCamp, LinkedIn, and Twitter are examples of platforms. In this article, a platform is a place where you have an account.</p>
<p>Some platforms don't offer 2FA. You might have relatively little data with them. So 2FA would be overkill there. On the other hand, the major and mainstream platforms where you can have an account offer 2FA. These are platforms where a user's account is worth hacking. </p>
<p>In most platforms, 2FA is optional. In a few, however, you are required to set up 2FA from the very first time you access the account and you can't turn it off. In other words, 2FA is a must-do on a few platforms.</p>
<p>You should set up 2FA where it is available. To set up 2FA, go to the security settings of your account on a supporting platform. Set up 2FA using at least of one the specified methods and you are good to go. On subsequent sign-ins, you will need to complete 2FA to enter the account.</p>
<h2 id="heading-methods-of-two-factor-authentication">Methods of Two-Factor Authentication</h2>
<p>Methods of 2FA refer to the various ways to set up 2FA. They are as follows:</p>
<ol>
<li>Security Key 2FA</li>
<li>Phone number 2FA</li>
<li>Authenticator app 2FA</li>
<li>Sign-In prompt</li>
<li>Recovery code(s)</li>
</ol>
<p>Security keys are USB or Bluetooth keys that you can connect to your device. They are physical 2FA devices and seem to be the most secure. This is because it is highly unlikely that a hacker would come and steal the key from your house. </p>
<p>To set up 2FA using keys, plug in the key or connect via Bluetooth and complete the steps. You can then use the key in consequent sign-ins. You have to buy these keys from the market or online to use them. </p>
<p>Phone number 2FA involves receiving a One-Time Password (OTP) code either via call or SMS. Provide a phone number and the platform sends an OTP to it. Sometimes, you could receive an automated call with the code. Either way, enter the code in the platform and you have successfully set up the phone number 2FA. </p>
<p>On subsequent sign-ins to access your account, after entering your password, you will receive an OTP code to that phone. Enter the code to gain access to your account.</p>
<p>Authenticator apps generate a 6-digit code every minute. <a target="_blank" href="https://www.google.com/search?q=authenticator+apps">There are many authenticator apps</a>, like Google Authenticator, Microsoft Authenticator, and others. </p>
<p>To set up an authenticator app, you either scan a QR code or enter a seed phrase. The platform where your account resides will provide you with either of these. Scan or enter the phrase and you will keep getting a unique 6-digit code every minute. </p>
<p>In the next sign-ins, after entering your password, paste the latest 6-digit code from the authenticator app and you are in.</p>
<p>Sign-in prompts are not as common as other 2FA methods. Not all platforms have this feature. It involves authorizing your sign-in from an app or website of that platform where you are already signed in. </p>
<p>GitHub and Google have these features. With the GitHub app, you can complete 2FA by entering a number on the screen from the app/website you signed in to. Google does it together with your Android device and your Google account.</p>
<p>Aside from the above 2FA methods, we also have recovery codes. Recovery codes are a one-time-use code that you can use to complete 2FA. You can access them under the 2FA settings of your account.</p>
<p>In fact, once you set at least one 2FA method on your account, the platform autogenerates these recovery codes for you. These codes are like a backup option for 2FA. You are meant to use them when you don't have access to the other 2FA methods.</p>
<h2 id="heading-the-problem-with-two-factor-authentication">The Problem with Two-Factor Authentication</h2>
<p>The problem with 2FA is that if you lose access to all the methods you've set up, <strong>you've permanently lost access</strong> to that account. Let me explain.</p>
<p>2FA ensures that it is the correct user that is accessing an account. That's why we have those above secondary methods aside from your password. 2FA can only be enabled or disabled while signed in. </p>
<p>But aside from the recovery codes, there is no other recovery mechanism. If you lose the security keys, phone number, devices with the authenticator apps and previous sign-ins, and the recovery codes, your account is gone. Not even the account support for that platform can help you. </p>
<p>This is not the common "Forgot Password" feature, where the platform sends a unique link or code to your email or phone to reset your password. This is 2FA. Once you lose all 2FA methods you had set, your account is gone. Support can't give you the recovery codes because they don't know them. Those codes are encrypted with your account's access (only you can access them when you are signed in).</p>
<p>Does this mean that you shouldn't set up 2FA? </p>
<p>No. It means that in addition to setting up 2FA, you have to guard your 2FA methods. Remember that it is security that led us to 2FA. Well, 2FA itself also needs security. So you have to secure it.</p>
<p>From here, we will be looking at the various things you can do to prevent account loss due to Two-Factor Authentication.</p>
<h2 id="heading-how-to-protect-yourself-from-losing-access-to-your-accounts">How to Protect Yourself from Losing Access to Your Accounts</h2>
<h3 id="heading-1-backup-and-write-down-the-recovery-codes">1. Backup and write down the recovery code(s)</h3>
<p>All platforms that grant 2FA also give a recovery code(s). Always back them up. Copy and save the recovery codes securely somewhere of your choice. Hide them discretely online. Encrypt them in some vaulted storage. </p>
<p>Most importantly, <strong>write</strong> them down. Like use your hand and write with a pen in some diary or book you won't lose. You can equally print and store them with your documents and certificates. Just protect each recovery code per platform in the most secure way you're able.</p>
<p>Also, don't store the recovery code of an account in that same account. No one keeps spare keys inside the house. You give out your spare keys to trusted persons or keep them in other places where you can go retrieve them when need be. This is because if your main key goes missing while you are out, you can go and retrieve the spares. You can't retrieve the spare keys when they are inside the house and you are locked out.</p>
<p>In the same vein, it makes no sense to keep the recovery codes of your Google Account's 2FA inside your account's Google Drive or in a private Google Doc owned by the same account. When you will need it, you can't get it.</p>
<p>Recovery codes are <strong>one-time-use</strong> codes. You can't reuse the same recovery code for another 2FA session. You shouldn't really be using your recovery codes. If you start using them, it is a sign that you need to either update your 2FA methods, turn off 2FA temporarily, or regenerate a new set of recovery codes (to replace the used ones).</p>
<p>You can always copy out your recovery codes. Also, you can always generate a new set of recovery codes in each platform. Every platform permits that. Remember that you have to be signed in to do all these things. </p>
<p>Also, anytime you disable or turn off two-factor authentication in your account settings, the previous recovery codes become invalid. If you enable 2FA again, you will need to re-backup and re-write the new recovery codes.  </p>
<p>Make sure you store recovery code(s) for each platform in multiple and different places. </p>
<h3 id="heading-2-backup-and-write-down-the-authenticator-app-seed">2. Backup and write down the authenticator app seed</h3>
<p>When setting up the authenticator app 2FA, don't use the QR code option. Go for the seed option. The seed for authenticator apps comes as a series of about 20 to 40 alphanumeric characters. When the platform shows you this seed, first <strong>write</strong> it down. </p>
<p>Write down the authenticator app seed in your diary or in some book you've set aside for backup. If possible, copy it out and print it. Equally backup the seed for each platform in some other online media where you can retrieve it from. You want to minimize your chances of losing these seeds. </p>
<p>Each minute, authenticator apps generate 6-digit codes <strong>based on the seed</strong> and not based on the app itself or the platform. If you paste that same seed into another authenticator app, the two apps will be generating the exact same 6 digits, every minute. In fact, if you re-use that exact same phrase to recreate another 2FA entry in the same authenticator app, the two entries will be generating the same 6-digit code per minute.</p>
<p>This is why the authenticator app seed is as important as the 2FA recovery codes. If you lose access to the device that your 2FA authenticator app is on, you can put your seeds in another authenticator and get back the 6-digit codes for 2FA. Protect these seeds too. Because if someone has both your passwords and seeds, then they have access to your accounts.  </p>
<p>Backing up and writing down authenticator app seeds is necessary and it is under-preached. Many people don't realize that device loss means account loss if the authenticator app was their only 2FA method and if they didn't save the seed and recovery codes somewhere. </p>
<p>A device crash can happen. The Operating System might crash and you may lose apps and data. The authenticator app could be mistakenly uninstalled. Backing up and writing down the seeds prevent these circumstances from affecting your 2FA setups. </p>
<p>You can reinstall the authenticator app and reconfigure the entries of each account from your saved seed, without needing to go to the account settings of each platform. Also, you will be capable of continuing to use your two-factor authentication methods, without needing to re-backup or re-write down a new set of recovery codes.</p>
<p>If you've already set up the authenticator app 2FA, and you didn't save the original seed your account's platform gave you, please, please, please go and reconfigure the authenticator app 2FA in your security settings. You might have to just disable only the authenticator app and turn it back on. While re-enabling, make sure you back up and write down the seed. You don't want to live the experience of being locked out of your account.</p>
<h3 id="heading-3-set-up-more-than-one-2fa-method">3. Set up more than one 2FA method</h3>
<p>Setting up more than one method for two-factor authentication reduces your chances of account loss. The idea is to ensure that you always have access to at least one (if not all) 2FA methods. You can set up all the available 2FA methods per platform account. </p>
<p>For USB/Bluetooth security key, you can use the same key for all your accounts across various platforms. For your phone number, you can use the same phone number across each account on each platform. (But some platforms don't permit you to have multiple accounts with the same phone number. Have this in mind if you fall into that category).</p>
<p>The authenticator app seed for each account per platform is different. But you can use the same authenticator app for all accounts in which you've set up 2FA. Just remember to back up and write down the seeds as mentioned in the previous section.</p>
<p>So for each platform, you want to ensure that you have set more than one 2FA method. You can receive an SMS with some phone number. You have a working authenticator app. You've backed up and written down the authenticator app seed and 2FA recovery codes. And where you can, you've equally set up the USB/Bluetooth security key.</p>
<h3 id="heading-4-always-update-2fa-methods">4. Always update 2FA methods</h3>
<p>Things can happen that warrant updating your 2FA methods. Updating 2FA methods include removing or adding some methods. You can equally change the settings of the same method. </p>
<p>For example, let's say you never had a security key in your account. But you had set up 2FA with a phone number and authenticator app. Then you just bought a new security key. You can go and add the security key in your 2FA settings as a new method. On the other hand, if you lose your USB/Bluetooth security key, you can disable the security key option. Then when you buy a new one, you can put it back in your settings. </p>
<p>If you lost your all backups and written-down versions of your authenticator app seeds and 2FA recovery codes, you can regenerate new ones and re-backup and re-write down. You really shouldn't lose everything. Such loss is not good. Well, anything is possible. Prevention is the only remedy as there is no cure in the world for complete 2FA loss. </p>
<p>Also, it is okay to turn off 2FA if you need to. 2FA is recommended and necessary. But if you see the need to, turn it off for the moment. Then set it up back sometime later on. </p>
<h3 id="heading-5-set-up-recovery-email-and-phone">5. Set up recovery email and phone</h3>
<p>Backup email and phone are not really a 2FA thing. The reason is they are always there whether you configure two-factor or not. Most major platforms permit you to set a recovery email and or a recovery phone number for your account. These recovery assets come in handy when you forget your password and not when you lose 2FA.</p>
<p>Well, we are talking about security, so it is worth mentioning that you should have recovery email and phone properly set up where possible. </p>
<p>In some platforms, your account or your recovery phone number can be different from your 2FA phone number. Your 2FA phone number must always be one where you can receive an OTP (via call or SMS) and use it to complete the 2FA step.</p>
<h2 id="heading-summary">Summary</h2>
<p>You want to lock out malicious access to your account. But you don't want to lock out yourself.  </p>
<p>In this guide, we've looked at how to best use the two-factor authentication feature across various accounts. At the same time, we've also seen how you can ensure that you don't lose access to your account because you tried securing it in the first place.</p>
<p>Back up and write down recovery codes and authenticator app seeds. Set up multiple 2FA methods. Always review and update your 2FA settings. Do these and be rest assured of the best online security.</p>
<p>Cheers!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Stacked Architecture to Build a Flutter Todo App ]]>
                </title>
                <description>
                    <![CDATA[ Flutter is a UI toolkit for building cross-platform applications. You can build Flutter apps using various state management techniques like the Stacked architecture.  This article will explain what Stacked architecture is and will guide you through c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/flutter-stacked-architecture-todo-app/</link>
                <guid isPermaLink="false">66b9ff4285b28e0b24537e55</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ State Management  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Mon, 18 Jul 2022 22:03:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/cover-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Flutter is a UI toolkit for building cross-platform applications. You can build Flutter apps using various state management techniques like the <a target="_blank" href="https://pub.dev/packages/stacked">Stacked</a> architecture. </p>
<p>This article will explain what Stacked architecture is and will guide you through creating a simple Todo App in Flutter with Stacked.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-intro-to-app-state-and-flutter-widgets">Intro to App State and Flutter Widgets</a></li>
<li><a class="post-section-overview" href="#heading-why-do-you-need-state-management-architectures-in-flutter">Why Do You Need State Management Architectures in Flutter</a>?</li>
<li><a class="post-section-overview" href="#heading-what-is-stacked-architecture">What is Stacked Architecture</a>?</li>
<li><a class="post-section-overview" href="#heading-about-the-todo-app-we-will-be-building">About the Todo App We Will be Building</a></li>
<li><a class="post-section-overview" href="#heading-how-to-setup-flutter-and-the-todos-service">How to Setup Flutter and the Todos Service</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-todo-apps-ui">How to build the Todo App's UI</a></li>
<li><a class="post-section-overview" href="#heading-note-on-other-stacked-features">Note on Other Stacked Features</a></li>
<li><a class="post-section-overview" href="#heading-summary">Summary</a></li>
</ul>
<h2 id="heading-intro-to-app-state-and-flutter-widgets">Intro to App State and Flutter Widgets</h2>
<p>State refers to any data you use in rendering your UI. It could be user-generated or from your servers or backend.</p>
<p>In Flutter, the app's <a target="_blank" href="https://en.wikipedia.org/wiki/User_interface">User Interface (UI)</a> is a function of State. In other words, your UI at any given point in time is a visual representation of your app's state. </p>
<p>Still, in Flutter, you use widgets to build different parts of the app's UI. A widget in Flutter is a piece or unit of the UI. Essentially, a Flutter application is a big tree of widgets. Examples of widgets are <a target="_blank" href="https://api.flutter.dev/flutter/material/AppBar-class.html">AppBar</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container-class.html">Container</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Icon-class.html">Icon</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Image-class.html">Image</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Text-class.html">Text</a>, and so on. </p>
<p>Flutter has two types of widgets: <a target="_blank" href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html">StatelessWidget</a>s and <a target="_blank" href="https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html">StatefulWidget</a>s. You use StatefulWidgets in Flutter to build widgets that have State. </p>
<p>To tell Flutter that some state has changed in your application, you call the <code>[setState](https://api.flutter.dev/flutter/widgets/State/setState.html)</code> function (available only inside StatefulWidgets) and Flutter will rebuild the widget tree based on state data that has changed.</p>
<p>Widgets in Flutter don't change. They are immutable. Flutter paints UIs efficiently at about 60 frames per second. </p>
<p>At each frame paint, Flutter checks the widget tree. If any widgets have changed, Flutter gets rid of them and replaces them with new widgets that have the changed state.</p>
<h2 id="heading-why-do-you-need-state-management-architectures-in-flutter">Why Do You Need State Management Architectures in Flutter?</h2>
<p><code>setState</code> is good. It is an optimal way of handling UI and state because it encourages a <a target="_blank" href="https://www.freecodecamp.org/news/imperative-vs-declarative-programming-difference/">declarative programming</a> style (which is the best way to code UIs).</p>
<p>But then as your Flutter codebase grows, you will realize that you will always make calls to <code>setState</code>. Your Flutter app will have so many widgets that depend on huge and changing state data. This causes your code to be clogged up.</p>
<p>Using state management architectures across frameworks and libraries helps with <a target="_blank" href="https://en.wikipedia.org/wiki/Separation_of_concerns">Separation of concerns</a>, clean code, and scaling. State management is also helpful when multiple developers are contributing to the same codebase. Architectures also help with code testability.</p>
<p>So to build robust apps, you should use state management where you need it. By default, <code>setState</code> is your go-to option for state management. But as you begin adopting architectures, you will use other available <a target="_blank" href="https://docs.flutter.dev/development/data-and-backend/state-mgmt/options">state management options in Flutter</a>.</p>
<p>In Flutter, every widget has a <code>build</code> method that returns its tree of widgets. The <code>build</code> method takes a <code>BuildContext</code> with which it can access important data about the app or perform tasks like navigation or showing dialogs. </p>
<p><a target="_blank" href="https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html"><code>InheritedWidget</code></a> is a built-in approach for accessing state from a widget higher in the widget tree. It accesses state from this higher widget with the help of the BuildContext.</p>
<p><code>[ChangeNotifier](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html)</code> is a Flutter class that does as its name says. It notifies listeners about changes in its values. You can extend this class and call its <code>[notifyListeners();](https://api.flutter.dev/flutter/foundation/ChangeNotifier/notifyListeners.html)</code> method which you can in turn use to update the UI where need be. Popular state management options in Flutter use <code>ChangeNotifier</code>. For example, <a target="_blank" href="https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple">Provider</a>.</p>
<p>Some state management architectures find a way to navigate or show dialogs without the BuildContext in the build method. That way they can do these crucial parts of the app in files not containing widgets. And if they need to update the UI, they use <code>notifyListeners()</code>. Such is the case with Stacked architecture.</p>
<h2 id="heading-what-is-stacked-architecture">What is Stacked Architecture?</h2>
<p>Stacked is a modern Flutter state management with top-notch <a target="_blank" href="https://en.wikipedia.org/wiki/Separation_of_concerns">separation of concerns</a> and <a target="_blank" href="https://www.freecodecamp.org/news/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f/">dependency injection</a>. </p>
<p>Separation of concerns involves separating all UI code from logic code. Such separation is important for the maintainability of your Flutter project.</p>
<p>For example, with separation of concerns properly set up in your codebase, if there are updates to your backend API, you would only need to update the files that deal with the API logic. You won't necessarily need to update the UI code. This minimizes bugs that could happen while coding.</p>
<p>Dependency injection (or inversion of control) is a popular programming technique. For a given block of code, dependence injection is the fact that that block does not configure the things it needs to function (dependencies) by itself.</p>
<p>With dependency injection, you provide a class with the services (dependencies) it needs for proper functioning. In turn, these services abstract out and handle the app's business logic.</p>
<p>For example, a weather service can take care of fetching weather details and a weather display widget will use this service to display the data. This widget doesn't know how to set up the service. All it knows is that it will receive the weather data it needs to display from the weather service class in some way.</p>
<p>Stacked implements separation of concerns and dependency injection by building Flutter code around 3 entities: <strong>Views</strong>, <strong>ViewModels</strong>, and <strong>Services</strong>. </p>
<p>Views handle only UI code and are linked to ViewModels. ViewModels accompany views and handle UI logic. ViewModels use services. Views should never access services.  </p>
<p>Stacked was founded by <a target="_blank" href="https://www.linkedin.com/in/dane-mackier-a0b99670">Dane Mackier</a> and is now being maintained by the community. <a target="_blank" href="https://developers.google.com/community/experts/directory/profile/profile-dane-mackier">Dane Mackier is a GDE (Google Developer Expert) for Dart and Flutter</a>. He used other popular state management architectures and combined their advantages in building Stacked.</p>
<p>Note that Stacked uses ChangeNotifier.</p>
<h2 id="heading-about-the-todo-app-we-will-be-building">About the Todo App We Will Be Building</h2>
<p>To keep things simple, we will build a one-screen dark-mode Todo App with Stacked architecture. </p>
<p>The app will display a message if there are no todos yet. However, if there are Todos, it will display all the todos one after the other. </p>
<p>Each Todo will have a "checkable circle" at its left to indicate whether it has been completed or not. At its right, a Todo will have a button (with a dash icon) to remove the Todo. The Todo's text content will fill the center or major part of the screen. </p>
<p>We will also have a floating action button (with a plus icon) to create new todos.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/sample.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In addition to the above, we will store Todos to some browser or device storage with <a target="_blank" href="https://pub.dev/packages/hive">Hive</a>. As a result, on closing and re-opening the Todo App, all previously created Todos will be restored.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/fcc_refresh.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Todos been restored after browser reload</em></p>
<p>Because this is Stacked architecture, we will have a TodosService that will take care of managing Todos. We will also have a TodosScreen (for the UI). The TodosScreen will have a View and a ViewModel. </p>
<p>That said, let's start coding.</p>
<p><a target="_blank" href="https://github.com/obumnwabude/flutter_stacked_todo/tree/freecodecamp">If you don't want to code along, the final code is here.</a></p>
<h2 id="heading-how-to-setup-flutter-and-the-todos-service">How to Setup Flutter and the Todos Service</h2>
<h3 id="heading-1-install-flutter">1. Install Flutter</h3>
<p>First, you'll need to have Flutter installed and working properly. If you do, you can get to step two. You can also get to step two if you don't want to follow along.</p>
<p>If you don't have Flutter installed, install it by following <a target="_blank" href="https://docs.flutter.dev/get-started/install">the steps for your operating system here</a>. </p>
<p>Run the <code>flutter doctor -v</code> command in your terminal. If all the listed options have a good check or green color, then you are good to continue. If any of the results have errors, search the error online and you'll find solutions to the problem.</p>
<h3 id="heading-2-create-the-flutter-project">2. Create the Flutter Project</h3>
<p>Run the following command to create a new Flutter project:</p>
<pre><code class="lang-bash">flutter create todo
</code></pre>
<p>This creates a new Flutter project with <code>todo</code> as the name of the app. If you want a different app name (aside from <code>todo</code>), use it instead of <code>todo</code>.</p>
<h3 id="heading-3-add-the-packages">3. Add the Packages</h3>
<p>We need to add the <code>[stacked](https://pub.dev/packages/stacked)</code> Flutter package to project. It will provide us with necessary classes (like ViewModels), given that we are building with the Stacked Architecture.</p>
<p>We also need to install another package to help with dependency injection or services. To keep things simple, we will use the <code>[get_it](https://pub.dev/packages/get_it)</code> package for "getting" services in this Todo App. </p>
<p>Because we want to persist todos across app closes with Hive, we will also need to add the <code>[hive](https://pub.dev/packages/hive)</code> and <code>[hive_flutter](https://pub.dev/packages/hive_flutter)</code> packages.</p>
<p>Change the terminal's focus directory to be inside the <code>todo</code> Flutter project you just created above. Run the following command in the same terminal:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> todo
</code></pre>
<p>If you used a different app name, use it with the <code>cd</code> command instead of <code>todo</code>.</p>
<p>Run the following command in your terminal to add <code>get_it</code>, <code>hive</code>, <code>hive_flutter</code>, and <code>stacked</code> in the Flutter project. </p>
<pre><code class="lang-bash">flutter pub add get_it hive hive_flutter stacked
</code></pre>
<h3 id="heading-4-create-a-todo-model">4. Create a Todo Model</h3>
<p>We are building a Todo App meaning that we will be interacting with Todos. A Todo, in the context of the code, is an entity that we will be manipulating.</p>
<p>To keep things simple, a Todo will be a Dart class with just three properties: </p>
<ol>
<li><code>id</code>: Uniquely identifying string for each Todo.</li>
<li><code>completed</code>: Boolean value to indicate the status of the Todo</li>
<li><code>content</code>: The actual text content of the Todo.</li>
</ol>
<p>Open the Flutter project in your favorite editor. </p>
<p>Create a new folder with name <code>models</code> inside the <code>lib</code> folder. Inside this newly created <code>models</code> folder, create a new file with name <code>todo.dart</code>.</p>
<p>Paste the following into the newly created <code>lib/models/todo.dart</code> file:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Todo</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> id;
  <span class="hljs-built_in">bool</span> completed;
  <span class="hljs-built_in">String</span> content;

  Todo({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id, <span class="hljs-keyword">this</span>.completed = <span class="hljs-keyword">false</span>, <span class="hljs-keyword">this</span>.content = <span class="hljs-string">''</span>});
}
</code></pre>
<p>Notice that the <code>id</code> property is required for each todo. However, by default, a Todo is <strong>not</strong> completed and has empty content.</p>
<h3 id="heading-5-create-the-todoadapter-for-hive">5. Create the TodoAdapter (for Hive)</h3>
<p>Hive works well with primitive types (like bools, ints, strings, and so on). But to properly retrieve and save custom types (like our Todo model) from and to browser or device storage, Hive needs us to create adapters for our custom types.</p>
<p>Create a file named <code>todo.adapter.dart</code> in the <code>lib/models</code> folder. The <code>todo.adapter.dart</code> should accompany the <code>todo.dart</code> file. </p>
<p>Paste the following into the newly created <code>lib/models/todo.adapter.dart</code> file:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:hive_flutter/hive_flutter.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'todo.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoAdapter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">TypeAdapter</span>&lt;<span class="hljs-title">Todo</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> typeId = <span class="hljs-number">1</span>;

  <span class="hljs-meta">@override</span>
  Todo read(BinaryReader reader) {
    <span class="hljs-keyword">final</span> numOfFields = reader.readByte();
    <span class="hljs-keyword">final</span> fields = &lt;<span class="hljs-built_in">int</span>, <span class="hljs-built_in">dynamic</span>&gt;{
      <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i &lt; numOfFields; i++) reader.readByte(): reader.read(),
    };
    <span class="hljs-keyword">return</span> Todo(
      id: fields[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String</span>,
      completed: fields[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">bool</span>,
      content: fields[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String</span>,
    );
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> write(BinaryWriter writer, Todo obj) {
    writer
      ..writeByte(<span class="hljs-number">3</span>)
      ..writeByte(<span class="hljs-number">0</span>)
      ..write(obj.id)
      ..writeByte(<span class="hljs-number">1</span>)
      ..write(obj.completed)
      ..writeByte(<span class="hljs-number">2</span>)
      ..write(obj.content);
  }
}
</code></pre>
<p>The above code basically provides read and write methods for Hive to retrieve and store a Todo. </p>
<h3 id="heading-6-create-the-todosservice">6. Create the TodosService</h3>
<p>Create a new folder with name <code>services</code> inside the <code>lib</code> folder. Inside this newly created <code>services</code> folder, create a new file with name <code>todos.service.dart</code>.</p>
<p>Paste the following into the newly created <code>lib/services/todos.services.dart</code> file:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:math'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:hive_flutter/hive_flutter.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked/stacked.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'../models/todo.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodosService</span> <span class="hljs-title">with</span> <span class="hljs-title">ReactiveServiceMixin</span> </span>{
  <span class="hljs-keyword">final</span> _todos = ReactiveValue&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt;(
    Hive.box(<span class="hljs-string">'todos'</span>).<span class="hljs-keyword">get</span>(<span class="hljs-string">'todos'</span>, defaultValue: []).cast&lt;Todo&gt;(),
  );
  <span class="hljs-keyword">final</span> _random = Random();

  <span class="hljs-built_in">List</span>&lt;Todo&gt; <span class="hljs-keyword">get</span> todos =&gt; _todos.value;

  TodosService() {
    listenToReactiveValues([_todos]);
  }

  <span class="hljs-built_in">String</span> _randomId() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">String</span>.fromCharCodes(
      <span class="hljs-built_in">List</span>.generate(<span class="hljs-number">10</span>, (_) =&gt; _random.nextInt(<span class="hljs-number">33</span>) + <span class="hljs-number">80</span>),
    );
  }

  <span class="hljs-keyword">void</span> _saveToHive() =&gt; Hive.box(<span class="hljs-string">'todos'</span>).put(<span class="hljs-string">'todos'</span>, _todos.value);

  <span class="hljs-keyword">void</span> newTodo() {
    _todos.value.insert(<span class="hljs-number">0</span>, Todo(id: _randomId()));
    _saveToHive();
    notifyListeners();
  }

  <span class="hljs-built_in">bool</span> removeTodo(<span class="hljs-built_in">String</span> id) {
    <span class="hljs-keyword">final</span> index = _todos.value.indexWhere((todo) =&gt; todo.id == id);
    <span class="hljs-keyword">if</span> (index != <span class="hljs-number">-1</span>) {
      _todos.value.removeAt(index);
      _saveToHive();
      notifyListeners();
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
  }

  <span class="hljs-built_in">bool</span> toggleStatus(<span class="hljs-built_in">String</span> id) {
    <span class="hljs-keyword">final</span> index = _todos.value.indexWhere((todo) =&gt; todo.id == id);
    <span class="hljs-keyword">if</span> (index != <span class="hljs-number">-1</span>) {
      _todos.value[index].completed = !_todos.value[index].completed;
      _saveToHive();
      notifyListeners();
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
  }

  <span class="hljs-built_in">bool</span> updateTodoContent(<span class="hljs-built_in">String</span> id, <span class="hljs-built_in">String</span> text) {
    <span class="hljs-keyword">final</span> index = _todos.value.indexWhere((todo) =&gt; todo.id == id);
    <span class="hljs-keyword">if</span> (index != <span class="hljs-number">-1</span>) {
      _todos.value[index].content = text;
      _saveToHive();
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    }
  }
}
</code></pre>
<h4 id="heading-understanding-reactive-services-in-stacked">Understanding Reactive Services in Stacked</h4>
<p>The TodosService class declaration comes with a <a target="_blank" href="https://pub.dev/documentation/stacked/latest/stacked/ReactiveServiceMixin-mixin.html">ReactiveServiceMixin</a>. This is where Stacked features start coming in.</p>
<p>With Stacked, services by default are not reactive. However, you need to make a service reactive if any other parts of the project code (other services or ViewModels) have to "react" to changes in the values of the service. </p>
<p>If a service is reactive, that is, has the ReactiveServiceMixin, it means that that service will have at least one <a target="_blank" href="https://pub.dev/documentation/stacked/latest/stacked/ReactiveValue-class.html">ReactiveValue</a> among its properties. It also means that the service has to call <code>[listenToReactiveValues](https://pub.dev/documentation/stacked/latest/stacked/ReactiveServiceMixin/listenToReactiveValues.html)</code> with a list of the reactive values in that service.</p>
<p>The idea behind reactivity is that when the reactive values change (either from user interaction or your backend server), the service can update listeners of that value that there are changes. In turn, these listeners can rebuild UIs just as if <code>setState</code> was called from within the widget.</p>
<h4 id="heading-about-the-todosservice-class">About the TodosService class</h4>
<p>In our case, the TodosService class has only one private reactive  <code>_todos</code> field. <code>_todos</code> keeps a <code>ReactiveValue</code> of TodoList. This private reactive <code>_todos</code> is also given to the list of reactive values to listen to in the constructor (<code>listenToReactiveValues</code>).</p>
<p>This is where Hive comes in. With Hive, you store data as key-value pairs inside boxes. For our app, we are using a 'todos' box. Inside that box, we are using the 'todos' key to retrieve stored <code>todos</code>. </p>
<pre><code class="lang-dart"> Hive.box(<span class="hljs-string">'todos'</span>).<span class="hljs-keyword">get</span>(<span class="hljs-string">'todos'</span>, defaultValue: []).cast&lt;Todo&gt;(),
</code></pre>
<p>The empty list (<code>[]</code>) defaultValue is necessary. For the first time, the Todo App is run on a device that had never stored <code>todos</code> before, so the empty list will be returned instead.</p>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Type_conversion">Casting</a> the retrieved value as a Todo object is also important (<code>.cast&lt;Todo&gt;()</code>). If you omit that step, Flutter will throw errors.</p>
<p>The TodosService class provides a <code>todos</code> getter for accessing the value of the private reactive TodoList (<code>_todos.value</code>).</p>
<pre><code class="lang-dart"><span class="hljs-built_in">List</span>&lt;Todo&gt; <span class="hljs-keyword">get</span> todos =&gt; _todos.value;
</code></pre>
<p>The TodosService class also provides methods for manipulating Todos and their properties (<code>removeTodo</code>, <code>toggleStatus</code>, and <code>updateTodoContent</code>). Each of these methods takes the Todo's <code>id</code> and uses the <code>id</code> to carry out the appropriate action.</p>
<p>Notice that all these methods call the private <code>_saveToHive()</code> method. The reason is whenever <code>todos</code> are updated, the updates are saved to our local storage with Hive. So that if the app is closed and re-opened, the latest state of <code>todos</code> will be loaded back.</p>
<p>Also notice that these methods call <code>notifyListeners()</code>. It is part of the idea behind having only a getter for <code>todos</code> (and no setter). So that whenever there are updates (from these methods), we can call <code>notifyListeners()</code> (if need be) and do appropriate logic (like <code>_saveToHive</code>).</p>
<p><code>notifyListeners()</code> is the equivalent of <code>setState()</code> but this time not inside a StatefulWidget. It tells possible listeners (like the upcoming TodosScreenViewModel) that the <code>todos</code> getter has changed. In turn, the ViewModel will rebuild the UI of its view and render the new state of the <code>todos</code>.</p>
<p>It is worth pointing out that <code>updateTodoContent</code> doesn't call <code>notifyListeners()</code>. We will point out the reason why when we will build the UI of the TodosScreenView.</p>
<p>Notice the private <code>_randomId()</code> method that returns a random string of 10 characters. The <code>newTodo()</code> method uses <code>_randomId()</code> to set the <code>id</code> of a new Todo and inserts that new Todo at the beginning of the TodoList.</p>
<p>If you want new Todos to be added at the end of the list, use <code>_todos.value.add(Todo(id: _randomId()));</code> instead of <code>_todos.value.insert(0, Todo(id: _randomId()));</code> in the <code>newTodo()</code> method.</p>
<p>The entire above pattern of using a service introduces code structure and makes the code easier to read (compared to if everything was in a widget).</p>
<p>This service pattern becomes very useful if we were saving the <code>todos</code> to some external API and fetching them back on app load. But that will be beyond the scope of simply introducing Stacked Architecture.</p>
<h3 id="heading-6-setup-the-service-locator">6. Setup the Service Locator</h3>
<p>Create a new folder with the name <code>app</code> inside the <code>lib</code> folder. Inside this newly created <code>app</code> folder, create a new file with the name <code>locator.dart</code>.</p>
<p>Paste the following into the newly created <code>lib/app/locator.dart</code> file:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it/get_it.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'../services/todos.service.dart'</span>;

<span class="hljs-keyword">final</span> locator = GetIt.instance;

setupLocator() {
  locator.registerLazySingleton(() =&gt; TodosService());
}
</code></pre>
<p>Here, out of convention, we named the <code>GetIt</code> instance <code>locator</code>. After all, that name reflects what it does. It locates services. Other developers might want to use <code>getIt</code> or some other descriptive name for this service locator. That's okay.</p>
<p>We have a <code>setupLocator()</code> function that registers our TodosService with the locator. If we had other services, we would register them here in a similar way. </p>
<p>We need to call the <code>setupLocator()</code> function before the entire Flutter app launches. This way the services are available to any widgets that the Flutter app will need. </p>
<p>Delete the entire contents of the <code>lib/main.dart</code> file and paste the following in there:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:hive_flutter/hive_flutter.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'app/locator.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'models/todo.adapter.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'ui/views/todos_screen_view.dart'</span>;

<span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
  WidgetsFlutterBinding.ensureInitialized();

  <span class="hljs-keyword">await</span> Hive.initFlutter();
  Hive.registerAdapter(TodoAdapter());
  <span class="hljs-keyword">await</span> Hive.openBox(<span class="hljs-string">'todos'</span>);

  setupLocator();

  runApp(<span class="hljs-keyword">const</span> MyApp());
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyApp({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      home: <span class="hljs-keyword">const</span> TodosScreenView(),
      theme: ThemeData.dark(),
      title: <span class="hljs-string">'Flutter Stacked Todos'</span>,
    );
  }
}
</code></pre>
<p><code>WidgetsFlutterBinding.ensureInitialized()</code> is the first statement in the same <code>main</code> method. </p>
<p>In simple terms, we use this statement because Flutter asks us to always include it as the first thing in the <code>main</code> method anytime we want to do some other stuff (like  <code>Hive.initFlutter()</code> or  <code>setupLocator()</code>) before launching the Flutter app with <code>runApp()</code>.</p>
<p>Notice that we are initializing Hive, registering the TodoAdapter, and opening the 'todos' box in the <code>main</code> method. This is the last part of setting up Hive.</p>
<p>Also notice that we are now calling the <code>setupLocator()</code> function inside the <code>main</code> method and before the final <code>runApp</code> call to launch the Flutter app.</p>
<p>We used a dark theme in our MaterialApp by setting the <code>theme</code> property to <code>ThemeData.dark()</code>. This dark theme is just for styling – you can remove it if you prefer the default light theme. You can also customize the app's theme as you wish.</p>
<p>Our <code>lib/main.dart</code> file currently has errors. The error is that the <code>TodosScreenView</code> widget does not exist and we set it as the <code>home</code> property of our MaterialApp. </p>
<p>If you are using a <a target="_blank" href="https://docs.flutter.dev/get-started/editor">Flutter-enabled IDE</a>, you'll notice that the above have been indicated as errors. </p>
<p>No worries, we will create those files now.</p>
<h2 id="heading-how-to-build-the-todo-apps-ui">How to Build the Todo App's UI</h2>
<h3 id="heading-the-different-types-of-viewmodels">The Different Types of ViewModels</h3>
<p>In Stacked, think ViewModels first before their Views. This will help you gather the dependencies that the corresponding view needs before actually building the view.</p>
<p>Stacked comes with different ViewModel types. In Stacked, you have BaseViewModels, ReactiveViewModels, FutureViewModels, StreamViewModels, and MultipleFutureViewModels. Use each one based on your current need. </p>
<p>Use ReactiveViewModels if your View or its ViewModel will need to use reactive values from reactive services. </p>
<p>We will use the reactive ViewModel type for our TodosScreenViewModel. We are using a ReactiveViewModel because we will need the reactive <code>todos</code> getter in the TodosScreen.</p>
<h3 id="heading-1-create-the-todosscreenviewmodel">1. Create the TodosScreenViewModel</h3>
<p>Create a new folder with name <code>ui</code> inside the <code>lib</code> folder. Inside this newly created <code>ui</code> folder, create another folder named <code>todos_screen</code>. Then inside the new <code>todos_screen</code> folder, create a new file with name <code>todos_screen_viewmodel.dart</code>.</p>
<p>Paste the following into the newly created <code>lib/ui/todos_screen/todos_screen_viewmodel.dart</code> file:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked/stacked.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'../../app/locator.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../../models/todo.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../../services/todos.service.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodosScreenViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ReactiveViewModel</span> </span>{
  <span class="hljs-keyword">final</span> _firstTodoFocusNode = FocusNode();
  <span class="hljs-keyword">final</span> _todosService = locator&lt;TodosService&gt;();
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> toggleStatus = _todosService.toggleStatus;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> removeTodo = _todosService.removeTodo;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> updateTodoContent = _todosService.updateTodoContent;

  <span class="hljs-built_in">List</span>&lt;Todo&gt; <span class="hljs-keyword">get</span> todos =&gt; _todosService.todos;

  <span class="hljs-keyword">void</span> newTodo() {
    _todosService.newTodo();
    _firstTodoFocusNode.requestFocus();
  }

  FocusNode? getFocusNode(<span class="hljs-built_in">String</span> id) {
    <span class="hljs-keyword">final</span> index = todos.indexWhere((todo) =&gt; todo.id == id);
    <span class="hljs-keyword">return</span> index == <span class="hljs-number">0</span> ? _firstTodoFocusNode : <span class="hljs-keyword">null</span>;
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">List</span>&lt;ReactiveServiceMixin&gt; <span class="hljs-keyword">get</span> reactiveServices =&gt; [_todosService];
}
</code></pre>
<p>Notice how we have access to the <code>_todosService</code> with the help of <code>locator</code>. We override the <code>reactiveServices</code> getter on ReactiveViewModels and provide the <code>_todosService</code> to this list. </p>
<p>That way, whenever <code>notifyListeners()</code> is called inside TodosService, this TodosScreenViewModel will be notified and it will rebuild the UI as necessary.</p>
<p>From TodosScreenViewModel, we expose the <code>removeTodo</code>, <code>toggleStatus</code>, and <code>updateTodoContent</code> methods from the service to the TodosScreenView (which will come up later).</p>
<p>You might wonder why we need to do this. Why not just expose the service itself or rather access the service from the View or widget itself?</p>
<p>The point here is architectural rules and separation of concerns. Remember that the Stacked architecture states that Views should never access services. </p>
<p>Besides, we are doing this because we are keeping things simple. If the app grows bigger than this and we begin to add features to the views, you will realize that the TodosScreenViewModel will have to do other logic before or after making calls to the service's methods. In that case, we won't do such direct method exposure.</p>
<p>This is evident in the <code>newTodo()</code> method of TodosScreenViewModel. It calls the <code>_todosService.newTodo()</code> function to create a new empty Todo. Then it goes ahead and requests focus on the first or just-created Todo's node (<code>_firstTodoFocusNode.requestFocus()</code>).</p>
<p>That way, the cursor will automatically focus on the text input field of the newly created empty Todo after it is created. You will see this in action when we create the TodosScreenView.</p>
<p>The <code>getFocusNode</code> method returns this <code>_firstTodoFocusNode</code> if the Todo calling it is the first Todo. This call will be made from the UI in the TodosScreenView (coming up later on).</p>
<h4 id="heading-note-on-the-late-keyword">Note on the <code>late</code> keyword</h4>
<p>The <code>late</code> keyword attached to the directly exposed service methods is necessary. </p>
<p>Dart is beautiful. <code>late</code> is a feature from Dart that says we are sure that these methods will be assigned <em>later</em> on (from the service) after the ViewModel has been instantiated.</p>
<p>If you remove the <code>late</code> keyword, Dart will complain with "The instance member '_todosService' can't be accessed in an initializer." This complaint is valid.</p>
<p>The complaint comes up because, when the TodosScreenViewModel has been instantiated, Dart is not sure if, at the time when it needs to instantiate those exposed methods (that had <code>late</code> in front of them), the '_todosService' has completed its initialization to be available for the methods.</p>
<p>Generally, instance members can't self initialize each other. The exception is either using the <code>late</code> keyword (as we did) or doing such initialization in the constructor. </p>
<p>Behind the scenes, the <code>late</code> keyword delays the initialization of the dependent instance members (in this case, the directly exposed methods) till the independent instance member (in this case, _todosService) has completed its initialization.</p>
<p>We didn't do these initializations in the constructor because it will make the code longer. And besides, it has the same effect as using <code>late</code>.</p>
<h3 id="heading-2-create-the-todosscreenview">2. Create the TodosScreenView</h3>
<p>Create a new file with name <code>todos_screen_view.dart</code> inside the <code>lib/ui/todos_screen</code> folder. In other words, the <code>todos_screen_view.dart</code> file should accompany its ViewModel file: <code>todos_screen_viewmodel.dart</code>.</p>
<p>Paste the following into the newly created <code>lib/ui/todos_screen/todos_screen_view.dart</code> file:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked/stacked.dart'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'todos_screen_viewmodel.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodosScreenView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> TodosScreenView({Key? key}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> ViewModelBuilder&lt;TodosScreenViewModel&gt;.reactive(
      viewModelBuilder: () =&gt; TodosScreenViewModel(),
      builder: (context, model, _) =&gt; Scaffold(
        appBar: AppBar(title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Flutter Stacked Todos'</span>)),
        body: ListView(
          padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(vertical: <span class="hljs-number">16</span>),
          children: [
            <span class="hljs-keyword">if</span> (model.todos.isEmpty)
              Opacity(
                opacity: <span class="hljs-number">0.5</span>,
                child: Column(
                  children: <span class="hljs-keyword">const</span> [
                    SizedBox(height: <span class="hljs-number">64</span>),
                    Icon(Icons.emoji_food_beverage_outlined, size: <span class="hljs-number">48</span>),
                    SizedBox(height: <span class="hljs-number">16</span>),
                    Text(<span class="hljs-string">'No todos yet. Click + to add a new one.'</span>),
                  ],
                ),
              ),
            ...model.todos.map((todo) {
              <span class="hljs-keyword">return</span> ListTile(
                leading: IconButton(
                  icon: Icon(
                    todo.completed ? Icons.task_alt : Icons.circle_outlined,
                  ),
                  onPressed: () =&gt; model.toggleStatus(todo.id),
                ),
                title: TextField(
                  controller: TextEditingController(text: todo.content),
                  decoration: <span class="hljs-keyword">null</span>,
                  focusNode: model.getFocusNode(todo.id),
                  maxLines: <span class="hljs-keyword">null</span>,
                  onChanged: (text) =&gt; model.updateTodoContent(todo.id, text),
                  style: TextStyle(
                    fontSize: <span class="hljs-number">20</span>,
                    decoration:
                        todo.completed ? TextDecoration.lineThrough : <span class="hljs-keyword">null</span>,
                  ),
                ),
                trailing: IconButton(
                  icon: <span class="hljs-keyword">const</span> Icon(Icons.horizontal_rule),
                  onPressed: () =&gt; model.removeTodo(todo.id),
                ),
              );
            }),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: model.newTodo,
          child: <span class="hljs-keyword">const</span> Icon(Icons.add),
        ),
      ),
    );
  }
}
</code></pre>
<h4 id="heading-about-the-todosscreenview">About the TodosScreenView</h4>
<p>The first line after the declaration of the <code>build</code> method is a return statement. This statement returns a <code>ViewModelBuilder</code> widget for the TodosScreenViewModel. </p>
<p>This is another aspect of Stacked architecture. It explains what we mean by Views being attached to ViewModels. In essence, we use ViewModelBuilders to render a View.</p>
<p>That way the View has access to the public properties and methods of its ViewModel. Also, calling <code>notifyListeners()</code> inside the ViewModel auto-updates the View's UI. Furthermore, most (if not all) UI logic that is not declarative should be moved to the ViewModel.   </p>
<p>This explains why we shifted the logic of the first Todo's FocusNode to the TodosScreenViewModel.</p>
<p>The <code>body</code> of the TodosScreenView's Scaffold is a <code>ListView</code> for all the Todos gotten from <code>model.todos</code>.</p>
<p>However, the first member of the ListView is a conditional Opacity widget with 0.5 opacity. Its child is a Column for empty state with spacing, a teacup icon, and Text for children.</p>
<p>We used <code>ListTile</code> to display each Todo. It is a convenience widget that takes <code>leading</code>, <code>title</code>, and <code>trailing</code> widgets for the left, center, and right parts of the screen.</p>
<p><strong>Note on Right-To-Left</strong>: ListTile is a good widget, and Flutter is a good framework. They both are internationalized. If this app was run on a device naturally set with a right-to-left locale (in Arabic for example), the app will be mirrored. So the leading, title, and trailing widgets of ListTiles will be right, center, and left parts of the screen instead. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/rtl-sample.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The <code>leading</code> widget on the ListTile is an IconButton whose icon is either an empty or checked circle depending on if the Todo is completed or not. The <code>onPressed</code> callback on the IconButton toggles the status of the Todo.</p>
<p>The <code>title</code> (center) widget on the ListTile is a TextField with no decoration. No decoration here means it has no backgrounds, borders, or underlines. The aim is to give the user the feeling that they can just read their Todo content, while at the same time, the ability to edit the content is in the same place. </p>
<p>The <code>[TextEditingController](https://api.flutter.dev/flutter/widgets/TextEditingController-class.html)</code> given to the TextField is used to set the text content on the field from the content of the Todo. Setting <code>maxLines</code> to null on the TextField is telling Flutter that text in the TextField can span across multiple lines. </p>
<p>The <code>onChanged</code> callback updates the text content of the attached Todo. This callback is called for every keystroke or edit of text. We are doing this to keep all Todos always in sync with the UI.</p>
<p>We didn't call <code>notifyListeners()</code> in this callback (<code>updateTodoContent</code>) in the TodosService to prevent the cursor from jumping (given that we are making the call for each keystroke). </p>
<p>If <code>notifyListeners()</code> was called in this callback, the UI would be rebuilt each time, and the cursor would keep jumping back to the start of the TextField after each keystroke.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/todo_jumping_cursor-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-3-run-the-todo-app">3. Run the Todo App</h3>
<p>If you are using a Flutter-enabled IDE, you should be able to run the above app on a preconfigured device already. </p>
<p>If you are not, no problem – run the following command in the same terminal. Or rather, make sure that you are in the same project folder in the terminal, then run the command:</p>
<pre><code class="lang-bash">flutter run
</code></pre>
<p>It should run the Flutter app on an available device or ask you to choose a device and then run the application on it. </p>
<p>You should be able to create Todos, mark them as complete, and remove them.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/todo_usage.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you've gotten this far, Congratulations on building a Todo App with the Stacked Architecture!</p>
<p>For a mini-recap, the files inside your <code>lib</code> folder now should be:</p>
<ul>
<li><code>app/locator.dart</code></li>
<li><code>models/todo.adapter.dart</code></li>
<li><code>models/todo.dart</code></li>
<li><code>services/todos.service.dart</code></li>
<li><code>ui/todos_screen/todos_screen_view.dart</code></li>
<li><code>ui/todos_screen/todos_screen_viewmodel.dart</code></li>
<li><code>main.dart</code></li>
</ul>
<p><a target="_blank" href="https://github.com/obumnwabude/flutter_stacked_todo/tree/freecodecamp">You can get the final code on GitHub here</a>.</p>
<h2 id="heading-note-on-other-stacked-features">Note on Other Stacked Features</h2>
<p>There is more to using the Stacked architecture. The above Todo App was one-screen only and it did not need any navigation.</p>
<p>You will need to configure Navigation or Routing in most if not all applications you'll build with Flutter. Navigation is a necessity once you have more than one screen in the Flutter App.</p>
<p>Stacked lets you configure an <code>@StackedApp</code> decoration on an empty Dart class. This decoration can take routes and dependencies info as in the following snippet:</p>
<pre><code class="lang-dart"><span class="hljs-meta">@StackedApp</span>(
  routes: [MaterialRoute(page: TodosScreenView, initial: <span class="hljs-keyword">true</span>)],
  dependencies: [LazySingleton(classType: TodosService)],
)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">App</span> </span>{
  <span class="hljs-comment">// This class has no puporse besides housing the annotation that</span>
  <span class="hljs-comment">// generates the required functionality.</span>
}
</code></pre>
<p>You'd then need to add <code>build_runner</code> and <code>stacked_generator</code> packages to the Flutter project. Then in development, you keep the following command running:</p>
<pre><code class="lang-bash">flutter pub run build_runner build --delete-conflicting-outputs
</code></pre>
<p>This command will generate the contents of the <code>setupLocator()</code> function for you. And it is the generated file (and function) that you would add to the main method in <code>lib/main.dart</code>.</p>
<p>Furthermore, the Stacked architecture also has a <code>[stacked_services](https://pub.dev/packages/stacked_services)</code> package. This package helps with configuring crucial services like bottom sheet, dialog, navigation, and snack bar, without the BuildContext, inside ViewModels. </p>
<p>All these introduce convenience and give you a better developer experience while using Stacked.</p>
<p>But these features would be overkill for the simple Todo App we just built. That's why we didn't include them.</p>
<p>The idea is to understand the fundamentals behind the Stacked architecture and be able to use the architecture in your <strong>large-scale</strong> Flutter projects.</p>
<p>The stress is on large-scale. In reality, if you are building anything simple, you won't need to use <em>any</em> state management architecture. Even a <em>simple</em> Todo App. </p>
<p>But in this article, I aimed to get you started with Stacked Architecture and I needed something simple (like this Todo App) to explain the concepts.</p>
<p>Of course, you can still use Stacked to build simple things. That's what we just did with a Todo App. But understand that you will enjoy the benefits of Stacked when you start adding more features like saving todos to some server, authentication, grouping todos into categories, and more in the Todo App.</p>
<h2 id="heading-summary">Summary</h2>
<p>State Management architectures are good ways to structure Flutter projects. They are optional but can become a necessity if you are building a complex application or if more than one person is contributing to the same codebase.</p>
<p>These architectures find a way to update the UI without the default calls to <code>setState()</code>. Stacked is one such architecture. It uses <code>notifyListeners()</code> to update the UI. </p>
<p>A key feature of Stacked is carrying out crucial services like navigation without the BuildContext. We didn't explore that feature in this Todo App. But have it in mind as you go further with using Stacked.</p>
<p>Your Stacked apps should have widgets inside View files. Views should be tied to ViewModels and UI logic should take place here. Services should be used for the app's business logic and APIs. They should be used by other services or ViewModels.</p>
<p>We've used this simple 3-entity structure to build a functional Todo App. You can use the same pattern to build bigger and better apps. Better still, you can refactor existing applications to follow this pattern and have cleaner code.</p>
<p>We also used Hive to save and retrieve Todos after closing and re-opening the Flutter application. Hive is a lightweight package and can serve similar purposes in your apps.</p>
<p><a target="_blank" href="https://play.google.com/store/apps/details?id=org.freecodecamp">The freeCodeCamp mobile app</a> is built with Flutter. It is open-source and was built using Stacked architecture. <a target="_blank" href="https://github.com/freeCodeCamp/mobile">You can contribute to the freeCodeCamp app here.</a> </p>
<p>Cheers!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Why You Should Use Flutter for Your Projects ]]>
                </title>
                <description>
                    <![CDATA[ Flutter is a UI toolkit and SDK which you can use to build applications. Flutter is open source and you can use it to build highly performant mobile and desktop apps.  In this article, I'll explain in detail the various benefits of using Flutter so y... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/why-you-should-use-flutter/</link>
                <guid isPermaLink="false">66b9ff52052fa53219e0a31c</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Tue, 12 Jul 2022 20:12:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/pexels-pixabay-326055.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Flutter is a UI toolkit and SDK which you can use to build applications. Flutter is open source and you can use it to build highly performant mobile and desktop apps. </p>
<p>In this article, I'll explain in detail the various benefits of using Flutter so you can decide whether to use it for your next project.</p>
<h2 id="heading-top-benefits-of-flutter">Top Benefits of Flutter</h2>
<ol>
<li><a class="post-section-overview" href="#heading-flutter-is-cross-platform-1">Flutter is Cross-Platform</a></li>
<li><a class="post-section-overview" href="#heading-flutter-is-cross-platform-1">How Flutter is Cross-Platform</a></li>
<li><a class="post-section-overview" href="#heading-flutter-has-a-powerful-ui-engine">Flutter Has a Powerful UI Engine</a> </li>
<li><a class="post-section-overview" href="#heading-how-flutter-renders-uis">How Flutter Renders UIs</a></li>
<li><a class="post-section-overview" href="#heading-flutter-has-great-state-management">Flutter Has Great State Management</a> </li>
<li><a class="post-section-overview" href="#heading-flutter-provides-a-great-developer-experience">Flutter Provides a Great Developer Experience</a> </li>
<li><a class="post-section-overview" href="#heading-flutter-has-a-wonderful-developer-community">Flutter Has a Wonderful Developer Community</a></li>
<li><a class="post-section-overview" href="#please-use-flutter">Summary</a></li>
</ol>
<p>Now let's look at each of these features in more detail.</p>
<h2 id="heading-flutter-is-cross-platform">Flutter is Cross-Platform</h2>
<p>Software is cross-platform when it is available for different Operating Systems. You want your product to have such cross-platform ability so users on any device can use your product comfortably.</p>
<p>Having support for desktop, mobile, and web platforms is tough. For desktop, you will need to write code for macOS (with Swift), Linux (with C), and Windows (with C++). For mobile, you will need to write code for Android (with Kotlin/Java and XML) and iOS (with Swift).</p>
<p>To make your product accessible as a website, you have to use HTML, CSS, and JavaScript. Or, use any frontend JavaScript framework like Angular, React, or Vue.</p>
<p>Making applications for the various desktop and mobile operating systems requires separate SDKs and skillsets. In the past, you would've needed to hire developers who were proficient at each platform to implement your app on each of those platforms. This is expensive. </p>
<p>To add a feature, you would've needed to update the code across all these platforms which is tedious.</p>
<h3 id="heading-how-flutter-is-cross-platform">How Flutter is cross-platform</h3>
<p>Flutter code can run on desktop, mobile, and web platforms. So, you don't need to hire developers for each platform. You need to write the code only once in Flutter and you can rest assured that the app will work across the other platforms. So, Flutter is cheap.</p>
<p>Adding features to your app is fast because you only have to make code updates once in Flutter and that's all.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/demo.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Same Flutter app working on desktop, mobile, and web.</em></p>
<p>The Flutter framework gives you APIs for painting and events (through widgets). It also provides APIs for platform-specific services (through method channels). So Flutter <em>exposes</em> <em>everything</em> to you to build the app in any way you want it. That's how cross-platform works.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/Screenshot--224--2.png" alt="Image" width="600" height="400" loading="lazy">
<em>Adapted from https://youtu.be/l-YO9CmaSUM</em></p>
<p>Don't worry about performance. Naturally, Dart can compile to native code. So, the performance of Flutter apps is close (if not equal to) that of native apps. </p>
<p><a target="_blank" href="https://dart.dev/tools/dart-compile">Dart's Ahead-Of-Time compiler</a> is used to bundle the Flutter code. It is used when you launch the <code>flutter build</code> command. During the build step, only the app and the Flutter engine are shipped. This makes the built app small in size (very close to a native app). </p>
<h2 id="heading-flutter-has-a-powerful-ui-engine">Flutter Has a Powerful UI Engine</h2>
<p>UI rendering in Flutter is pixel-perfect. As a Flutter developer, you are in charge of every pixel painted on the device screen. </p>
<p>Flutter achieves this with the help of its engine. The Flutter engine is in charge of interpreting Flutter code to exactly what is drawn on the device's screen. Each Flutter app (built for any given platform) contains the engine which handles painting at runtime.</p>
<p>This also explains how Flutter is efficiently cross-platform, in terms of UI rendering. But this is more about the efficiency of the engine. The Flutter engine uses <a target="_blank" href="https://skia.org/">Skia</a> for graphics. Skia is a 2D graphics library that handles graphics rasterization on various hardware and software.</p>
<p>The Flutter engine doesn't only contain Skia. It also has implementations of Flutter's core APIs (like texts and network I/O) at basic levels.</p>
<p>The Flutter engine is so powerful that it can efficiently re-render UIs at a speed of <strong>60 frames per second</strong> (60 fps). So when there are UI changes or animations, the engine makes them so fast as if it was a native application.</p>
<h3 id="heading-how-flutter-renders-uis">How Flutter Renders UIs</h3>
<p>Flutter uses composition aggressively. Composition here means widgets have widgets, which in turn have widgets, and so on. Everything in Flutter is a widget and Flutter UI code is essentially a big tree of widgets. </p>
<p>Behind the scenes, Flutter keeps <strong>3 separate trees</strong> for the UI. We normally code the outermost widget tree. In turn, the Flutter engine creates an <em>Element</em> and a <em>RenderObject</em> tree based on the widget tree.</p>
<p>In essence, each widget has its own corresponding Element and RenderObject. These last two entities are in charge of the actual representation of widgets on the device screen. Widgets themselves just hold the UI properties (like color, padding, shadows, and so on) that we set ourselves.</p>
<p>When UI is re-rendered, Flutter always destroys and rebuilds any changed widget (because widgets are immutable). However, it only replaces a linked Element or RenderObject <em>if need be.</em></p>
<p>These extra UI trees explain how the widget tree is destroyed and rebuilt in each frame of the 60 fps rendering speed and yet the UI is re-rendered properly. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/widget_tree_1.png" alt="Image" width="600" height="400" loading="lazy">
<em>The 3 UI trees, adapted from https://youtu.be/996ZgFRENMs</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/widget_tree_2.png" alt="Image" width="600" height="400" loading="lazy">
<em>The 3 UI trees with the Center widget as an example. Adapted from https://youtu.be/996ZgFRENMs</em></p>
<h2 id="heading-flutter-has-great-state-management">Flutter Has Great State Management</h2>
<p>State refers to data that your application needs to render its UI at any point in time. It could be user-generated or from your servers or backend. </p>
<p>In general, Flutter has two types of widgets: StatelessWidgets and StatefulWidgets. Anytime you will need to update the UI based on state, simply use a StatefulWidget ahead of time. Then to update the UI, call <code>setState()</code> anywhere inside the State class of a StatefulWidget and Flutter will rebuild the UI tree.</p>
<p>This <code>setState()</code> mechanism turns out to be the most efficient out there. It is also the React style of updating UI based on state. UI programming should be declarative and <code>setState()</code> simply advocates that.</p>
<p>You should call <code>setState()</code>  after asynchronous code has been executed (if any). This is because it is part of the main UI thread and blocking calls can hijack the UI rendering process.</p>
<p>If you have processes that are highly CPU intensive in a Flutter app, consider using <a target="_blank" href="https://dart.dev/guides/language/concurrency#how-isolates-work">Dart Isolates</a> to carry out those processes, then call <code>setState()</code> with the data you get when processing is complete.</p>
<p>Sometimes, you may need access to state not scoped to a particular widget. At that point, you need a State Management architecture. There are a good number of them to choose from. These architectures provide state at an app-wide level without reconfiguring in each widget.</p>
<p><a target="_blank" href="https://docs.flutter.dev/development/data-and-backend/state-mgmt/options">Available options</a> include Provider, Riverpod, Redux, InheritedWidget, <a target="_blank" href="https://filledstacks.com/">Stacked</a>, and many more.</p>
<p>Another advantage of these architectures is that a good number of them permit good <a target="_blank" href="https://en.m.wikipedia.org/wiki/Separation_of_concerns">separation of concerns</a> and <a target="_blank" href="https://en.m.wikipedia.org/wiki/Dependency_injection">dependency injection (or inversion of control)</a>.</p>
<p>In simpler terms, separation of concerns gives you the ability to write UI-specific code separate from logic-specific code. This way, if you want to debug, or change the packages or APIs you're using, you'll do them easily from one place (without fear of damaging the codebase). Besides, it promotes clean code too.</p>
<p>Dependency Injection involves the use of services or something similar to obtain what widgets need to work without the widgets configuring the services themselves. Some architectures like Stacked permit you to manage app-wide states without the BuildContext. </p>
<p>This pattern is useful because, in StatelessWidgets, the BuildContext is available only within the build method and you might need to use stuff outside it. So you could do other things like BottomSheet, Navigation, Toast, and so on without BuildContext.</p>
<h2 id="heading-flutter-provides-a-great-developer-experience">Flutter Provides a Great Developer Experience</h2>
<p>There are many reasons the developer experience with Flutter is awesome. Here are a few of them:</p>
<h3 id="heading-flutter-has-only-one-programming-language-dart">Flutter has only one programming language – Dart.</h3>
<p>Programming on its own is a demanding activity. Frameworks, libraries, and tools shouldn't make coding tougher. </p>
<p>When coding for some platforms you usually write code in more than one programming language. For example, while coding for frontend web, you will alternate between HTML, CSS, and JavaScript files to manage a given portion of your webpage.</p>
<p>Flutter makes coding easy because you write in just one programming language: Dart. So most of the time, the logic and UI properties for a given portion of your Flutter app will be in the same Dart file. </p>
<p>Also, Flutter and Dart are written in plain English. Widget names and their properties reflect what they are. While coding Flutter, you are less likely to have any headaches understanding a given widget and or its use case(s).</p>
<h3 id="heading-flutter-has-hot-reloading">Flutter has hot-reloading.</h3>
<p>Dart is a modern programming language that comes with <a target="_blank" href="https://dart.dev/tools/dart-compile">an Ahead-Of-Time (AOT) and a Just-In-Time (JIT) compiler.</a> </p>
<p>The JIT compiler gives the feel that Dart is rather interpreted in runtime. In other words, it makes it possible for changes to Dart files to reflect immediately without needing to recompile the file, as is the case with some programming languages like C or Java. </p>
<p>Flutter leverages the JIT for development. Flutter ships a <a target="_blank" href="https://mrale.ph/dartvm">Dart VM</a> to the platform in which the app's code is hosted. That way, when you launch the <code>flutter run</code> command, pressing <code>r</code> in that terminal ships in the changes in dart files, without recompiling the whole app. This instantaneous, yet <em>cross-platform</em> change-reflecting feature of Flutter is called <strong>hot-reloading</strong>.</p>
<p>Flutter also has <strong>hot-restarting</strong>. Hot restarting is achieved by pressing <code>R</code> (uppercase instead of lowercase this time). <em>Hot restarting</em> restarts the app entirely.</p>
<p>You need to do a hot-restart instead if you change some important parts of the Flutter code. For example, changing <code>class</code> declarations or their superclasses will need a hot restart.</p>
<h3 id="heading-flutter-is-universal">Flutter is universal.</h3>
<p>Flutter is available for each Operating System. So you won't need to migrate or use a particular Operating System to create apps with Flutter as is the case with iOS apps (where you need an Apple computer).  </p>
<p>Flutter smoothly integrates with popular IDEs. Android Studio, IntelliJ, and VS Code (Visual Studio Code) have plugins or extensions for Flutter. So you can manipulate Flutter commands directly in the IDE without using the terminal. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/demo2.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Editing Flutter as an Android app in Android Studio</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/demo3.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Editing Flutter as a Windows app in VS Code</em></p>
<p>Android Studio is relatively demanding in terms of computing resources. If your laptop <a target="_blank" href="https://developer.android.com/studio#system-requirements-a-namerequirementsa">is not up to 8GB RAM</a>, don't feel left out. </p>
<p>Setting up Flutter auto-detects your Operating System and browsers. Flutter is available for the web (to run Flutter apps in browsers). So you can test Flutter code in your browser or as a desktop app if RAM or CPU capacity could be a problem. </p>
<p>There is also <a target="_blank" href="https://dartpad.dev">dartpad.dev</a>, <a target="_blank" href="https://flutlab.io">flutlab.io</a>, and <a target="_blank" href="https://flutterflow.io">flutterflow.io</a> that let you create Flutter apps online, in the browser. You will find these useful as they do not require as many computing resources as running Flutter for Android or iOS. </p>
<h3 id="heading-flutter-devtools">Flutter DevTools</h3>
<p>Dart naturally comes with a set of utilities for optimizing and debugging Dart code. This suite of tools is accessible from the browser or IDE you are using. While coding Flutter, using DevTools will shorten your coding time and give you deep insights into your app. </p>
<p><a target="_blank" href="https://docs.flutter.dev/development/tools/devtools/overview">Flutter DevTools</a> include indispensable tooling like an inspector, a debugger, a performance monitor, a network monitor, logger, and more.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/Screenshot--234-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Flutter DevTools</em></p>
<h2 id="heading-flutter-has-a-wonderful-developer-community">Flutter Has a Wonderful Developer Community</h2>
<blockquote>
<p>The Flutter framework is relatively small... – <a target="_blank" href="https://docs.flutter.dev/resources/architectural-overview">from the Flutter Docs</a> </p>
</blockquote>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/flutter_is_smalll.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The Flutter team themselves acknowledge the community. In itself, the Flutter framework is relatively small compared to the entire Flutter ecosystem. Flutter is Open Source. </p>
<p>Around Flutter there are so many developers that have extended the framework. These developers have published more than 24,000 packages on <a target="_blank" href="https://pub.dev">pub.dev</a>. Each package has at least one widget you can import to your Flutter app. </p>
<p>If you have issues with Flutter or come across errors while using Flutter, you will find a solution once you search about it online. Chances are there are <a target="_blank" href="https://github.com/flutter/flutter/issues">GitHub issues</a> or <a target="_blank" href="https://stackoverflow.com/questions/tagged/flutter">Stackoverflow questions</a> on what you are looking for. Stackoverflow has a lot of Flutter developers willing to help you, so don't hesitate to ask questions with the Flutter tag. </p>
<p><a target="_blank" href="https://flutter.dev/community">Flutter Groups</a> and <a target="_blank" href="https://developers.google.com/community/gdg">GDG</a>s are tech communities, found in various countries. Annually, these communities organize a Flutter-only event commonly known <a target="_blank" href="https://www.flutterfestival.com/">FlutterFest</a>. Some FlutterFest events are in person while some are virtual. These events bring the community members together for Flutter, Flutter, and more Flutter.</p>
<p>That said, you should use Flutter because you are not alone. You are confident that there are people around you that equally use it too and can help you when you are in need. </p>
<p>Also, maintenance and future support for Flutter look promising. <a target="_blank" href="https://fuchsia.dev">Fuchsia</a> is an upcoming open-source operating system. Asides from the current cross-platform support, Flutter for Fuchsia is available.</p>
<h2 id="heading-summary">Summary</h2>
<p>In summary, use Flutter because of the many benefits you will gain. </p>
<p>You will have a cross-platform application whose UI is pixel perfect and state properly managed. You will also enjoy your development experience and will leverage the Flutter community.  </p>
<p>Cheers to the many Flutter apps you will build.</p>
<h2 id="heading-helpful-resources">Helpful Resources</h2>
<ul>
<li><a target="_blank" href="https://youtu.be/l-YO9CmaSUM">How is Flutter different for app development</a></li>
<li><a target="_blank" href="https://youtu.be/996ZgFRENMs">How Flutter renders Widgets</a> </li>
<li><a target="_blank" href="https://docs.flutter.dev/resources/architectural-overview">Flutter Architectural Overview</a> </li>
<li><a target="_blank" href="https://docs.flutter.dev/development/data-and-backend/state-mgmt/options">List of state management approaches</a> </li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-implement-any-ui-in-flutter">How to Implement any UI in Flutter</a> </li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement Any UI in Flutter ]]>
                </title>
                <description>
                    <![CDATA[ In this article, you will learn how to convert any user interface image, piece, or screen into Flutter code. This is not a tutorial on building an app. It is rather a guide that will help you implement any UI you come across into an app you already h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-any-ui-in-flutter/</link>
                <guid isPermaLink="false">66b9ff4c85b28e0b24537e57</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mobile app development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UI Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ User Interface ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Obum ]]>
                </dc:creator>
                <pubDate>Wed, 08 Jun 2022 15:22:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/06/anyuicover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, you will learn how to convert any user interface image, piece, or screen into <a target="_blank" href="https://flutter.dev">Flutter</a> code.</p>
<p>This is not a tutorial on building an app. It is rather a guide that will help you implement any UI you come across into an app you already have. This tutorial also explains a wide variety of UI concepts in Flutter.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-flutter">What is Flutter?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-widgets-in-flutter">Widgets in Flutter</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-widget-tree">The Widget Tree</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-any-ui-in-flutter">How to Implement any UI in Flutter</a><br>  <a class="post-section-overview" href="#heading-1-write-your-code-starting-at-the-top-left-and-move-down-to-the-bottom-right">1. Implement Top Left; Down Right</a><br>  <a class="post-section-overview" href="#heading-2-choose-a-widget">2. Choose a Widget</a><br>  <a class="post-section-overview" href="#heading-3-use-widget-groups">3. Use widget groups</a><br>  <a class="post-section-overview" href="#heading-a-columnrow">a. Column/Row</a><br>  <a class="post-section-overview" href="#heading-b-stack-widget">b. Stack Widget</a><br>  <a class="post-section-overview" href="#heading-4-create-custom-widgets">4. Create custom widgets</a><br>  <a class="post-section-overview" href="#heading-5-add-more-customization">5. Add more customization</a><br>  <a class="post-section-overview" href="#heading-a-container-widget">a. Container Widget</a><br>  <a class="post-section-overview" href="#heading-b-gesturedetector-inkwell">b. GestureDetector / InkWell</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-scrolling-interfaces">How to Implement Scrolling Interfaces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-about-custompaint">About CustomPaint</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-what-is-flutter">What is Flutter?</h2>
<blockquote>
<p>Flutter is an open source framework by Google for building beautiful, natively compiled, multi-platform applications from a single codebase. – (s<a target="_blank" href="https://flutter.dev">ource: flutter.dev</a>)</p>
</blockquote>
<p>In Flutter, contrary to most frameworks, <a target="_blank" href="https://dart.dev">Dart</a> is the only programming language you use to code. This is an underemphasized benefit of Flutter. Especially for a tool that can build desktop, mobile, and web applications.</p>
<p>Most <a target="_blank" href="https://en.wikipedia.org/wiki/User_interface_design">UI</a> platforms use more than one language. For example, in front-end web development, you have to write <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML">HTML</a>, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS">CSS</a>, and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript">JavaScript</a>. For <a target="_blank" href="https://developer.android.com/">Android</a>, you have to write <a target="_blank" href="https://developer.android.com/kotlin">Kotlin</a> (or <a target="_blank" href="https://developer.android.com/studio/write/java8-support">Java</a>) and <a target="_blank" href="https://developer.android.com/guide/topics/ui/declaring-layout#write">XML</a>. But in Flutter, it's just one language: Dart.</p>
<p>Coupled with the only-one-programming-language benefit, Flutter is simple because everything in Flutter is a widget. For example <a target="_blank" href="https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html">AnimatedWidget</a>, <a target="_blank" href="https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html">BottomNavigationBar</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container-class.html">Container</a>, <a target="_blank" href="https://api.flutter.dev/flutter/material/Drawer-class.html">Drawer</a>, <a target="_blank" href="https://api.flutter.dev/flutter/material/ElevatedButton-class.html">ElevatedButton</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/FormField-class.html">FormField</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Image-class.html">Image</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Opacity-class.html">Opacity</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Padding-class.html">Padding</a>, ...</p>
<p>This is part of what makes Flutter easy to use – it's basically plain English. <a target="_blank" href="https://docs.flutter.dev/development/ui/widgets">Widget</a> names reflect what they are and their properties are easy to understand.</p>
<h2 id="heading-widgets-in-flutter">Widgets in Flutter</h2>
<p>A widget is a Dart <a target="_blank" href="https://dart.dev/samples#classes">class</a> that either extends <a target="_blank" href="https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html">StatefulWidget</a> or <a target="_blank" href="https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html">StatelessWidget</a>.</p>
<p>Your local <a target="_blank" href="https://docs.flutter.dev/get-started/install">Flutter installation</a> comes with several widgets. To check out the widgets available by default, open the packages folder of your Flutter installation in your preferred editor. Then search across all files for "extends StatefulWidget" and "extends StatelessWidget" and take note of the number of results.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/flutter-packages-widget-count.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>As of <a target="_blank" href="https://docs.flutter.dev/development/tools/sdk/release-notes/release-notes-2.10.0">Flutter 2.10</a>, you will get <strong>408</strong> StatefulWidgets and <strong>272</strong> StatelessWidgets. That is a total of <strong>680 widgets</strong> available for you to use and implement UIs.</p>
<p>These widgets typically have all you need. But at times they may not be enough. <a target="_blank" href="https://pub.dev">pub.dev</a>, Dart and Flutter's package manager, have many more widgets you can use to implement UIs.</p>
<p>It is difficult to count the widgets in pub.dev. But searching an empty string (don't enter anything in the search bar and then press the search icon) and setting the SDK to Flutter returns the current total number of published packages.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/pub.dev-widget-count.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>At the time of writing, there are more than 23000 Flutter packages in pub.dev. Each package has <em>at least one</em> widget. This means that you have more than 23000 widgets from pub.dev to implement, in addition to the available 680. This means that you can really implement any UI you want easily in Flutter.</p>
<p>Adding to the many available widgets, you can also create your own widgets as you implement UIs.</p>
<h2 id="heading-the-widget-tree">The Widget Tree</h2>
<p>The following is part of the code you get when you create a new Flutter project and remove the comments:</p>
<pre><code class="lang-dart"> <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &lt;Widget&gt;[
            <span class="hljs-keyword">const</span> Text(
              <span class="hljs-string">'You have pushed the button this many times:'</span>,
            ),
            Text(
              <span class="hljs-string">'<span class="hljs-subst">$_counter</span>'</span>,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: <span class="hljs-string">'Increment'</span>,
        child: <span class="hljs-keyword">const</span> Icon(Icons.add),
      ),
    );
  }
</code></pre>
<p>The parent <code>Scaffold</code> takes the <code>appBar</code>, <code>body</code>, and <code>floatingActionButton</code> parameters. In turn, the <a target="_blank" href="https://api.flutter.dev/flutter/material/AppBar-class.html">AppBar</a> also takes a <code>title</code> parameter that has a <code>Text</code> value.</p>
<p><code>body</code> takes a <code>Center</code> value that has a <code>Column</code> <code>child</code>. The <code>Column</code> in turn has two <code>Text</code>s as <code>children</code>. The <code>FloatingActionButton</code> takes the <code>onPressed</code> callback, 'Increment' <code>tooltip</code>, and an <code>Icon</code> for a <code>child</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot--142--2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Flutter widget tree breakdown</em></p>
<p>This is a simple widget tree. It has parents and descendants. <code>child</code> and <code>children</code> are common properties of most Flutter widgets. As widgets continuously take more widget children, your app gradually grows into a large widget tree.</p>
<p>As you implement UIs in Flutter, bear in mind that you are building a widget tree. You will notice that your code indents inwards from the left margin. It seems to develop some kind of virtual greater than sign (of empty space) at the left.</p>
<p><strong>Note:</strong> Huge indentation levels are a sign that you need to refactor your code. It means that you need to extract some widget hierarchy into a separate widget.</p>
<h2 id="heading-how-to-implement-any-ui-in-flutter">How to Implement Any UI in Flutter</h2>
<h3 id="heading-1-write-your-code-starting-at-the-top-left-and-move-down-to-the-bottom-right">1. Write your code starting at the top left and move down to the bottom right</h3>
<p>You'll implement the UI widget after widget according to each element's position in the UI. So you will first write code for things that appear at the top of the UI. Then you keep writing code for the other items moving down the page until you reach the bottom of that UI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screenshot--143-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is intuitive.</p>
<p>On the horizontal axis, go from left to right. If need be, or if it is a <a target="_blank" href="https://medium.com/@carlolucera/flutter-and-directionality-d9ac42197fb8">right-to-left</a> UI, then implement it from right to left instead.</p>
<h3 id="heading-2-choose-a-widget">2. Choose a Widget</h3>
<p>Next you'll need to logically determine the widget you want to use for a given UI element. At a bare minimum, for a given UI element, you will use simple widgets you're familiar with based on what their names say they do.</p>
<p>Chances are the name of what the UI component looks like is the name of the widget. If you find it hard to make a choice, a quick online search will give you the answers. Flutter has a great online community.</p>
<h3 id="heading-3-use-widget-groups">3. Use widget groups</h3>
<p>If a group of UI items is arranged vertically, one after another, use a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Column-class.html">Column</a>. If they are arranged horizontally, one after another, use a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Row-class.html">Row</a>. If they are placed on top of each other, use a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Stack-class.html">Stack</a>, with the floating widgets wrapped in <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Positioned-class.html">Positioned</a> widgets.</p>
<h4 id="heading-a-columnrow">a. Column/Row</h4>
<p>Inside a Column or Row, you can change or adjust how the widgets will align themselves on the main or cross axis. Use their <a target="_blank" href="https://api.flutter.dev/flutter/rendering/CrossAxisAlignment.html">CrossAxisAlignment</a> and <a target="_blank" href="https://api.flutter.dev/flutter/rendering/MainAxisAlignment.html">MainAxisAlignment</a> properties for such adjustments.</p>
<p>For the cross axis, you can align to center, end, start, and stretch. For the main axis, you can align to center, end, space around, space between, space evenly, and end.</p>
<p>In a <code>Column</code>, the vertical axis is the main axis while the horizontal axis is the cross axis. In a <code>Row</code>, the horizontal axis is the main axis while the vertical axis is the cross axis.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/1Untitled-1-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Adapted from https://arzerin.com/2019/11/20/flutter-column/</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/05/Untitled-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Adapted from https://arzerin.com/2019/11/20/flutter-row/</em></p>
<p>In Columns and Rows, if you want a particular child widget to take as much available space as possible, wrap that widget inside an <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Expanded-class.html">Expanded</a> widget. If you are familiar with web frontend, you'll notice that Columns and Rows are like <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex">display: flex;</a> in CSS.</p>
<h4 id="heading-b-stack-widget">b. Stack Widget</h4>
<p>With <code>Stack</code>, the last widget(s) in the <code>children</code>'s list appears on top of the earlier children.</p>
<p>You might have to edit the Stack's <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Stack/alignment.html">alignment</a> to indicate the relative positions of the widgets. Like <a target="_blank" href="https://api.flutter.dev/flutter/painting/AlignmentDirectional/topCenter-constant.html">topCenter</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/AlignmentDirectional/center-constant.html">center</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/AlignmentDirectional/bottomEnd-constant.html">bottomEnd</a>, and so on.</p>
<p>The Stack's size is calculated based on non-positioned widgets (Widgets in the children list not wrapped in a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Positioned-class.html">Positioned</a> parent). When coding, remember that your Stack should either have at least one non-positioned widget, or it should be wrapped in a parent widget that explicitly sets the Stack's size.</p>
<p>Positioned takes any or all of <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Positioned/bottom.html">bottom</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Positioned/top.html">top</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Positioned/left.html">left</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Positioned/right.html">right</a>. They set the child's position relative to the Stack. Negative values move the child in the opposite direction. However, negative values clip parts of the child out. Use <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Stack/clipBehavior.html">clipBehavior: Clip.none</a> on the Stack to show all the parts of the positioned widget.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/Screenshot--148-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Full code</em> <a target="_blank" href="https://gist.github.com/obumnwabude/de06d67b7e636dfb8ae1b852b97624b4"><em>here</em></a><em>.</em></p>
<h3 id="heading-4-create-custom-widgets">4. Create custom widgets</h3>
<p>As you build the widget tree, you will notice two things:</p>
<ol>
<li><p>Either a chunk of the tree grows too big and it is a logical unit on its own.</p>
</li>
<li><p>Or some chunks or sets of widgets might repeat themselves with slight changes.</p>
</li>
</ol>
<p>These are two indications that you should <a target="_blank" href="https://en.wikipedia.org/wiki/Code_refactoring">refactor</a> your code. It means that you should extract out those widgets and define them in another Dart file.</p>
<p>Your <a target="_blank" href="https://docs.flutter.dev/get-started/editor">code editor</a> will help you with refactoring. With or without the editor, all you need to do is:</p>
<ol>
<li><p>Create a new Dart file. The file name should reflect the new widget's name.</p>
</li>
<li><p>Create a new class that extends StatefulWidget or StatelessWidget, depending on if the new widget has <a target="_blank" href="https://api.flutter.dev/flutter/widgets/State-class.html">State</a> or not.</p>
</li>
<li><p>Then return the widget chunk from a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/StatelessWidget/build.html">build</a> method.</p>
</li>
<li><p>(Optional) If need be, your new Dart class can take positional or named parameters to its constructor to customize the widget's look.</p>
</li>
</ol>
<pre><code class="lang-dart"><span class="hljs-comment">// in counter_display.dart</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CounterDisplay</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Column(
        mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(<span class="hljs-string">'You have pushed the button this many times:'</span>),
        Text(<span class="hljs-string">'<span class="hljs-subst">$counter</span>'</span>, style: TextStyle(fontSize: <span class="hljs-number">24</span>)),
      ],
    );
  }
}

<span class="hljs-comment">// in main.dart</span>
<span class="hljs-comment">//</span>
<span class="hljs-comment">// ... </span>
  body: Center(child: CounterDisplay()),
<span class="hljs-comment">// ...</span>
</code></pre>
<p>You will build many custom widgets and they in turn will be descendants to more custom widgets, and that's fine. The widget tree is meant to continuously grow as the need arises.</p>
<h3 id="heading-5-add-more-customization">5. Add more customization</h3>
<p>You won't customize widgets only because of refactoring and <a target="_blank" href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">repetitions (DRY code)</a>. You will create custom widgets because of the UI you are implementing.</p>
<p>You will create custom widgets because the many available widgets don't always meet the exact needs of a given UI. You'll need to combine them in some special way to implement a particular UI.</p>
<h4 id="heading-a-container-widget">a. Container Widget</h4>
<p><a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container-class.html">Container</a> is a powerful widget. You can style it in different ways. If you are used to web frontend, you'll notice that it is like a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div">div</a> in HTML.</p>
<p>Container is a base widget. You can use it to create any UI piece.</p>
<p>Some Container parameters are <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container/constraints.html">constraints</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container/decoration.html">decoration</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container/margin.html">margin</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container/padding.html">padding</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container/transform.html">transform</a>, among others. Of course, Container takes a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/Container/child.html">child</a> which can be any widget.</p>
<p>The decoration property can take a <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration-class.html">BoxDecoration</a>, which in turn can take several other properties. This is the heart of Container's flexibility. BoxDecoration takes parameters like <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration/border.html">border</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration/borderRadius.html">borderRadius</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration/boxShadow.html">boxShadow</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration/color.html">color</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration/gradient.html">gradient</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration/image.html">image</a>, <a target="_blank" href="https://api.flutter.dev/flutter/painting/BoxDecoration/shape.html">shape</a>, among others.</p>
<p>With these parameters and their values, you can implement any UI to your taste. You can use <code>Container</code> instead of the many <a target="_blank" href="https://api.flutter.dev/flutter/material/material-library.html">material widgets</a> that Flutter comes with. That way your app is to your taste.</p>
<h4 id="heading-b-gesturedetector-inkwell">b. GestureDetector / InkWell</h4>
<p><a target="_blank" href="https://api.flutter.dev/flutter/widgets/GestureDetector-class.html">GestureDetector</a> as the name implies detects user interactions. Not every UI piece is a button. And while implementing UIs you will need some widgets to react to user actions. In such a case, use GestureDetector.</p>
<p>GestureDetector can detect different types of gestures: taps, double taps, swipes, ... GestureDetector of course takes a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/GestureDetector/child.html">child</a> (which can be any widget), and different callbacks for different gestures like <a target="_blank" href="https://api.flutter.dev/flutter/widgets/GestureDetector/onTap.html">onTap</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/GestureDetector/onDoubleTap.html">onDoubleTap</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/GestureDetector/onDoubleTap.html">onPanUpdate</a> (for swipes), ...</p>
<p>Note: By default, when users interact with the empty spaces in the child of GestureDetectors, the callbacks are not called. If you want your GestureDetector to react to gestures on empty space (within its child), then set the <a target="_blank" href="https://api.flutter.dev/flutter/widgets/GestureDetector/behavior.html">behavior</a> property of the GestureDetector to <a target="_blank" href="https://api.flutter.dev/flutter/rendering/HitTestBehavior.html">HitTestBehavior.translucent</a>.</p>
<pre><code class="lang-dart">GestureDetector(
  <span class="hljs-comment">// set behavior to detect taps on empty spaces</span>
  behavior: HitTestBehavior.translucent,
  child: Column(
    children: [
      Text(<span class="hljs-string">'I have space after me ...'</span>),
      SizedBox(height: <span class="hljs-number">32</span>),
      Text(<span class="hljs-string">'... that can detect taps.'</span>),
    ],
  ),
  onTap: () =&gt; <span class="hljs-built_in">print</span>(<span class="hljs-string">'Tapped on empty space.'</span>),
)
</code></pre>
<p><a target="_blank" href="https://api.flutter.dev/flutter/material/InkWell-class.html">InkWell</a> is similar to GestureDetector. It responds to some gestures that GestureDetector responds to. However, it shows ripple effects when interacted with (which GestureDetectors don't).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/QqEZ3.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>From https://stackoverflow.com/q/58285012/13644299</em></p>
<p>InkWell must have a <a target="_blank" href="https://api.flutter.dev/flutter/material/Material-class.html">Material</a> ancestor. So, if your topmost widget is <a target="_blank" href="https://api.flutter.dev/flutter/material/MaterialApp-class.html">MaterialApp</a> you need not worry. Else, wrap the InkWell in a Material.</p>
<p>You should also do this wrapping if you are changing the colors of the InkWell's parent or child. If you don't, the ripple won't show. You also have to set the <a target="_blank" href="https://api.flutter.dev/flutter/material/Material/color.html">color</a> of the Material widget for the ripple to show. You can set the color to <a target="_blank" href="https://api.flutter.dev/flutter/material/Colors/transparent-constant.html">Colors.transparent</a> and Flutter will take care of the rest.</p>
<h2 id="heading-how-to-implement-scrolling-interfaces">How to Implement Scrolling Interfaces</h2>
<p>Scrolling is a little delicate topic. By default, widgets don't scroll in Flutter. If your Column or Row will be scrollable, use a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/ListView-class.html">ListView</a> instead. ListView takes children parameter too.</p>
<p>ListView also has factory constructors like <a target="_blank" href="https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html">ListView.builder</a> and <a target="_blank" href="https://api.flutter.dev/flutter/widgets/ListView/ListView.separated.html">ListView.separated</a>. The builder gives you more control over the build process of the children whereas the separated takes into account a Separator (like <a target="_blank" href="https://api.flutter.dev/flutter/material/Divider-class.html">Divider</a> for example).</p>
<p>By default, ListViews scroll their children vertically. However, you can change the <a target="_blank" href="https://api.flutter.dev/flutter/widgets/ScrollView/scrollDirection.html">scrollDirection</a> of a ListView to <a target="_blank" href="https://api.flutter.dev/flutter/painting/Axis.html">Axis.horizontal</a> to scroll its children horizontally.</p>
<p>At times, you might want to use <a target="_blank" href="https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html">SingleChildScrollView</a> instead of ListView. As the name implies, it takes a single <a target="_blank" href="https://api.flutter.dev/flutter/widgets/SingleChildScrollView/child.html">child</a> and it can scroll. You can pass widget groups as its child.</p>
<p><a target="_blank" href="https://docs.flutter.dev/development/ui/widgets/scrolling">There are other scrolling widgets</a>.</p>
<p>But take special note of <a target="_blank" href="https://api.flutter.dev/flutter/widgets/CustomScrollView-class.html">CustomScrollView</a>. It gives you huge control of scrolling, unlike the others. It takes <a target="_blank" href="https://api.flutter.dev/flutter/widgets/CustomScrollView/slivers.html">slivers</a>, which in turn are scrolling widgets with powerful scroll mechanisms.</p>
<p><a target="_blank" href="https://api.flutter.dev/flutter/widgets/SliverFillRemaining-class.html">SliverFillRemaining</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/SliverFillViewport-class.html">SliverFillViewport</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/SliverGrid-class.html">SliverGrid</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/SliverList-class.html">SliverList</a>, <a target="_blank" href="https://api.flutter.dev/flutter/widgets/SliverPersistentHeader-class.html">SliverPersistentHeader</a> among others, are examples of widgets you include in the list of slivers. Most of these widgets take a delegate, which handles how scrolling occurs.</p>
<p>A good case to use CustomScrollView is with <a target="_blank" href="https://api.flutter.dev/flutter/material/SliverAppBar-class.html">SliverAppBar</a>, where you want the AppBar to be expanded by default and shrunk on scroll.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/ap.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Another example could be with a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/DraggableScrollableSheet-class.html">DraggableScrollableSheet</a> where you keep some action button sticked to the bottom.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/bs.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-about-custompaint">About CustomPaint</h2>
<p>This is where Flutter gave ultimate flexibility to the UI world.</p>
<p><a target="_blank" href="https://api.flutter.dev/flutter/widgets/CustomPaint-class.html">CustomPaint</a> is to Flutter what the Canvas API is to HTML or SVG is to images.</p>
<p>CustomPaint is a widget in Flutter that gives you the ability to design and draw without limitations. It gives you a canvas on which you can draw with a <a target="_blank" href="https://api.flutter.dev/flutter/widgets/CustomPaint/painter.html">painter</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/visualizer.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>From https://blog.codemagic.io/flutter-custom-painter/</em></p>
<p>You will rarely use CustomPaint. But be aware that it exists. Because there might be very complex UIs that widget combinations might not implement them and you will have no choice than drawing with <code>CustomPaint</code>.</p>
<p>When that time comes, it won't be hard for you because you are already familiar with other widgets.</p>
<h2 id="heading-summary">Summary</h2>
<p>For a given UI piece, choose a widget, write its code, build the widget with other widgets, and see what great UI you are implementing with Flutter.</p>
<p>Implementing UIs is a major part of mobile, web, and desktop app development. Flutter is a UI toolkit that build cross-platform for those platforms. Flutter's declarative nature and its widget abundance make UI implementation simple.</p>
<p>Keep implementing UIs in Flutter. As you do, it will become second nature to you. And you will be able to implement any UI in Flutter.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
