PageSourceSearch

https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx

html adityasharma.co collected 2026-10-06 12:16:48 UTC 283,989 bytes, 538 lines download raw bytes

1<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1696485327830/8b86432c-2243-4136-8241-d2224d5cee4f.png"/><link rel="stylesheet" href="/_next/static/css/0d16c72050851854.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/7ffcc02a3c67389b.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/c20e76e7d11dec32.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/59fefb2212bd5b60.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-55b36f0cd6c208fe.js"/>
1<script src="/_next/static/chunks/4bd1b696-100b9d70ed4e49c1.js" async=""></script>
1<script src="/_next/static/chunks/9da6db1e-8a4e8c23641c31bf.js" async=""></script>
1<script src="/_next/static/chunks/1255-a242e0ce0372fddf.js" async=""></script>
1<script src="/_next/static/chunks/main-app-bda8a52fe841b3aa.js" async=""></script>
1<script src="/_next/static/chunks/4554-984b480a9db5f3fe.js" async=""></script>
1<script src="/_next/static/chunks/7563-4dfa0994a9b0e37c.js" async=""></script>
1<script src="/_next/static/chunks/9216-9272709ba40c1cfd.js" async=""></script>
1<script src="/_next/static/chunks/2810-6b74d77b4b0dc790.js" async=""></script>
1<script src="/_next/static/chunks/3091-bf1537ff6571a1bd.js" async=""></script>
1<script src="/_next/static/chunks/app/layout-6ae7b4b583d4bba3.js" async=""></script>
1<script src="/_next/static/chunks/app/not-found-b861935655c5698b.js" async=""></script>
1<script src="/_next/static/chunks/5001-fae0bebdd93f95e3.js" async=""></script>
1<script src="/_next/static/chunks/2423-c8b1388240338d7d.js" async=""></script>
1<script src="/_next/static/chunks/652-f40dcb480b4e356f.js" async=""></script>
1<script src="/_next/static/chunks/730-8191af9e9424b148.js" async=""></script>
1<script src="/_next/static/chunks/app/page-cc18ae1a3a7a5ed8.js" async=""></script>
1<script src="/_next/static/chunks/0dbeb660-9e82e99813552576.js" async=""></script>
1<script src="/_next/static/chunks/1545-7abf96433ff6553f.js" async=""></script>
1<script src="/_next/static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js" async=""></script>
1<meta name="next-size-adjust" content=""/><title>Flutter App Development: Clean Architecture with GetX</title><meta name="description" content="Elevate your Flutter app development with Clean Architecture and GetX. Learn to build efficient, organized, and high-performance apps. Dive in now!"/><link rel="canonical" href="https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx"/><meta property="og:title" content="Flutter App Development: Clean Architecture with GetX"/><meta property="og:description" content="Elevate your Flutter app development with Clean Architecture and GetX. Learn to build efficient, organized, and high-performance apps. Dive in now!"/><meta property="og:url" content="https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx"/><meta property="og:site_name" content="Aditya&#x27;s Blog"/><meta property="og:image" content="https://cdn.hashnode.com/res/hashnode/image/upload/v1696500681728/c3e1000f-3ed5-48d7-a5f8-08fc8242a340.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:type" content="article"/><meta property="article:published_time" content="2023-10-05T10:12:05.108Z"/><meta property="article:modified_time" content="2024-05-24T17:35:34.293Z"/><meta property="article:author" content="https://blog.adityasharma.co/@BetaPundit"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="https://twitter.com/BetaPundit"/><meta name="twitter:title" content="Flutter App Development: Clean Architecture with GetX"/><meta name="twitter:description" content="Elevate your Flutter app development with Clean Architecture and GetX. Learn to build efficient, organized, and high-performance apps. Dive in now!"/><meta name="twitter:image" content="https://cdn.hashnode.com/res/hashnode/image/upload/v1696500681728/c3e1000f-3ed5-48d7-a5f8-08fc8242a340.png"/><link rel="icon" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1602225116875/Bf-bb_MDw.jpeg"/>
1<script type="text/javascript">window.addEventListener('message',function(e){var d=e.data;if(!d||d.__hnWidget!==1||typeof d.h!=='number'||d.h<=0)return;var f=document.querySelectorAll('.hn-widget-frame');for(var i=0;i<f.length;i++){if(f[i].contentWindow===e.source){f[i].style.height=d.h+'px';break;}}});</script>
1<script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script>
1</head><body class="__variable_c2612a __variable_246ccd __variable_3069d5 bg-background dark:bg-background"><div hidden=""><!--$--><!--/$--></div>
1<script>((a2, b2, c4, d3, e2, f2, g4, h3) => {
2          let i3 = document.documentElement, j2 = ["light", "dark"];
3          function k2(b3) {
4            var c5;
5            (Array.isArray(a2) ? a2 : [a2]).forEach((a3) => {
6              let c6 = a3 === "class", d4 = c6 && f2 ? e2.map((a4) => f2[a4] || a4) : e2;
7              c6 ? (i3.classList.remove(...d4), i3.classList.add(f2 && f2[b3] ? f2[b3] : b3)) : i3.setAttribute(a3, b3);
8            }), c5 = b3, h3 && j2.includes(c5) && (i3.style.colorScheme = c5);
9          }
10          if (d3) k2(d3);
11          else try {
12            let a3 = localStorage.getItem(b2) || c4, d4 = g4 && a3 === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : a3;
13            k2(d4);
14          } catch {
15          }
16        })("class","theme","system",null,["light","dark"],null,true,true)</script>
16<main id="main-content" class="min-h-screen bg-transparent"><header class="sticky top-0 z-50 w-full py-4 bg-background backdrop-blur-sm transition-transform duration-300 translate-y-0"><a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-50 focus:rounded-md focus:bg-background focus:px-4 focus:py-2 focus:text-sm focus:shadow">Skip to main content</a><div class="relative w-full mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem] px-4 sm:px-12 md:px-18 lg:px-16 xl:px-10"><div class="flex items-center justify-between gap-3 xl:hidden"><div class="flex items-center gap-3 min-w-0 flex-1"><a href="https://hashnode.com/?utm_source=https%3A%2F%2Fblog.adityasharma.co&amp;utm_medium=referral&amp;utm_campaign=blog_header_logo&amp;utm_content=logo" class="inline-flex items-center justify-center text-lg text-muted-foreground hover:text-foreground transition border rounded-md h-9 w-9 shrink-0 border-border hover:bg-muted"><i class="text-xl fa-brands fa-hashnode" aria-hidden="true"></i><span class="sr-only">Hashnode</span></a><a title="Aditya&#x27;s Blog" class="text-lg font-semibold text-foreground truncate min-w-0" href="/">Aditya&#x27;s Blog</a></div><div class="flex items-center gap-2 shrink-0"><button class="gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent py-2 h-9 w-9 px-0 flex items-center justify-center text-muted-foreground hover:text-foreground bg-transparent border-border shadow-none" aria-label="Open search (press Control or Command and K)"><i class="fa-jelly fa-search text-sm" aria-hidden="true"></i><span class="sr-only">Open search (press Control or Command and K)</span></button><button class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent w-9 px-3 h-9 text-muted-foreground hover:text-foreground shadow-none" type="button" id="radix-_R_2j4anpfalb_" aria-haspopup="menu" aria-expanded="false" data-state="closed" data-slot="dropdown-menu-trigger"><i class="fa-jelly fa-sun text-sm scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" aria-hidden="true"></i><i class="fa-jelly fa-moon absolute text-sm scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" aria-hidden="true"></i><span class="sr-only">Toggle theme</span></button><button class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 border border-input bg-background hover:bg-accent hover:text-accent-foreground py-2 h-9 w-10 px-0 shadow-none" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="radix-_R_3j4anpfalb_" data-state="closed" data-slot="sheet-trigger"><i class="fa-jelly fa-bars" aria-hidden="true"></i><span class="sr-only">Open menu</span></button></div></div><div class="hidden xl:grid gap-4 items-center xl:grid-cols-[1fr_auto_1fr] xl:gap-6 max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="flex items-center w-full gap-3 min-w-0"><a href="https://hashnode.com/?utm_source=https%3A%2F%2Fblog.adityasharma.co&amp;utm_medium=referral&amp;utm_campaign=blog_header_logo&amp;utm_content=logo" class="inline-flex items-center justify-center text-lg text-muted-foreground hover:text-foreground transition border rounded-md h-9 w-9 border-border hover:bg-muted"><i class="text-xl fa-brands fa-hashnode" aria-hidden="true"></i><span class="sr-only">Hashnode</span></a><span class="h-6 border-l border-border"></span><p class="font-semibold text-foreground min-w-0 truncate text-lg sm:text-xl"><a title="Aditya&#x27;s Blog" href="/">Aditya&#x27;s Blog</a></p></div><nav aria-label="Main navigation" class="flex justify-center w-full"><nav aria-label="Main" data-orientation="horizontal" dir="ltr" data-slot="navigation-menu" data-viewport="false" class="group/navigation-menu relative flex flex-1 items-center max-w-max justify-center"><div style="position:relative"><ul data-orientation="horizontal" data-slot="navigation-menu-list" class="group flex flex-1 list-none items-center flex-nowrap justify-center gap-1 text-muted-foreground text-base" dir="ltr"><li data-slot="navigation-menu-item" class="relative"><a data-slot="navigation-menu-link" class="data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground focus:bg-accent focus:text-accent-foreground [&amp;_svg:not([class*=&#x27;text-&#x27;])]:text-muted-foreground flex-col p-2 outline-none focus-visible:outline-1 [&amp;_svg:not([class*=&#x27;
16size-&#x27;])]:size-4 inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2" data-radix-collection-item="" href="/page/portfolio">Portfolio</a></li></ul></div></nav></nav><div class="flex items-center justify-end gap-3 text-muted-foreground"><button class="gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent py-2 h-9 w-9 px-0 flex items-center justify-center text-muted-foreground hover:text-foreground bg-transparent border-border shadow-none" aria-label="Open search (press Control or Command and K)"><i class="fa-jelly fa-search text-sm" aria-hidden="true"></i><span class="sr-only">Open search (press Control or Command and K)</span></button><div class="flex items-center gap-3"><button class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent w-9 px-3 h-9 text-muted-foreground hover:text-foreground shadow-none" type="button" id="radix-_R_6t4anpfalb_" aria-haspopup="menu" aria-expanded="false" data-state="closed" data-slot="dropdown-menu-trigger"><i class="fa-jelly fa-sun text-sm scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" aria-hidden="true"></i><i class="fa-jelly fa-moon absolute text-sm scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" aria-hidden="true"></i><span class="sr-only">Toggle theme</span></button><a target="_blank" rel="noopener" class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 border border-input bg-background hover:bg-accent hover:text-accent-foreground py-2 gap-2 px-3 h-9 shadow-none" href="https://hashnode.com"><i class="text-sm fa-jelly fa-pencil" aria-hidden="true"></i>Write</a></div></div></div></div></header><div data-slot="dialog-header" class="flex flex-col gap-2 text-center sm:text-left sr-only"><h2 id="radix-_R_8anpfalbH1_" data-slot="dialog-title" class="text-lg leading-none font-semibold">Command Palette</h2><p id="radix-_R_8anpfalbH2_" data-slot="dialog-description" class="text-muted-foreground text-sm">Search for a command to run...</p></div><section class="py-8"><div class="px-4 sm:px-8"><div class="w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="grid gap-5 max-w-full"><article class="w-full max-w-full rounded-xl border bg-card text-card-foreground"><div class="px-5 pt-12 pb-8 sm:px-8"><header class="space-y-5"><div class="space-y-4 text-center max-w-4xl mx-auto"><h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold leading-tight tracking-tight text-balance text-foreground">Building Flutter Apps with Clean Architecture Using GetX</h1><p class="text-lg sm:text-xl leading-normal font-normal text-muted-foreground text-balance">Mastering Clean Architecture and GetX for High-Performing Flutter Apps</p><div class="flex flex-wrap items-center justify-center gap-3 text-sm text-muted-foreground"><div class="flex items-center gap-2"><span>Updated</span><time dateTime="2024-05-24T17:35:34.293Z">May 24, 2024</time></div><span class="text-muted-foreground">•</span><span>12<!-- --> min read</span><span class="text-muted-foreground">•</span><a href="/building-flutter-apps-with-clean-architecture-using-getx.md" target="_blank" rel="noopener" class="inline-flex items-center gap-2 text-muted-foreground transition-colors hover:text-foreground"><i class="fa-brands fa-markdown" aria-hidden="true"></i>View as Markdown</a></div></div></header></div><div class="w-full"><img alt="Building Flutter Apps with Clean Architecture Using GetX" width="1200" height="750" decoding="async" data-nimg="1" class="w-full h-auto" style="color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696485327830/8b86432c-2243-4136-8241-d2224d5cee4f.png"/></div><div class="px-5 py-12 sm:px-8 space-y-8"><div class="max-w-2xl mx-auto"><div class="flex flex-wrap items-center gap-5"><div class="flex items-center gap-5"><div class="flex gap-3 items-start"><a target="_blank" rel="noopener" href="https://hashnode.com/@BetaPundit"><span class="relative flex shrink-0 overflow-hidden rounded-full h-10 w-10 border border-border transition-opacity hover:opacity-80"><span class="flex h-full w-full items-center justify-center rounded-full bg-muted text-muted-foreground font-semibold text-lg">A</span></span></a><div class="min-w-0"><div class="flex items-center gap-2"><a target="_blank" rel="noopener" class="font-medium text-foreground hover:text-primary transition-colors" href="https://hashnode.com/@BetaPundit">Aditya Sharma</a><div class="flex items-center gap-1.5 text-sm"><a target="_blank" rel="noopener" class="text-muted-foreground hover:text-foreground transition-colors" aria-label="X (Twitter)" href="https://twitter.com/BetaPundit"><i class="fa-brands fa-x-twitter" aria-hidden="true"></i></a><a target="_blank" rel="noopener" class="text-muted-foreground hover:text-foreground transition-colors" aria-label="LinkedIn" href="https://linkedin.com/in/aditya-sharma26"><i class="fa-brands fa-linkedin" aria-hidden="true"></i></a></div></div><div class="text-sm text-muted-foreground line-clamp-2"><p>I am a full stack developer, a Machine Learning enthusiast &amp; a Flutter Developer. Also, I am a budding Entrepreneur! </p>
17<p>I am a student of Life and believe in building tech for people.</p>
18<p>
18I love contributing to open-source projects and use my knowledge to develop a software solution for general problems arising in the community. </p>
19</div></div></div></div></div></div><div class="max-w-2xl mx-auto"><a class="group flex items-center gap-3 rounded-lg border border-border px-3 py-2 transition hover:border-primary hover:bg-accent" href="/series/what-the-flutter"><i class="fa-jelly fa-layer-group h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors" aria-hidden="true"></i><div class="flex flex-col"><span class="text-sm text-muted-foreground">Part of series</span><span class="text-sm font-medium text-foreground group-hover:text-primary transition-colors">What the Flutter</span></div></a></div><div class="relative"><aside class="hidden xl:block absolute top-0 left-0 h-full"><nav class="sticky top-22 w-44"><div class="hidden xl:block group/toc relative"><div class="grid"><div class="[grid-area:1/1] opacity-0 group-hover/toc:opacity-100 transition-opacity duration-200 max-h-[calc(100vh-3rem)] overflow-y-auto"><p class="text-sm font-semibold uppercase tracking-wide text-muted-foreground mb-3">On this page</p><nav aria-label="On this page"><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-what-is-clean-architecture">What is Clean Architecture?</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-project-setup">Project Setup</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-packages">Packages</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-folder-structure">Folder Structure</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-architecture-and-dependency-rules">Architecture and Dependency Rules</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-app-configuration">App Configuration</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-routing">Routing</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-theme">Theme</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-utils">Utils</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-dependency-injection">Dependency Injection</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-domain-layer">Domain Layer</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-entities-models">Entities (Models)</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-repositories-abstract-definitions">Repositories (Abstract Definitions)</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-data-layer">Data Layer</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-data-sources">Data Sources</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-repositories-implementation">Repositories (Implementation)</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-presentation-layer">Presentation Layer</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-controllers">Controllers</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0.75rem" href="#heading-screens">Screens</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-run">Run</a><a class="block py-1 text-sm transition-colors hover:text-foreground text-muted-foreground" style="padding-left:0rem" href="#heading-conclusion">Conclusion</a></nav></div><div class="[grid-area:1/1] relative flex flex-col gap-px transition-opacity duration-200 group-hover/toc:opacity-0 pointer-events-none self-start max-h-[calc(100vh-3rem)] overflow-hidden pl-3"><div class="absolute left-0 w-0.5 rounded-full bg-foreground transition-all duration-300 opacity-0" style="transform:translateY(0px);height:12px"></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:27px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:39px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:16px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:33px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:26px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:20px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:41px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:17px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:35px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:35px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:12px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:29px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:18px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:11px"></div></div><div class="flex items-center h-3" style="padding-left:10px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div><div class="flex items-center h-3" style="padding-left:0px"><div class="h-0.5 rounded-full transition-all duration-300 bg-border" style="width:10px"></div></div></div></div></div></nav></aside><div class="max-w-2xl mx-auto min-w-0 contain-[inline-size]"><div class="prose dark:prose-invert prose-pre:bg-foreground prose-pre:text-background prose-pre:border prose-pre:border-border prose-pre:rounded prose-pre:p
19x-5 prose-pre:py-4 dark:prose-pre:bg-muted dark:prose-pre:text-foreground dark:prose-pre:border-border prose-pre:overflow-x-auto max-w-none text-base sm:text-lg [&amp;&gt;div&gt;p:first-child]:mt-0 [&amp;&gt;div&gt;p:first-child]:pt-0 min-w-0 wrap-break-word [&amp;_a]:break-all **:max-w-full"><div><p>Hello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure, this guide will help you build organized and maintainable Flutter apps. So, grab your coding capes, and let's dive in!</p>
20<h2 id="heading-what-is-clean-architecture">What is Clean Architecture?</h2>
21<p>Before we delve into the nitty-gritty of Flutter clean architecture with GetX, let's start with a quick introduction to clean architecture.</p>
22<p><strong>Pros of Clean Architecture:</strong></p>
23<ul>
24<li><p><strong>Modularity:</strong> Clean architecture promotes a modular approach, making your codebase easier to understand and maintain.</p>
25</li>
26<li><p><strong>Testability:</strong> It encourages writing unit tests and makes your codebase more test-friendly.</p>
27</li>
28<li><p><strong>Scalability:</strong> Your app can grow and evolve without major architectural changes.</p>
29</li>
30<li><p><strong>Independence:</strong> The architecture keeps external frameworks and libraries at bay, preventing them from tightly coupling with your code.</p>
31</li>
32</ul>
33<p><strong>Cons of Clean Architecture:</strong></p>
34<ul>
35<li><p><strong>Initial Complexity:</strong> Setting up a clean architecture project might seem more daunting compared to a simple structure.</p>
36</li>
37<li><p><strong>Learning Curve:</strong> Developers new to clean architecture may take some time to fully grasp its concepts.</p>
38</li>
39</ul>
40<p>Now that we're clear on the basics, let's roll up our sleeves and get started!</p>
41<hr />
42<h2 id="heading-project-setup">Project Setup</h2>
43<p>
43This project is written using the current latest stable versions of:</p>
44<ul>
45<li><p><a target="_blank" href="https://docs.flutter.dev/development/tools/sdk/releases" rel="noopener ugc"><strong>Flutter</strong></a>: v3.10.6</p>
46</li>
47<li><p><a target="_blank" href="https://dart.dev/guides/language/evolution" rel="noopener ugc"><strong>Dart</strong></a>: v3.0.6</p>
48</li>
49</ul>
50<h3 id="heading-packages">Packages</h3>
51<p>Before we start coding, let's gather our tools and libraries. Here are the essential packages we'll use:</p>
52<ul>
53<li><p><a target="_blank" href="https://pub.dev/packages/get" rel="noopener ugc"><strong>getX</strong></a><strong>:</strong> The Swiss Army knife for state management. (You can use any kind of state management you prefer to work with such as <a target="_blank" href="https://pub.dev/packages/provider" rel="noopener ugc"><strong>provider</strong></a><strong>or</strong><a target="_blank" href="https://pub.dev/packages/flutter_bloc" rel="noopener ugc"><strong>flutter_bloc</strong></a>)</p>
54</li>
55<li><p><a target="_blank" href="https://pub.dev/packages/freezed" rel="noopener ugc"><strong>freezed</strong></a><strong>:</strong> A code generation package for creating immutable data models.</p>
56</li>
57<li><p><a target="_blank" href="https://pub.dev/packages/retrofit" rel="noopener ugc"><strong>retrofit</strong></a> <strong>&amp;</strong> <a target="_blank" href="https://pub.dev/packages/dio" rel="noopener ugc"><strong>dio</strong></a><strong>:</strong> For handling HTTP requests.</p>
58</li>
59<li><p><a target="_blank" href="https://pub.dev/packages/get_it" rel="noopener ugc"><strong>get_it</strong></a> <strong>&amp;</strong> <a target="_blank" href="https://pub.dev/packages/injectable" rel="noopener ugc"><strong>injectable</strong></a><strong>:</strong> For dependency injection.</p>
60</li>
61<li><p><a target="_blank" href="https://pub.dev/packages/go_router" rel="noopener ugc"><strong>go_router</strong></a><strong>:</strong> A declarative routing package that provides a convenient, URL-based API for navigating between different screens.</p>
62</li>
63<li><p><a target="_blank" href="https://pub.dev/packages/pretty_dio_logger" rel="noopener ugc"><strong>
63pretty_dio_logger</strong></a><strong>:</strong> A delightful logger for HTTP requests.</p>
64</li>
65</ul>
66<p>Let's include these packages in our <code>pubspec.yaml</code>:</p>
67<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
68  <span class="hljs-attr">get:</span> <span class="hljs-string">^4.6.6</span>
69  <span class="hljs-attr">retrofit:</span> <span class="hljs-string">^4.0.2</span>
70  <span class="hljs-attr">dio:</span> <span class="hljs-string">^5.3.3</span>
71  <span class="hljs-attr">get_it:</span> <span class="hljs-string">^7.6.4</span>
72  <span class="hljs-attr">injectable:</span> <span class="hljs-string">^2.3.0</span>
73  <span class="hljs-attr">go_router:</span> <span class="hljs-string">^11.1.2</span>
74  <span class="hljs-attr">pretty_dio_logger:</span> <span class="hljs-string">^1.3.1</span>
75
76<span class="hljs-attr">dev_dependencies:</span>
77  <span class="hljs-attr">flutter_test:</span>
78    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
79  <span class="hljs-attr">flutter_lints:</span> <span class="hljs-string">^2.0.0</span>
80  <span class="hljs-attr">build_runner:</span> <span class="hljs-string">^2.4.6</span>
81  <span class="hljs-attr">freezed:</span> <span class="hljs-string">^2.4.3</span>
82  <span class="hljs-attr">json_serializable:</span> <span class="hljs-string">^6.7.1</span>
83  <span class="hljs-attr">retrofit_generator:</span> <span class="hljs-string">^7.0.8</span>
84  <span class="hljs-attr">injectable_generator:</span> <span class="hljs-string">^2.4.0</span>
85</code></pre>
86<h3 id="heading-folder-structure">Folder Structure</h3>
87<p>Organization is key in clean architecture. Here's a recommended folder structure:</p>
88<pre><code class="lang-c">lib/
89  ├── config/
90  │     ├── injection/
91  │     ├── router/
92  │     └── themes/
93  ├── data/
94  │     ├── datasources/
95  │     │    ├── local/
96  │     │    └── remote/
97  │     └── repositories/
98  ├── domain/
99  │     ├── models/
100  │     └── repositories/
101  ├── presentation/
102  │     ├── controllers/
103  │     ├── pages/
104  │     └── widgets/
105  ├── utils/
106  │     ├── constants/
107  │     ├── extensions/
108  │     └── resources/
109  └── main.dart
110</code></pre>
111<p>The <strong>config</strong> folder includes the configuration of the app (<strong>themes</strong>, <strong>router</strong>, ..etc) and anything else related to the app’s configurations.</p>
112<p>The <strong>utils</strong> folder, on the other hand, includes (<strong>constants</strong>, <strong>extensions</strong>, <strong>resources</strong>, ..etc) and anything related to that.</p>
113<p>The (<strong>data</strong>, <strong>domain</strong> and <strong>presentation</strong>) folders will be explained later in detail.</p>
114<h3 id="heading-architecture-and-dependency-rules">Architecture and Dependency Rules</h3>
115<p>The clean architecture consists of three primary layers:</p>
116<ol>
117<li><p><strong>Data Layer:</strong> Responsible for data fetching and caching.</p>
118</li>
119<li><p><strong>Domain Layer:</strong> Houses the core business logic.</p>
120</li>
121<li><p><strong>Presentation Layer:</strong> Manages the user interface and user interactions.</p>
122</li>
123</ol>
124<p>Each layer has its unique responsibilities, and dependencies flow inward. The Data Layer is independent, the Domain Layer depends on Data, and the Presentation Layer relies on both Data and Domain.</p>
125<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696499988717/67904f8e-432e-4d19-86d3-491d790d0f1a.png" alt class="image--center mx-auto" /></p>
126<hr />
127<h2 id="heading-app-configuration">App Configuration</h2>
128<p>Before diving into the layers, let's configure our app. Here's a sneak peek:</p>
129<h3 id="heading-routing">Routing</h3>
130<p>Routing is a fundamental aspect of any Flutter app. We'll use the <code>go_router</code> package to handle navigation efficiently.</p>
131<blockquote>
132<p><em>You can absolutely choose whatever the routing package you prefer, and that suits your needs.</em></p>
133</blockquote>
134<p>Now, after adding the package to your <code>pubspec.yaml</code> file as we talked about it at the beginning of the article, create a folder in this path <strong>lib/config</strong> and call it <strong>router</strong>, then inside the folder create a file and name it <code>app_router.dart</code> as shown below:</p>
135<pre><code class="lang-dart"><span class="hljs-meta">@singleton</span>
136<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppRouter</span> </span>{
137  <span class="hljs-keyword">final</span> GoRouter _router = GoRouter(
138    debugLogDiagnostics: <span class="hljs-keyword">true</span>,
139    initialLocation: <span class="hljs-string">'/'</span>,
140    routes: [
141      GoRoute(
142        path: <span class="hljs-string">'/'</span>,
143        builder: (context, state) =&gt; <span class="hljs-keyword">const</span> HomeScreen(),
144      ),
145      GoRoute(
146        path: <span class="hljs-string">'/profile'</span>,
147        builder: (context, state) =&gt; <span class="hljs-keyword">const</span> UserDetailsScreen(),
148      ),
149      ...
150      <span class="hljs-comment">// Add more routes as needed</span>
151  );
152
153  GoRouter <span class="hljs-keyword">get</span> router =&gt; _router;
154}
155</code></pre>
156<h3 id="heading-theme">Theme</h3>
157<p>Defining a global theme ensures consistency in your app's appearance. Let's create a file <code>app_theme.dart</code> inside <strong>lib/config/themes</strong>:</p>
158<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dtdcopedia/config/themes/app_colors.dart'</span>;
159<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
160
161<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppTheme</span> </span>{
162  <span class="hljs-keyword">static</span> ThemeData <span class="hljs-keyword">get</span> light {
163    <span class="hljs-keyword">return</span> ThemeData(
164      appBarTheme: <span class="hljs-keyword">const</span> AppBarTheme(
165        elevation: <span class="hljs-number">0</span>,
166        color: AppColors.primary,
167      ),
168      scaffoldBackgroundColor: AppColors.background,
169      primaryColor: AppColors.primary,
170      splashColor: Colors.transparent,
171      fontFamily: <span class="hljs-string">'Hind'</span>,
172    );
173  }
174
175  <span class="hljs-keyword">static</span> ThemeData <span class="hljs-keyword">get</span> dark {
176    <span class="hljs-keyword">return</span> ThemeData(
177      appBarTheme: <span class="hljs-keyword">const</span> AppBarTheme(
178        elevation: <span class="hljs-number">0</span>,
179        color: AppColors.primary,
180      ),
181      scaffoldBackgroundColor: AppColors.text,
182      primaryColor: AppColors.primary,
183      splashColor: Colors.transparent,
184      fontFamily: <span class="hljs-string">'Hind'</span>,
185    );
186  }
187}
188</code></pre>
189<p>This sets up your app's global theme with the specified colors, fonts, and more. You can customize it the way you like.</p>
190<div data-node-type="callout">
191<div data-node-type="callout-emoji">💡</div>
192<div data-node-type="callout-text">Use <strong>ThemeExtension</strong> for creating custom themes: <a target="_blank" href="https://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa" rel="noopener ugc">https://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa</a></div>
193</div>
194
195<h3 id="heading-utils"><strong>Utils</strong></h3>
196<p>In the <code>utils</code> folder, you can organize your utility functions, constants, extensions, and resources neatly. Here's an example structure:</p>
197<pre><code class="lang-dart">lib/
198  ├── utils/
199  │     ├── constants/
200  │     │     └── app_constants.dart
201  │     ├── extensions/
202  │     │     ├── string_extensions.dart
203  │     │     └── date_extensions.dart
204  │     ├── resources/
205  │     │     └── data_state.dart
206  │     └── ...
207  └── ...
208</code></pre>
209<p>For example, in <code>app_constants.dart</code>:</p>
210<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppConstants</span> </span>{
211  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> appTitle = <span class="hljs-string">'Flutter Clean Architecture'</span>;
212
213 <span class="hljs-comment">// Networking and APIs</span>
214  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> baseUrl = <span class="hljs-string">'https://appi.example.com/'</span>; 
215  <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> defaultApiKey = <span class="hljs-string">'YOUR_API_KEY'</span>;
216
217 <span class="hljs-comment">// Storage and Databases</span>
218 <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> appTableName = <span class="hljs-string">'app_table'</span>;
219 <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> databaseName = <span class="hljs-string">'app_database.db'</span>;
220}
221</code></pre>
222<p>And in <code>string_extensions.dart</code>:</p>
223<pre><code class="lang-dart"><span class="hljs-keyword">extension</span> StringExtensions <span class="hljs-keyword">on</span> <span class="hljs-built_in">String</span> {
224  <span class="hljs-built_in">String</span> capitalize() {
225    <span class="hljs-keyword">return</span> <span class="hljs-string">"<span class="hljs-subst">${<span class="hljs-keyword">this</span>[<span class="hljs-number">0</span>].toUpperCase()}</span><span class="hljs-subst">${<span class="hljs-keyword">this</span>.substring(<span class="hljs-number">1</span>)}</span>"</span>;
226  }
227}
228</code></pre>
229<p>In the <strong>lib/utils/resources</strong> create a file and name it <code>data_state.dart</code> which contains the following code:</p>
230<pre><code class="lang-dart"><span class="hljs-keyword">
230import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
231
232<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataState</span>&lt;<span class="hljs-title">T</span>&gt; </span>{
233    <span class="hljs-keyword">final</span> T data;
234    <span class="hljs-keyword">final</span> DioError error;
235    <span class="hljs-keyword">const</span> DataState({<span class="hljs-keyword">this</span>.data, <span class="hljs-keyword">this</span>.error});
236}
237
238<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataSuccess</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">DataState</span>&lt;<span class="hljs-title">T</span>&gt; </span>{ 
239    <span class="hljs-keyword">const</span> DataSuccess(T data): <span class="hljs-keyword">super</span>(data: data);
240}
241
242<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataFailed</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">DataState</span>&lt;<span class="hljs-title">T</span>&gt; </span>{ 
243    <span class="hljs-keyword">const</span> DataFailed (DioError error): <span class="hljs-keyword">super</span>(error: error);
244}
245</code></pre>
246<p>This wrapper class <code>DataState</code> can be used to wrap our entire network call in order to determine the state of the request being sent to the server and its response.</p>
247<p>As you can see in the code, we’ve two different states, one when we get a successful response (<strong>DataSuccess</strong>), and the other when an error occurs while sending the request or receiving the response (<strong>DataFailed</strong>).</p>
248<p>This is very important as later on when we will have too many requests and logic, you will see how minimized the code will become.</p>
249<h3 id="heading-dependency-injection">Dependency Injection</h3>
250<p>Dependency injection is a crucial part of clean architecture. We use <code>get_it</code> and <code>injectable</code> to manage our dependencies efficiently. Here's a glimpse of how it works:</p>
251<p>First, inside <strong>lib/config/injection,</strong> create a file named <code>injection.dart</code>:</p>
252<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it/get_it.dart'</span>;
253<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;
254<span class="hljs-keyword">import</span> <span class="hljs-string">'injection.config.dart'</span>;
255
256<span class="hljs-keyword">final</span> locator = GetIt.instance;
257
258<span class="hljs-meta">@InjectableInit</span>()
259Future&lt;<span class="hljs-keyword">void</span>&gt; configureDependencies() {
260  <span class="hljs-keyword">return</span> locator.init();
261}
262</code></pre>
263<p>Now, create a file named <code>register_module.dart</code> inside the same folder. Here we can inject third party packages such as <strong>SharedPreferences</strong> &amp; <strong>Dio</strong>:</p>
264<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;
265
266<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
267<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;
268<span class="hljs-keyword">import</span> <span class="hljs-string">'package:pretty_dio_logger/pretty_dio_logger.dart'</span>;
269<span class="hljs-keyword">import</span> <span class="hljs-string">'package:shared_preferences/shared_preferences.dart'</span>;
270
271<span class="hljs-meta">@module</span>
272<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegisterModule</span> </span>{
273  <span class="hljs-comment">// Inject SharedPreferences</span>
274  <span class="hljs-meta">@preResolve</span>
275  Future&lt;SharedPreferences&gt; <span class="hljs-keyword">get</span> prefs =&gt; SharedPreferences.getInstance();
276
277  <span class="hljs-comment">// Inject Dio</span>
278  <span class="hljs-meta">@lazySingleton</span>
279  Dio dio(SharedPreferences sharedPreferences) {
280    <span class="hljs-keyword">final</span> token = sharedPreferences.getString(<span class="hljs-string">'token'</span>) ?? <span class="hljs-string">''</span>;
281
282    <span class="hljs-keyword">return</span> Dio(
283      BaseOptions(
284        baseUrl: AppConstants.baseUrl,
285        headers: {
286          <span class="hljs-keyword">if</span> (token.isNotEmpty) HttpHeaders.authorizationHeader: <span class="hljs-string">"Bearer <span class="hljs-subst">$token</span>"</span>,
287        },
288      ),
289    )..interceptors.add(
290        PrettyDioLogger(
291          requestHeader: <span class="hljs-keyword">true</span>,
292          requestBody: <span class="hljs-keyword">true</span>,
293          responseHeader: <span class="hljs-keyword">true</span>,
294          maxWidth: <span class="hljs-number">120</span>,
295        ),
296      );
297  }
298}
299</code></pre>
300<p>Now you can import and use these utility functions, constants &amp; injected modules throughout your project for a clean and organized codebase. Finally, the <code>main.dart</code> file will look like this:</p>
301<pre><code class="lang-dart"><span class="hljs-keyword">void</span> main() <span class="hljs-keyword">async</span> {
302  WidgetsFlutterBinding.ensureInitialized();
303  <span class="hljs-keyword">await</span> configureDependencies();
304  runApp(<span class="hljs-keyword">const</span> MyApp());
305}
306
307<span class="hljs-keyword">final</span> _router = locator&lt;AppRouter&gt;().router;
308
309<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>{
310  <span class="hljs-keyword">const</span> MyApp({<span class="hljs-keyword">super</span>.key});
311
312  <span class="hljs-meta">
312@override</span>
313  Widget build(BuildContext context) {
314    <span class="hljs-keyword">return</span> MaterialApp.router(
315      debugShowCheckedModeBanner: <span class="hljs-keyword">false</span>,
316      title: AppConstants.appTitle,
317      theme: AppTheme.light,
318      darkTheme: AppTheme.dark,
319      routerConfig: _router,
320    );
321  }
322}
323</code></pre>
324<hr />
325<h2 id="heading-domain-layer"><strong>Domain Layer</strong></h2>
326<p>Now, let's dive into the Domain Layer. The Domain Layer is the core of the application's business logic. It encapsulates the rules, behaviours, and core functionality that define how the application operates. This layer is entirely independent of external factors such as databases, network calls, or the user interface.</p>
327<p>In <strong>Clean Architecture</strong>, the <strong>Domain Layer</strong> should be independent of any specific implementation or framework. This means that the code in the Domain Layer should be written in a <strong>technology-agnostic</strong> way, with no dependencies on external systems or libraries.</p>
328<h3 id="heading-entities-models"><strong>Entities (Models)</strong></h3>
329<p>Entities/Models are plain Dart classes that represent the core business objects of the application. They model the data that the app operates on and typically contain minimal logic. Entities are defined solely based on the business requirements, and they do not contain any database-specific or UI-related code.</p>
330<p><strong>Freezed</strong> helps us create immutable data models effortlessly. Here's a sample domain model <code>user.dart</code> inside <strong>lib/domain/models</strong>:</p>
331<pre><code class="lang-dart"><span class="hljs-keyword">part</span> <span class="hljs-string">'user.freezed.dart'</span>;
332
333<span class="hljs-meta">@freezed</span>
334<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">User</span> </span>{
335  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> User({
336    <span class="hljs-keyword">required</span> <span class="hljs-built_in">int</span> id,
337    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> name,
338    <span class="hljs-comment">
338// Add more fields as needed</span>
339  }) = _User;
340}
341</code></pre>
342<h3 id="heading-repositories-abstract-definitions"><strong>Repositories (Abstract Definitions)</strong></h3>
343<p>In the Domain Layer, repositories are defined as abstract classes or interfaces. These abstract definitions declare the methods for interacting with data without specifying how data is fetched or stored. The concrete implementations of these repositories are located in the Data Layer.</p>
344<p>Repositories are interfaces that define data sources for our domain layer. For example, <code>user_repository.dart</code> inside <strong>lib/domain/repositories</strong>:</p>
345<pre><code class="lang-dart"><span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserRepository</span> </span>{
346  Future&lt;UserEntity&gt; getUserById(<span class="hljs-built_in">int</span> id);
347}
348</code></pre>
349<h2 id="heading-data-layer"><strong>Data Layer</strong></h2>
350<p>The Data Layer is the foundational layer responsible for handling data operations in a Flutter app. Its primary purpose is to abstract and encapsulate data sources, ensuring that the rest of the application doesn't need to know the specifics of data retrieval and storage.</p>
351<h3 id="heading-data-sources"><strong>Data Sources</strong></h3>
352<p>Data Sources are responsible for interacting with external data providers, such as APIs, databases, or local storage. They define the interfaces for fetching and storing data. This layer is typically implemented using packages like <code>Retrofit</code>, <code>Dio</code>, or other HTTP client libraries for API interactions, as well as database libraries like <code>SQFLite</code> for local storage.</p>
353<p>Here's a sample data source service <code>user_data_source.dart</code> inside <strong>lib/data/datasources/remote</strong>:</p>
354<pre><code class="lang-dart"><span class="hljs-meta">@RestApi</span>(baseUrl: <span class="hljs-string">'https://api.example.com/'</span>)
355<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserDataSource</span> </span>{
356  <span class="hljs-keyword">factory</span> UserDataSource(Dio dio) = _UserDataSource;
357
358  <span class="hljs-meta">@GET</span>(<span class="hljs-string">'/users/{id}'</span>)
359  Future&lt;UserEntity&gt; getUserById(<span class="hljs-meta">@Path</span>() <span class="hljs-built_in">int</span> id);
360}
361</code></pre>
362<p>Now, create <code>data_source_injection.dart</code> file inside <strong>lib/config/injection</strong> and inject the <strong>UserDataSource</strong> class as a module:</p>
363<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
364<span class="hljs-keyword">import</span> <span class="hljs-string">'package:your_package_name/data/datasources/remote/user_data_source.dart'</span>;
365<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;
366
367<span class="hljs-meta">@module</span>
368<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataSourceInjectableModule</span> </span>{
369  UserDataSource getUserDataSource(Dio dio) =&gt; UserDataSource(dio);
370  <span class="hljs-comment">// Add more data dources as needed</span>
371}
372</code></pre>
373<h3 id="heading-repositories-implementation"><strong>Repositories (Implementation)</strong></h3>
374<p>Repositories act as intermediaries between Data Sources and the Domain Layer. They provide a clean and consistent API for accessing data. Repositories are defined as abstract classes in the Domain Layer, listing methods like <code>getUserById()</code> or <code>fetchPosts()</code>. The concrete implementations of these repositories, found in the Data Layer, interact with Data Sources to fetch and manipulate data. Repositories also manage data caching and storage.</p>
375<p>Before we implement the <strong>UserRepository</strong> class, we need to create a base class called <strong>BaseApiRepository</strong>, create a folder inside the <strong>data</strong> folder and name it <strong>base</strong>, then create a file inside it and name it <code>base_api_repository.dart</code> which contains the following:</p>
376<pre><code class="lang-dart"><span class="hljs-keyword">
376import</span> <span class="hljs-string">'dart:io'</span> <span class="hljs-keyword">show</span> HttpStatus;
377<span class="hljs-keyword">import</span> <span class="hljs-string">'package:retrofit/dio.dart'</span>;
378
379<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
380<span class="hljs-keyword">import</span> <span class="hljs-string">'package:your_project_name/utils/resources/data_state.dart'</span>;
381<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
382
383<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseApiRepository</span> </span>{
384  <span class="hljs-meta">@protected</span>
385  Future&lt;DataState&lt;T?&gt;&gt; getStateOf&lt;T&gt;({
386    <span class="hljs-keyword">required</span> Future&lt;HttpResponse&lt;T&gt;&gt; <span class="hljs-built_in">Function</span>() request,
387  }) <span class="hljs-keyword">async</span> {
388    <span class="hljs-keyword">try</span> {
389      <span class="hljs-keyword">final</span> httpResponse = <span class="hljs-keyword">await</span> request();
390      <span class="hljs-keyword">if</span> (httpResponse.response.statusCode == HttpStatus.created || httpResponse.response.statusCode == HttpStatus.ok) {
391        <span class="hljs-keyword">return</span> DataSuccess(httpResponse.data);
392      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (httpResponse.response.statusCode == HttpStatus.unauthorized) {
393        <span class="hljs-keyword">throw</span> DataFailed(DioException(
394          response: httpResponse.response,
395          requestOptions: httpResponse.response.requestOptions,
396        ));
397      } <span class="hljs-keyword">else</span> {
398        <span class="hljs-keyword">throw</span> DioException(
399          response: httpResponse.response,
400          requestOptions: httpResponse.response.requestOptions,
401        );
402      }
403    } <span class="hljs-keyword">on</span> DioException <span class="hljs-keyword">catch</span> (error) {
404      <span class="hljs-keyword">return</span> DataFailed(error);
405    }
406  }
407}
408</code></pre>
409<p>This base class plays an important role here, which wraps our API call that comes from the data source <code>user_data_source.dart</code> and return a <strong>DataState</strong> object instead of <strong>HttpResponse</strong>.</p>
410<p>By doing so, we’re reducing the boilerplate code in each method without the need to write “<strong>try-catch”</strong> or “<strong>if”</strong> statements everywhere because you only need to pass the request in the “<strong>getStateOf</strong>” method and this should do the rest and return the result based on the generic “<strong>T</strong>” type.</p>
411<p>Now create <code>user_repository_impl.dart</code> inside <strong>lib/data/repositories</strong> &amp; implement the previously defined <strong>UserRepository</strong> class as below:</p>
412<pre><code class="lang-dart"><span class="hljs-meta">@Injectable</span>(<span class="hljs-keyword">as</span>: UserRepository)
413<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserRepositoryImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">BaseApiRepository</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserRepository</span> </span>{
414  <span class="hljs-keyword">final</span> UserDataSource _userDataSource;
415  UserRepositoryImpl(<span class="hljs-keyword">this</span>._userDataSource);
416
417  <span class="hljs-meta">
417@override</span>
418  Future&lt;DataState&lt;User?&gt;&gt; getUserById(<span class="hljs-built_in">int</span> id) {
419    <span class="hljs-keyword">return</span> getStateOf&lt;User&gt;(
420      request: () =&gt; _userDataSource.getUserById(id),
421    );
422  }
423}
424</code></pre>
425<h2 id="heading-presentation-layer">Presentation Layer</h2>
426<p>Last but not least, we arrive at the Presentation Layer, where we build the user interface, controllers, screens, and common widgets.</p>
427<p>The benefit of using <strong>Presentation Layer</strong> is that it makes the codebase more modular and maintainable. By separating the user interface from the business logic and infrastructure, it becomes easier to modify and extend the application over time. Also, it allows for different user interfaces to be developed and used with the same underlying business logic. For example, a <strong>web application</strong>, a <strong>mobile application</strong>, and a <strong>desktop application</strong> could all use the same Domain Layer but have different Presentation Layers that are optimized for their respective platforms.</p>
428<h3 id="heading-controllers">Controllers</h3>
429<p>Controllers manage the state and business logic of our app. Here's a snippet of a controller <code>user_controller.dart</code> inside <strong>lib/presentation/controllers</strong> using GetX:</p>
430<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">GetxController</span> </span>{
431  <span class="hljs-keyword">static</span> UserController <span class="hljs-keyword">get</span> call =&gt; Get.find();
432
433  <span class="hljs-keyword">final</span> UserRepository _userRepository = locator&lt;UserRepository&gt;();
434  <span class="hljs-keyword">final</span> Rx&lt;User&gt; user = Rx&lt;User&gt;(User(id: <span class="hljs-number">0</span>, name: <span class="hljs-string">''</span>));
435
436  Future&lt;<span class="hljs-keyword">void</span>&gt; fetchUser(<span class="hljs-built_in">int</span> id) <span class="hljs-keyword">async</span> {
437    <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _userRepository.getUserById(id);
438    user.value = result;
439  }
440}
441</code></pre>
442<h3 id="heading-screens">Screens</h3>
443<p>Screens are where the magic happens. Screens correspond to the different views or pages of the app. Each screen is typically represented by a Flutter widget. Screens are responsible for rendering the UI and displaying data to the user.</p>
444<p>You can create screen files inside <strong>lib/presentation/screens</strong> folder. For example, here's <code>user_details_screen.dart</code>:</p>
445<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserDetailsScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
446  <span class="hljs-keyword">const</span> UserDetailsScreen({<span class="hljs-keyword">super</span>.key});
447
448  <span class="hljs-meta">
448@override</span>
449  State&lt;UserDetailsScreen&gt; createState() =&gt; _UserDetailsScreenState();
450}
451
452<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_UserDetailsScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">UserDetailsScreen</span>&gt; </span>{
453  <span class="hljs-meta">@override</span>
454  <span class="hljs-keyword">void</span> initState() {
455    <span class="hljs-comment">// Initialize &amp; inject UserController() using Get.put()</span>
456    Get.put&lt;UserController&gt;(UserController(), permanent: <span class="hljs-keyword">true</span>);
457    <span class="hljs-keyword">super</span>.initState();
458  }
459
460  <span class="hljs-meta">@override</span>
461  Widget build(BuildContext context) {
462    <span class="hljs-keyword">final</span> controller = UserController.call;
463    <span class="hljs-keyword">final</span> userIdController = TextEditingController();
464
465    <span class="hljs-keyword">return</span> Scaffold(
466      appBar: AppBar(
467        title: Text(<span class="hljs-string">'User Details'</span>),
468      ),
469      body: Padding(
470        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16.0</span>),
471        child: Column(
472          children: [
473            TextField(
474              controller: userIdController,
475              decoration: InputDecoration(labelText: <span class="hljs-string">'Enter User ID'</span>),
476              keyboardType: TextInputType.number,
477            ),
478            SizedBox(height: <span class="hljs-number">16.0</span>),
479            ElevatedButton(
480              onPressed: () {
481                <span class="hljs-keyword">final</span> userId = <span class="hljs-built_in">int</span>.tryParse(userIdController.text);
482                <span class="hljs-keyword">if</span> (userId != <span class="hljs-keyword">null</span>) {
483                  controller.fetchUser(userId);
484                } <span class="hljs-keyword">else</span> {
485                  <span class="hljs-comment">// Handle invalid input</span>
486                  Get.snackbar(<span class="hljs-string">'Invalid Input'</span>, <span class="hljs-string">'Please enter a valid User ID'</span>,
487                      snackPosition: SnackPosition.BOTTOM);
488                }
489              },
490              child: Text(<span class="hljs-string">'Fetch User'</span>),
491            ),
492            SizedBox(height: <span class="hljs-number">16.0</span>),
493            Obx(() {
494              <span class="hljs-keyword">if</span> (controller.isLoading.value) {
495                <span class="hljs-keyword">return</span> CircularProgressIndicator();
496              } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (controller.user.value.id == <span class="hljs-number">0</span>) {
497                <span class="hljs-keyword">return</span> Text(<span class="hljs-string">'User details will appear here'</span>);
498              } <span class="hljs-keyword">else</span> {
499                <span class="hljs-keyword">return</span> Column(
500                  crossAxisAlignment: CrossAxisAlignment.start,
501                  children: [
502                    Text(<span class="hljs-string">'User ID: <span class="hljs-subst">${controller.user.value.id}</span>'</span>),
503                    Text(<span class="hljs-string">'Name: <span class="hljs-subst">${controller.user.value.name}</span>'</span>),
504                    <span class="hljs-comment">// Add more user details as needed</span>
505                  ],
506                );
507              }
508            }),
509          ],
510        ),
511      ),
512    );
513  }
514}
515</code></pre>
516<hr />
517<h2 id="heading-run">Run</h2>
518<ol>
519<li><p><strong>Install Dependencies</strong>: Ensure that you have all the required packages and dependencies installed. Run the following command:</p>
520<pre><code class="lang-bash"> flutter pub get
521</code></pre>
522</li>
523<li><p><strong>Generate Code</strong>: Since our app uses code generation tools like <code>freezed</code>, <code>json_serializable</code>, etc., it's important to generate necessary code files. Run the following command:</p>
524<pre><code class="lang-bash"> flutter pub run build_runner build --delete-conflicting-outputs
525</code></pre>
526<p> This command generates code that's needed for serialization, deserialization, and more. The <code>--delete-conflicting-outputs</code> flag helps resolve any conflicts that may arise during code generation.</p>
527</li>
528<li><p><strong>Run the App</strong>: Finally, you can launch the app using the following command:</p>
529<pre><code class="lang-bash"> flutter run
530</code></pre>
531</li>
532</ol>
533<hr />
534<h2 id="heading-conclusion">Conclusion</h2>
535<p>Congratulations! 🥳 You've just embarked on a thrilling journey of implementing clean architecture in Flutter using GetX. Clean architecture can be a bit challenging at first, but once you get the hang of it, it makes your code more maintainable and scalable.</p>
536<p>So, go forth and create amazing Flutter apps with clean architecture. Happy coding! 🚀📱✨</p>
537</div></div></div></div><div class="max-w-2xl mx-auto border-t border-border pt-8 space-y-5"><div class="flex flex-wrap items-center gap-2"><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/flutter">#<!-- -->flutter</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/app-development">#<!-- -->app-development</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/mobile-development">#<!-- -->mobile-development</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/clean-code">#<!-- -->
537clean-code</a><a class="inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary" href="/tag/developer">#<!-- -->developer</a></div><div class="flex items-center gap-2 text-sm text-muted-foreground" title="7,341 views"><i class="fa-regular fa-chart-simple h-4 w-4" aria-hidden="true"></i><span class="font-medium">7.3K<!-- --> views</span></div></div></div></article></div></div></div></section><section class="pb-8"><div class="px-4 sm:px-8"><div class="w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="max-w-4xl w-full mx-auto"><div class="rounded-xl border bg-card text-card-foreground p-5 sm:p-8"><section class="space-y-5"><div class="flex items-center justify-between"><h2 class="text-lg font-bold tracking-tight">Comments</h2><a target="_blank" rel="noopener" class="inline-flex items-center gap-2 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:border-primary hover:text-primary" href="https://hashnode.com/posts/building-flutter-apps-with-clean-architecture-using-getx/651e8bf5f457ef18124ee464"><i class="fa-regular fa-comment" aria-hidden="true"></i>Join the discussion</a></div><p class="text-sm text-muted-foreground py-5 text-center">No comments yet.<!-- --> <a target="_blank" rel="noopener" class="text-primary hover:underline" href="https://hashnode.com/posts/building-flutter-apps-with-clean-architecture-using-getx/651e8bf5f457ef18124ee464">Be the first to comment.</a></p></section></div></div></div></div></section><section class="pb-8"><div class="px-4 sm:px-8"><div class="w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="max-w-4xl w-full mx-auto"><div class="rounded-xl border bg-card text-card-foreground overflow-hidden"><div class="p-5 sm:p-6 space-y-3"><a class="group flex items-center gap-2" href="/series/what-the-flutter"><i class="fa-jelly fa-layer-group h-4 w-4 text-muted-foreground" aria-hidden="true"></i><h2 class="text-base font-semibold leading-snug tracking-tight text-foreground group-hover:text-primary transition-colors">What the Flutter</h2></a><div class="flex items-center gap-3 text-sm text-muted-foreground"><span>Part <!-- -->3<!-- --> of <!-- -->5</span><div class="flex-1 h-1 rounded-full bg-muted overflow-hidden"><div class="h-full rounded-full bg-primary transition-all" style="width:60%"></div></div></div><p class="text-sm leading-relaxed text-muted-foreground line-clamp-2">In this series, I will cover interesting micro articles on how to get more comfortable with Flutter &amp; Dart.</p></div><a class="block border-t border-border p-5 sm:p-6 hover:bg-muted transition-colors" href="/darts-extension-methods-demystified"><span class="text-sm text-muted-foreground">Up next</span><h3 class="text-base sm:text-lg font-semibold leading-snug tracking-tight text-foreground mt-1">Dart&#x27;s Extension Methods: Demystified</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-2 mt-1">Unleash the power of Dart&#x27;s Extension Methods with this simple guide</p></a></div></div></div></div></section><section class="pb-8"><div class="px-4 sm:px-8"><div class="w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]"><div class="max-w-4xl w-full mx-auto"><section class="space-y-6"><h2 class="text-2xl font-bold tracking-tight text-balance">More from this blog</h2><div class="flex flex-col gap-6"><article aria-labelledby="_R_panpfalb_" class="h-full"><a class="block h-full" href="/scalable-microfrontends-architecting-a-decoupled-ui-with-module-federation"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_panpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">Scalable Microfrontends: Architecting a Decoupled UI with Module Federation</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">Have you ever worked on a frontend codebase so large that you feared deploying a CSS change to the Settings page because it might somehow break the Checkout flow? Or perhaps y
537ou&#x27;ve struggled to align three different teams working on the same React re...</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">Nov 19, 2025</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>7 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">242</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="Scalable Microfrontends: Architecting a Decoupled UI with Module Federation" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763536062182/2941dcbc-787b-4892-9dad-d0671898a96b.png"/></div></div></div></div></a></article><article aria-labelledby="_R_19anpfalb_" class="h-full"><a class="block h-full" href="/mastering-websockets-in-flutter-a-comprehensive-guide"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_19anpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">Mastering WebSockets in Flutter: A Comprehensive Guide</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">Real-Time Communication in Flutter: A Comprehensive Guide to Implementing WebSockets for Seamless Connectivity</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">Sep 3, 2024</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>5 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">778</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="Mastering WebSockets in Flutter: A Comprehensive Guide" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725355834204/675ae9ac-bcfd-49cf-b9e5-923718f17489.png"/></div></div></div></div></a></article><article aria-labelledby="_R_1panpfalb_" class="h-full"><a class="block h-full" href="/mastering-fcm-in-flutter-your-ultimate-guide-to-engaging-notifications"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_1panpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">Mastering FCM in Flutter: Your Ultimate Guide to Engaging Notifications</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">Elevate Your Flutter App&#x27;s Notification Game: Master Firebase Cloud Messaging Like a Pro!</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">Feb 16, 2024</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>8 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">966</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="Mastering FCM in Flutter: Your Ultimate Guide to Engaging Notifications" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1708024953706/32db23ef-4729-49ff-a9fd-ccb8a47f56ce.png"/></div></div></div></div></a></article><article aria-labelledby="_R_29anpfalb_" class="h-full"><a class="block h-full" href="/darts-extension-methods-demystified"><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 h-full"><div class="grid h-full gap-4 p-4 items-stretch md:gap-6 md:p-6 md:grid-cols-[1.2fr_1fr]"><div class="flex flex-col gap-4"><header class="flex flex-col gap-3"><h3 id="_R_29anpfalb_" class="text-foreground text-lg sm:text-xl font-bold leading-snug tracking-tight transition-colors line-clamp-3">Dart&#x27;s Extension Methods: Demystified</h3><p class="text-sm leading-relaxed text-muted-foreground line-clamp-3">Unleash the power of Dart&#x27;s Extension Methods with this simple guide</p></header><div class="flex flex-wrap items-center gap-3 text-sm text-muted-foreground"><span class="whitespace-nowrap">Jun 19, 2022</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span>6 min read</span><span class="whitespace-nowrap"><span class="mr-3" aria-hidden="true">·</span><span data-state="closed" data-slot="tooltip-trigger">482</span></span></div></div><div class="flex items-center md:justify-end order-first md:order-last"><div class="relative overflow-hidden rounded-lg bg-muted aspect-[800/420] w-full md:max-w-[320px]"><img alt="Dart&#x27;s Extension Methods: Demystified" loading="lazy" decoding="async" data-nimg="fill" class="object-cover object-center" style="position:absolute;height:100%;width:100%;left:0;top:0;right:0;bottom:0;color:transparent" src="https://cdn.hashnode.com/res/hashnode/image/upload/v1655627438428/aSqa2GT_L.png"/></div></div></div></div></a></article><div class="rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 p-5 flex flex-col"><div class="flex flex-col items-center text-center gap-5"><div class="flex flex-col items-center gap-3"><span class="relative flex overflow-hidden rounded-full h-16 w-16 shrink-0"><span class="flex h-full w-full items-center justify-center rounded-full bg-muted text-muted-foreground font-medium text-lg">A</span></span><div class="space-y-1"><p class="text-base font-semibold text-foreground leading-tight">Aditya&#x27;s Blog</p><p class="text-sm text-muted-foreground">10<!-- --> posts</p></div></div><div class="prose prose-sm dark:prose-invert max-w-xs mx-auto text-muted-foreground"><p>I convert scrap to sculptures!</p>
538</div><div class="flex flex-wrap items-center justify-center gap-3"><a href="https://twitter.com/BetaPundit" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Twitter"><i class="fa-brands fa-x-twitter" aria-hidden="true"></i></a><a href="https://github.com/BetaPundit" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="GitHub"><i class="fa-brands fa-github" aria-hidden="true"></i></a><a href="https://linkedin.com/in/aditya-sharma26" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="LinkedIn"><i class="fa-brands fa-linkedin" aria-hidden="true"></i></a>
538<a href="https://adityasharma.co" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Website"><i class="fa-jelly fa-globe" aria-hidden="true"></i></a><a href="https://hashnode.com/@BetaPundit" target="_blank" rel="noopener" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Hashnode"><i class="fa-brands fa-hashnode" aria-hidden="true"></i></a></div></div></div></div></section></div></div></div></section>
538<script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","url":"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx","mainEntityOfPage":"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx","headline":"Building Flutter Apps with Clean Architecture Using GetX","description":"Hello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure...","author":{"@type":"Person","name":"Aditya Sharma","url":"https://hashnode.com/@BetaPundit","image":"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg","sameAs":"https://x.com/BetaPundit"},"publisher":{"@type":"Organization","name":"Aditya's Blog","url":"https://blog.adityasharma.co"},"datePublished":"2023-10-05T10:12:05.108Z","dateModified":"2024-05-24T17:35:34.293Z","image":{"@type":"ImageObject","url":"https://cdn.hashnode.com/res/hashnode/image/upload/v1696485327830/8b86432c-2243-4136-8241-d2224d5cee4f.png"},"keywords":"Flutter, app development, Mobile Development, clean code, Developer"}</script>
538<footer class="px-4 pt-8 pb-20 sm:px-8 text-muted-foreground" aria-label="Footer"><div class="w-full max-w-4xl mx-auto"><div class="flex flex-col items-center gap-4 text-center text-sm"><p class="font-medium"><time dateTime="2026"><span class="font-sans">©</span> <!-- -->2026</time> <!-- -->Aditya&#x27;s Blog</p><nav aria-label="Footer navigation"><ul class="flex flex-wrap justify-center gap-4 text-sm tracking-wide"><li><a class="transition hover:text-foreground" href="/archive">Archive</a></li><li><a class="transition hover:text-foreground" target="_blank" rel="noopener" href="https://hashnode.com/privacy-policy">Privacy</a></li><li><a class="transition hover:text-foreground" target="_blank" rel="noopener" href="https://hashnode.com/terms">Terms</a></li></ul></nav><div class="pt-8 flex justify-center gap-6"><a target="_blank" rel="noopener" class="flex items-center gap-2 text-sm transition hover:text-foreground" title="Sitemap" href="/sitemap.xml"><i class="fa-regular fa-sitemap"></i><span>Sitemap</span></a><a target="_blank" rel="noopener" class="flex items-center gap-2 text-sm transition hover:text-foreground" title="RSS Feed" href="/rss.xml"><i class="fa-regular fa-rss"></i><span>RSS</span></a></div><div class="pt-10 text-center"><a target="_blank" rel="noopener" aria-label="Visit Hashnode (opens in a new tab)" class="inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg]:size-4 [&amp;_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-10 rounded-md text-xl px-5 py-4" href="https://hashnode.com/?utm_source=https%3A%2F%2Fblog.adityasharma.co&amp;utm_medium=referral&amp;utm_campaign=blog_footer_logo&amp;utm_content=logo"><svg fill="none" viewBox="0 0 282 39" aria-hidden="true" class="h-6 w-auto min-w-[9.5rem] text-muted-foreground transition hover:text-foreground"><path fill="currentColor" d="M2.68 13.032c-3.573 3.505-3.573 9.363 0 12.936L13.032 36.32c3.505 3.573 9.363 3.573 12.936 0L36.32 25.968c3.573-3.573 3.573-9.431 0-12.936L25.968 2.68c-3.573-3.573-9.431-3.573-12.936 0zm12.211 1.935c2.507-2.521 6.582-2.544 9.104-.038s2.544 6.582.038 9.104-6.582 2.544-9.104.038-2.544-6.582-.038-9.104"></path><path fill="currentColor" d="M268.379 39q-6.322 0-10.35-4.108-4.002-4.134-4.002-10.483 0-6.296 4.002-10.43 4-4.135 10.163-4.135 6.028 0 9.817 3.734 3.814 3.735 3.814 9.497v2.8h-24.248v-4.054h17.633q-.187-2.747-2.054-4.508-1.868-1.76-4.962-1.76-3.28-.001-5.388 2.374-2.108 2.373-2.108 6.375 0 4.188 2.348 6.695 2.347 2.481 5.895 2.481 1.68 0 2.934-.453a8.5 8.5 0 0 0 2.321-1.307q1.041-.854 2.268-2.614l4.694 2.96q-1.707 2.748-3.494 4.135a12.7 12.7 0 0 1-3.975 2.081q-2.187.72-5.308.72m-33.496 0q-5.575 0-9.283-4.028-3.681-4.029-3.681-10.484 0-6.43 3.761-10.51 3.76-4.108 9.47-4.108 2.8 0 5.175 1.2 2.4 1.174 3.628 3.148l.026-.08V0h7.016v38.2h-6.722v-4.722l.64.8-.694.133q-1.28 2.108-3.788 3.361Q237.924 39 234.883 39m1.627-6.002q3.308 0 5.495-2.428t2.188-6.135q0-3.762-2.214-6.109-2.214-2.374-5.469-2.374-3.28 0-5.522 2.347-2.214 2.348-2.214 6.163 0 3.76 2.214 6.162 2.214 2.374 5.522 2.374M205.811 39q-6.376 0-10.537-4.161-4.161-4.188-4.161-10.404 0-6.321 4.161-10.456 4.188-4.135 10.537-4.135 6.348 0 10.537 4.161 4.188 4.161 4.188 10.43 0 6.216-4.188 10.404Q212.186 39 205.811 39m0-6.162q3.307 0 5.575-2.374 2.267-2.374 2.267-6.055 0-3.707-2.294-6.056-2.267-2.347-5.548-2.347-3.308 0-5.575 2.374-2.268 2.348-2.268 6.029t2.241 6.055q2.268 2.374 5.602 2.374M162.586 38.2V10.644h6.802v5.015l-.8-.987h.906q1.308-2.187 3.762-3.494 2.48-1.335 5.228-1.334 4.615 0 7.256 2.8 2.667 2.776 2.667 7.603V38.2h-7.069V21.794q0-2.854-1.387-4.401t-4.134-1.547q-2.614 0-4.402 1.974-1.76 1.974-1.76 4.881V38.2zm-30.181 0V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-7.069V21.794q0-2.827-1.387-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.814 2.027-1.814 4.962v15.391zm-14.416.8q-2.882 0-5.015-.64-2.108-.614-3.761-1.84-1.654-1.228-2.668-2.695l-.987-1.467 4.695-3.174.773 1.067a9.1 9.1 0 0 0 1.788 1.814 8.2 8.2 0 0 0 2.32 1.2q1.308.4 3.068.4 2.161 0 3.441-.8 1.308-.8 1.307-2.294 0-1.36-1.147-2.108-1.12-.747-5.068-1.654-5.335-1.253-7.656-3.334-2.32-2.107-2.321-5.362 0-3.76 3.148-6.002 3.148-2.267 7.923-2.267 2.454 0 4.348.507t3.388 1.493q1.493.988 2.454 2.188l.987 1.2-4.482 3.255-.667-.774a7.4 7.4 0 0 0-1.627-1.387 6.4 6.4 0 0 0-2.054-.88 9.5 9.5 0 0 0-2.401-.294q-1.974 0-3.201.8-1.2.774-1.2 1.921 0 1.254 1.12 2.027 1.147.748 5.229 1.681 5.334 1.227 7.629 3.308 2.294 2.055 2.294 5.308 0 4.029-3.228 6.43Q123.217 39 117.989 39M87.61 39q-4.508 0-7.335-2.454-2.828-2.481-2.828-6.482 0-4.188 3.254-6.669t8.483-2.48q2.082 0 4.028.426 1.974.427 3.361 1.12v-1.6q0-2.588-1.813-4.082-1.788-1.493-4.615-1.493-1.628 0-2.801.373a8.7 8.7 0 0 0-2.188.987q-.987.614-1.84 1.414l-.854.8-4.081-3.788 1.307-1.173a18.7 18.7 0 0 1 2.934-2.108q1.601-.933 3.601-1.44t4.589-.507q5.868 0 9.229 2.88 3.361 2.882 3.361 8.084V38.2h-6.749v-5.922l1.654 2.187h-1.76q-1.308 2.055-3.655 3.308Q90.545 39 87.611 39m1.814-4.988q3.148 0 5.149-2.187 2-2.215 2-5.309v-.08q-1.12-.693-2.72-1.094a13 13 0 0 0-3.388-.426q-2.828 0-4.562 1.2-1.734 1.173-1.734 3.521 0 2.028 1.468 3.201t3.787 1.174M49 38.2V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-
5387.07V21.794q0-2.827-1.386-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.815 2.027-1.814 4.962v15.391z"></path></svg></a></div></div></div></footer></main><!--$--><!--/$-->
538<script src="/_next/static/chunks/webpack-55b36f0cd6c208fe.js" id="_R_" async=""></script>
538<script>(self.__next_f=self.__next_f||[]).push([0])</script>
538<script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[16174,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"ThemeProvider\"]\n3:I[82180,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"TooltipProvider\"]\n4:I[63851,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"UrqlProvider\"]\n5:I[82714,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"ConsentManager\"]\n6:I[9766,[],\"\"]\n7:I[98924,[],\"\"]\n8:I[33293,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"4345\",\"static/chunks/app/not-found-b861935655c5698b.js\"],\"default\"]\n9:I[21447,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"Integrations\"]\na:I[35514,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"2810\",\"static/chunks/2810-6b74d77b4b0dc790.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"7177\",\"static/chunks/app/layout-6ae7b4b583d4bba3.js\"],\"Analytics\"]\nc:I[24431,[],\"OutletBoundary\"]\ne:I[15278,[],\"AsyncMetadataOutlet\"]\n10:I[24431,[],\"ViewportBoundary\"]\n12:I[24431,[],\"MetadataBoundary\"]\n13:\"$Sreact.suspense\"\n15:I[57150,[],\"\"]\n:HL[\"/_next/static/media/22a5144ee8d83bca-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/72a2a501b11d0ccb.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/8934b430a4adeb19-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/8e9860b6e62d6359-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/c4b700dcb2187787-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/media/e4af272ccee01ff0-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/css/0d16c72050851854.css\",\"style\"]\n:HL[\"/_next/static/css/7ffcc02a3c67389b.css\",\"style\"]\n:HL[\"/_next/static/css/c20e76e7d11dec32.css\",\"style\"]\n:HL[\"/_next/static/css/59fefb2212bd5b60.css\",\"style\"]\n0:{\"P\":null,\"b\":\"RrCGzNVHUG674hJ_sjvms\",\"p\":\"\",\"c\":[\"\",\"building-flutter-apps-with-clean-architecture-using-getx?x-host=blog.adityasharma.co\"],\"i\":false,\"f\":[[[\"\",{\"children\":[[\"slug\",\"building-flutter-apps-with-clean-architecture-using-getx\",\"c\"],{\"children\":[\"__PAGE__?{\\\"x-host\\\":\\\"blog.adityasharma.co\\\"}\",{}]}]},\"$undefined\",\"$undefined\",true],[\"\",[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/0d16c72050851854.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/7ffcc02a3c67389b.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/c20e76e7d11dec32.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/59fefb2212bd5b60.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefi"])</script>
538<script>self.__next_f.push([1,"ned\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[\"$\",\"script\",null,{\"type\":\"text/javascript\",\"dangerouslySetInnerHTML\":{\"__html\":\"window.addEventListener('message',function(e){var d=e.data;if(!d||d.__hnWidget!==1||typeof d.h!=='number'||d.h\u003c=0)return;var f=document.querySelectorAll('.hn-widget-frame');for(var i=0;i\u003cf.length;i++){if(f[i].contentWindow===e.source){f[i].style.height=d.h+'px';break;}}});\"}}]}],[\"$\",\"body\",null,{\"className\":\"__variable_c2612a __variable_246ccd __variable_3069d5 bg-background dark:bg-background\",\"children\":[\"$\",\"$L2\",null,{\"attribute\":\"class\",\"defaultTheme\":\"system\",\"enableSystem\":true,\"disableTransitionOnChange\":true,\"children\":[\"$\",\"$L3\",null,{\"children\":[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$L5\",null,{\"children\":[[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"$L8\",null,{}],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],[\"$\",\"$L9\",null,{}],[\"$\",\"$La\",null,{}]]}]}]}]}]}]]}]]}],{\"children\":[[\"slug\",\"building-flutter-apps-with-clean-architecture-using-getx\",\"c\"],[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1\",\"c\",{\"children\":[\"$Lb\",null,[\"$\",\"$Lc\",null,{\"children\":[\"$Ld\",[\"$\",\"$Le\",null,{\"promise\":\"$@f\"}]]}]]}],{},null,false]},null,false]},null,false],[\"$\",\"$1\",\"h\",{\"children\":[null,[[\"$\",\"$L10\",null,{\"children\":\"$L11\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]],[\"$\",\"$L12\",null,{\"children\":[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$13\",null,{\"fallback\":null,\"children\":\"$L14\"}]}]}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$15\",[]],\"s\":false,\"S\":false}\n11:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\nd:null\n16:I[80622,[],\"IconMark\"]\nf:{\"metadata\":[[\"$\",\"title\",\"0\",{\"children\":\"Flutter App Development: Clean Architecture with GetX\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Elevate your Flutter app development with Clean Architecture and GetX. Learn to build efficient, organized, and high-performance apps. Dive in now!\"}],[\"$\",\"link\",\"2\",{\"rel\":\"canonical\",\"href\":\"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx\"}],[\"$\",\"meta\",\"3\",{\"property\":\"og:title\",\"content\":\"Flutter App Development: Clean Architecture with GetX\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:description\",\"content\":\"Elevate your Flutter app development with Clean Architecture and GetX. Learn to build efficient, organized, and high-performance apps. Dive in now!\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:url\",\"content\":\"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:site_name\",\"content\":\"Aditya's Blog\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:image\",\"content\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1696500681728/c3e1000f-3ed5-48d7-a5f8-08fc8242a340.png\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:type\",\"content\":\"article\"}],[\"$\",\"meta\",\"11\",{\"property\":\"article:published_time\",\"content\":\"2023-10-05T10:12:05.108Z\"}],[\"$\",\"meta\",\"12\",{\"property\":\"article:modified_time\",\"content\":\"2024-05-24T17:35:34.293Z\"}],[\"$\",\"meta\",\"13\",{\"property\":\"article:author\",\"content\":\"https://blog.adityasharma.co/@BetaPundit\"}],[\"$\",\"meta\",\"14\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"15\",{\"name\":\"twitter:creator\",\"content\":\"https://twitter.com/BetaPundit\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:title\",\"content\":\"Flutter App Development: Clean Archit"])</script>
538<script>self.__next_f.push([1,"ecture with GetX\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:description\",\"content\":\"Elevate your Flutter app development with Clean Architecture and GetX. Learn to build efficient, organized, and high-performance apps. Dive in now!\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:image\",\"content\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1696500681728/c3e1000f-3ed5-48d7-a5f8-08fc8242a340.png\"}],[\"$\",\"link\",\"19\",{\"rel\":\"icon\",\"href\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602225116875/Bf-bb_MDw.jpeg\"}],[\"$\",\"$L16\",\"20\",{}]],\"error\":null,\"digest\":\"$undefined\"}\n14:\"$f:metadata\"\n17:I[652,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"8974\",\"static/chunks/app/page-cc18ae1a3a7a5ed8.js\"],\"BlogHeader\"]\n18:T9678,\u003cp\u003eHello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure, this guide will help you build organized and maintainable Flutter apps. So, grab your coding capes, and let's dive in!\u003c/p\u003e\n\u003ch2 id=\"heading-what-is-clean-architecture\"\u003eWhat is Clean Architecture?\u003c/h2\u003e\n\u003cp\u003eBefore we delve into the nitty-gritty of Flutter clean architecture with GetX, let's start with a quick introduction to clean architecture.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePros of Clean Architecture:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eModularity:\u003c/strong\u003e Clean architecture promotes a modular approach, making your codebase easier to understand and maintain.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eTestability:\u003c/strong\u003e It encourages writing unit tests and makes your codebase more test-friendly.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eScalability:\u003c/strong\u003e Your app can grow and evolve without major architectural changes.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eIndependence:\u003c/strong\u003e The architecture keeps external frameworks and libraries at bay, preventing them from tightly coupling with your code.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eCons of Clean Architecture:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eInitial Complexity:\u003c/strong\u003e Setting up a clean architecture project might seem more daunting compared to a simple structure.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eLearning Curve:\u003c/strong\u003e Developers new to clean architecture may take some time to fully grasp its concepts.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow that we're clear on the basics, let's roll up our sleeves and get started!\u003c/p\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-project-setup\"\u003eProject Setup\u003c/h2\u003e\n\u003cp\u003eThis project is 
538written using the current latest stable versions of:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://docs.flutter.dev/development/tools/sdk/releases\"\u003e\u003cstrong\u003eFlutter\u003c/strong\u003e\u003c/a\u003e: v3.10.6\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://dart.dev/guides/language/evolution\"\u003e\u003cstrong\u003eDart\u003c/strong\u003e\u003c/a\u003e: v3.0.6\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"heading-packages\"\u003ePackages\u003c/h3\u003e\n\u003cp\u003eBefore we start coding, let's gather our tools and libraries. Here are the essential packages we'll use:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/get\"\u003e\u003cstrong\u003egetX\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e The Swiss Army knife for state management. (You can use any kind of state management you prefer to work with such as \u003ca target=\"_blank\" href=\"https://pub.dev/packages/provider\"\u003e\u003cstrong\u003eprovider\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003eor\u003c/strong\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/flutter_bloc\"\u003e\u003cstrong\u003eflutter_bloc\u003c/strong\u003e\u003c/a\u003e)\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/freezed\"\u003e\u003cstrong\u003efreezed\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e A code generation package for creating immutable data models.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/retrofit\"\u003e\u003cstrong\u003eretrofit\u003c/strong\u003e\u003c/a\u003e \u003cstrong\u003e\u0026amp;\u003c/strong\u003e \u003ca target=\"_blank\" href=\"https://pub.dev/packages/dio\"\u003e\u003cstrong\u003edio\u003c/strong\u003e\u003c/a\u003e\u003cst"])</script>
538<script>self.__next_f.push([1,"rong\u003e:\u003c/strong\u003e For handling HTTP requests.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/get_it\"\u003e\u003cstrong\u003eget_it\u003c/strong\u003e\u003c/a\u003e \u003cstrong\u003e\u0026amp;\u003c/strong\u003e \u003ca target=\"_blank\" href=\"https://pub.dev/packages/injectable\"\u003e\u003cstrong\u003einjectable\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e For dependency injection.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/go_router\"\u003e\u003cstrong\u003ego_router\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e A declarative routing package that provides a convenient, URL-based API for navigating between different screens.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/pretty_dio_logger\"\u003e\u003cstrong\u003epretty_dio_logger\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e A delightful logger for HTTP requests.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLet's include these packages in our \u003ccode\u003epubspec.yaml\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-yaml\"\u003e\u003cspan class=\"hljs-attr\"\u003edependencies:\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eget:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^4.6.6\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eretrofit:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^4.0.2\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003edio:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^5.3.3\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eget_it:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^7.6.4\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003einjectable:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.3.0\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003ego_router:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^11.1.2\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003epretty_dio_logger:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^1.3.1\u003c/span\u003e\n\n\u003cspan class=\"hljs-attr\"\u003edev_dependencies:\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eflutter_test:\u003c/span\u003e\n    \u003cspan class=\"hljs-attr\"\u003esdk:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003eflutter\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eflutter_lints:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.0.0\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003ebuild_runner:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.4.6\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003efreezed:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.4.3\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003ejson_serializable:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^6.7.1\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eretrofit_generator:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^7.0.8\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003einjectable_generator:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.4.0\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-folder-structure\"\u003eFolder Structure\u003c/h3\u003e\n\u003cp\u003eOrganization is key in clean architecture. Here's a recommended folder structure:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-c\"\u003elib/\n  ├── config/\n  │     ├── injection/\n  │     ├── router/\n  │     └── themes/\n  ├── data/\n  │     ├── datasources/\n  │     │    ├── local/\n  │     │    └── remote/\n  │     └── repositories/\n  ├── domain/\n  │     ├── models/\n  │     └── repositories/\n  ├── presentation/\n  │     ├── controllers/\n  │     ├── pages/\n  │     └── widgets/\n  ├── utils/\n  │     ├── constants/\n  │     ├── extensions/\n  │     └── resources/\n  └── main.dart\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe \u003cstrong\u003econfig\u003c/strong\u003e folder includes the configuration of the app (\u003cstrong\u003ethemes\u003c/strong\u003e, \u003cstrong\u003erouter\u003c/strong\u003e, ..etc) and anything else related to the app’s configurations.\u003c/p\u003e\n\u003cp\u003eThe \u003cstrong\u003eutils\u003c/strong\u003e folder, on the other hand, includes (\u003cstrong\u003econstants\u003c/strong\u003e, \u003cstrong\u003eextensions\u003c/strong\u003e, \u003cstrong\u003eresources\u003c/strong\u003e, ..etc) and anything related to that.\u003c/p\u003e\n\u003cp\u003eThe (\u003cstrong\u003edata\u003c/strong\u003e, \u003cstrong\u003edomain\u003c/strong\u003e and \u003cstrong\u003epresentation\u003c/strong\u003e) folders will be explained later in detail.\u003c/p\u003e\n\u003ch3 id=\"heading-architecture-and-dependency-rules\"\u003eArchitecture and Dependency Rules\u003c/h3\u003e\n\u003cp\u003eThe clean architecture consists of three primary layers:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eData Layer:\u003c/strong\u003e Responsible for data fetching and caching.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eDomain Layer:\u003c/strong\u003e Houses the core business logic.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003ePresentation Layer:\u003c/strong\u003e Manages the user interface and user interactions.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eEac
538h layer has its unique responsibilities, and dependencies flow inward. The Data Layer is independent, the Domain Layer depends on Data, and th"])</script>
538<script>self.__next_f.push([1,"e Presentation Layer relies on both Data and Domain.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://cdn.hashnode.com/res/hashnode/image/upload/v1696499988717/67904f8e-432e-4d19-86d3-491d790d0f1a.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-app-configuration\"\u003eApp Configuration\u003c/h2\u003e\n\u003cp\u003eBefore diving into the layers, let's configure our app. Here's a sneak peek:\u003c/p\u003e\n\u003ch3 id=\"heading-routing\"\u003eRouting\u003c/h3\u003e\n\u003cp\u003eRouting is a fundamental aspect of any Flutter app. We'll use the \u003ccode\u003ego_router\u003c/code\u003e package to handle navigation efficiently.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003eYou can absolutely choose whatever the routing package you prefer, and that suits your needs.\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eNow, after adding the package to your \u003ccode\u003epubspec.yaml\u003c/code\u003e file as we talked about it at the beginning of the article, create a folder in this path \u003cstrong\u003elib/config\u003c/strong\u003e and call it \u003cstrong\u003erouter\u003c/strong\u003e, then inside the folder create a file and name it \u003ccode\u003eapp_router.dart\u003c/code\u003e as shown below:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-meta\"\u003e@singleton\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eAppRouter\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e GoRouter _router = GoRouter(\n    debugLogDiagnostics: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n    initialLocation: \u003cspan class=\"hljs-string\"\u003e'/'\u003c/span\u003e,\n    routes: [\n      GoRoute(\n        path: \u003cspan class=\"hljs-string\"\u003e'/'\u003c/span\u003e,\n        builder: (context, state) =\u0026gt; \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e HomeScreen(),\n      ),\n      GoRoute(\n        path: \u003cspan class=\"hljs-string\"\u003e'/profile'\u003c/span\u003e,\n        builder: (context, state) =\u0026gt; \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e UserDetailsScreen(),\n      ),\n      ...\n      \u003cspan class=\"hljs-comment\"\u003e// Add more routes as needed\u003c/span\u003e\n  );\n\n  GoRouter \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e router =\u0026gt; _router;\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-theme\"\u003eTheme\u003c/h3\u003e\n\u003cp\u003eDefining a global theme ensures consistency in your app's appearance. Let's create a file \u003ccode\u003eapp_theme.dart\u003c/code\u003e inside \u003cstrong\u003elib/config/themes\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dtdcopedia/config/themes/app_colors.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:flutter/material.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eAppTheme\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e ThemeData \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e light {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e ThemeData(\n      appBarTheme: \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e AppBarTheme(\n        elevation: \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e,\n        color: AppColors.primary,\n      ),\n      scaffoldBackgroundColor: AppColors.background,\n      primaryColor: AppColors.primary,\n      splashColor: Colors.transparent,\n      fontFamily: \u003cspan class=\"hljs-string\"\u003e'Hind'\u003c/span\u003e,\n    );\n  }\n\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e ThemeData \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e dark {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e ThemeData(\n      appBarTheme: \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e AppBarTheme(\n        elevation: \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e,\n        color: AppColors.primary,\n      ),\n      scaffoldBackgroun
538dColor: AppColors.text,\n      primaryColor: AppColors.primary,\n      splashColor: Colors.transparent,\n      fontFamily: \u003cspan class=\"hljs-string\"\u003e'Hind'\u003c/span\u003e,\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis sets up your app's global theme with the specified colors, fonts, and more. You can customize it the way you like.\u003c/p\u003e\n\u003cdiv data-node-type=\"callout\"\u003e\n\u003cdiv data-node-type=\"callout-emoji\"\u003e💡\u003c/div\u003e\n\u003cdiv data-node-type=\"callout-text\"\u003eUse \u003cstrong\u003eThemeExtension\u003c/strong\u003e for creating custom themes: \u003ca target=\"_blank\" href=\"https://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa\"\u003ehttps://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa\u003c/a\u003e"])</script>
538<script>self.__next_f.push([1,"\u003c/div\u003e\n\u003c/div\u003e\n\n\u003ch3 id=\"heading-utils\"\u003e\u003cstrong\u003eUtils\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eIn the \u003ccode\u003eutils\u003c/code\u003e folder, you can organize your utility functions, constants, extensions, and resources neatly. Here's an example structure:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003elib/\n  ├── utils/\n  │     ├── constants/\n  │     │     └── app_constants.dart\n  │     ├── extensions/\n  │     │     ├── string_extensions.dart\n  │     │     └── date_extensions.dart\n  │     ├── resources/\n  │     │     └── data_state.dart\n  │     └── ...\n  └── ...\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eFor example, in \u003ccode\u003eapp_constants.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eAppConstants\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e appTitle = \u003cspan class=\"hljs-string\"\u003e'Flutter Clean Architecture'\u003c/span\u003e;\n\n \u003cspan class=\"hljs-comment\"\u003e// Networking and APIs\u003c/span\u003e\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e baseUrl = \u003cspan class=\"hljs-string\"\u003e'https://appi.example.com/'\u003c/span\u003e; \n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e defaultApiKey = \u003cspan class=\"hljs-string\"\u003e'YOUR_API_KEY'\u003c/span\u003e;\n\n \u003cspan class=\"hljs-comment\"\u003e// Storage and Databases\u003c/span\u003e\n \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e appTableName = \u003cspan class=\"hljs-string\"\u003e'app_table'\u003c/span\u003e;\n \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e databaseName = \u003cspan class=\"hljs-string\"\u003e'app_database.db'\u003c/span\u003e;\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd in \u003ccode\u003estring_extensions.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eextension\u003c/span\u003e StringExtensions \u003cspan class=\"hljs-keyword\"\u003eon\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e {\n  \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e capitalize() {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"\u003cspan class=\"hljs-subst\"\u003e${\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e[\u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e].toUpperCase()}\u003c/span\u003e\u003cspan class=\"hljs-subst\"\u003e${\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.substring(\u003cspan class=\"hljs-number\"\u003e1\u003c/span\u003e)}\u003c/span\u003e\"\u003c/span\u003e;\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIn the \u003cstrong\u003elib/utils/resources\u003c/strong\u003e create a file and name it \u003ccode\u003edata_state.dart\u003c/code\u003e which contains the following code:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003c/span\u003e{\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e T data;\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e DioError error;\n    \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e DataState({\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.data, \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.error});\n}\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataSuccess\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003c/span\u003e{ \n    \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e DataSuccess(T data): \u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e(data: data);\n}\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataFailed\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003c/span\u003e{ \n    \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e DataFailed (DioError error): \u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e(error: error);\n}\n\u003c/"])</script>
538<script>self.__next_f.push([1,"code\u003e\u003c/pre\u003e\n\u003cp\u003eThis wrapper class \u003ccode\u003eDataState\u003c/code\u003e can be used to wrap our entire network call in order to determine the state of the request being sent to the server and its response.\u003c/p\u003e\n\u003cp\u003eAs you can see in the code, we’ve two different states, one when we get a successful response (\u003cstrong\u003eDataSuccess\u003c/strong\u003e), and the other when an error occurs while sending the request or receiving the response (\u003cstrong\u003eDataFailed\u003c/strong\u003e).\u003c/p\u003e\n\u003cp\u003eThis is very important as later on when we will have too many requests and logic, you will see how minimized the code will become.\u003c/p\u003e\n\u003ch3 id=\"heading-dependency-injection\"\u003eDependency Injection\u003c/h3\u003e\n\u003cp\u003eDependency injection is a crucial part of clean architecture. We use \u003ccode\u003eget_it\u003c/code\u003e and \u003ccode\u003einjectable\u003c/code\u003e to manage our dependencies efficiently. Here's a glimpse of how it works:\u003c/p\u003e\n\u003cp\u003eFirst, inside \u003cstrong\u003elib/config/injection,\u003c/strong\u003e create a file named \u003ccode\u003einjection.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:get_it/get_it.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:injectable/injectable.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'injection.config.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e locator = GetIt.instance;\n\n\u003cspan class=\"hljs-meta\"\u003e@InjectableInit\u003c/span\u003e()\nFuture\u0026lt;\u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e\u0026gt; configureDependencies() {\n  \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e locator.init();\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNow, create a file named \u003ccode\u003eregister_module.dart\u003c/code\u003e inside the same folder. Here we can inject third party packages such as \u003cstrong\u003eSharedPreferences\u003c/strong\u003e \u0026amp; \u003cstrong\u003eDio\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'dart:io'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:injectable/injectable.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:pretty_dio_logger/pretty_dio_logger.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:shared_preferences/shared_preferences.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-meta\"\u003e@module\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRegisterModule\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-comment\"\u003e// Inject SharedPreferences\u003c/span\u003e\n  \u003cspan class=\"hljs-meta\"\u003e@preResolve\u003c/span\u003e\n  Future\u0026lt;SharedPreferences\u0026gt; \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e prefs =\u0026gt; SharedPreferences.getInstance();\n\n  \u003cspan class=\"hljs-comment\"\u003e// Inject Dio\u003c/span\u003e\n  \u003cspan class=\"hljs-meta\"\u003e@lazySingleton\u003c/span\u003e\n  Dio dio(SharedPreferences sharedPreferences) {\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e token = sharedPreferences.getString(\u003cspan class=\"hljs-string\"\u003e'token'\u003c/span\u003e) ?? \u003cspan class=\"hljs-string\"\u003e''\u003c/span\u003e;\n\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Dio(\n      BaseOptions(\n        baseUrl: AppConstants.baseUrl,\n        headers: {\n          \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (token.isNotEmpty) HttpHeaders.authorizationHeader: \u003cspan class=\"hljs-string\"\u003e\"Bearer \u003cspan class=\"hljs-subst\"\u003e$token\u003c/span\u003e\"\u003c/span\u003e,\n        },\n      ),\n    )..interceptors.add(\n        PrettyDioLogger(\n          requestHeader: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n          requestBody: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n          responseHeader: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n          maxWidth: \u003cspan class=\"hljs-number\"\u003e120\u003c/span\u003e,\n        ),\n      );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNow you can import and use these utility functions, constants \u0026amp; injected modules throughout your project for a clean and organized codebase. Finally, the \u003ccode\u003emain.dart\u003c/code\u003e file will look like this:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e main() \u003cspan class=\"hljs-keyword\"\u003easync\u003c/span\u003e {\n  "])</script>
538<script>self.__next_f.push([1,"WidgetsFlutterBinding.ensureInitialized();\n  \u003cspan class=\"hljs-keyword\"\u003eawait\u003c/span\u003e configureDependencies();\n  runApp(\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e MyApp());\n}\n\n\u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e _router = locator\u0026lt;AppRouter\u0026gt;().router;\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eMyApp\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eStatelessWidget\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e MyApp({\u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e.key});\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  Widget build(BuildContext context) {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e MaterialApp.router(\n      debugShowCheckedModeBanner: \u003cspan class=\"hljs-keyword\"\u003efalse\u003c/span\u003e,\n      title: AppConstants.appTitle,\n      theme: AppTheme.light,\n      darkTheme: AppTheme.dark,\n      routerConfig: _router,\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-domain-layer\"\u003e\u003cstrong\u003eDomain Layer\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eNow, let's dive into the Domain Layer. The Domain Layer is the core of the application's business logic. It encapsulates the rules, behaviours, and core functionality that define how the application operates. This layer is entirely independent of external factors such as databases, network calls, or the user interface.\u003c/p\u003e\n\u003cp\u003eIn \u003cstrong\u003eClean Architecture\u003c/strong\u003e, the \u003cstrong\u003eDomain Layer\u003c/strong\u003e should be independent of any specific implementation or framework. This means that the code in the Domain Layer should be written in a \u003cstrong\u003etechnology-agnostic\u003c/strong\u003e way, with no dependencies on external systems or libraries.\u003c/p\u003e\n\u003ch3 id=\"heading-entities-models\"\u003e\u003cstrong\u003eEntities (Models)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eEntities/Models are plain Dart classes that represent the core business objects of the application. They model the data that the app operates on and typically contain minimal logic. Entities are defined solely based on the business requirements, and they do not contain any database-specific or UI-related code.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFreezed\u003c/strong\u003e helps us create immutable data models effortlessly. Here's a sample domain model \u003ccode\u003euser.dart\u003c/code\u003e inside \u003cstrong\u003elib/domain/models\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003epart\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'user.freezed.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-meta\"\u003e@freezed\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUser\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003ewith\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003e_\u003c/span\u003e$\u003cspan class=\"hljs-title\"\u003eUser\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003efactory\u003c/span\u003e User({\n    \u003cspan class=\"hljs-keyword\"\u003erequired\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id,\n    \u003cspan class=\"hljs-keyword\"\u003erequired\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e name,\n    \u003cspan class=\"hljs-comment\"\u003e// Add more fields as 
538needed\u003c/span\u003e\n  }) = _User;\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-repositories-abstract-definitions\"\u003e\u003cstrong\u003eRepositories (Abstract Definitions)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eIn the Domain Layer, repositories are defined as abstract classes or interfaces. These abstract definitions declare the methods for interacting with data without specifying how data is fetched or stored. The concrete implementations of these repositories are located in the Data Layer.\u003c/p\u003e\n\u003cp\u003eRepositories are interfaces that define data sources for our domain layer. For example, \u003ccode\u003euser_repository.dart\u003c/code\u003e inside \u003cstrong\u003elib/domain/repositories\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserRepository\u003c/span\u003e \u003c/span\u003e{\n  Future\u0026lt;UserEntity\u0026gt; getUserById(\u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"heading-data-layer\"\u003e\u003cstrong\u003eData Layer\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eThe Data Layer is the foundational layer responsible for handling data operations in a Flutter app. Its primary purpose is to abstract and encapsulate data sources, ensuring that the rest o"])</script>
538<script>self.__next_f.push([1,"f the application doesn't need to know the specifics of data retrieval and storage.\u003c/p\u003e\n\u003ch3 id=\"heading-data-sources\"\u003e\u003cstrong\u003eData Sources\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eData Sources are responsible for interacting with external data providers, such as APIs, databases, or local storage. They define the interfaces for fetching and storing data. This layer is typically implemented using packages like \u003ccode\u003eRetrofit\u003c/code\u003e, \u003ccode\u003eDio\u003c/code\u003e, or other HTTP client libraries for API interactions, as well as database libraries like \u003ccode\u003eSQFLite\u003c/code\u003e for local storage.\u003c/p\u003e\n\u003cp\u003eHere's a sample data source service \u003ccode\u003euser_data_source.dart\u003c/code\u003e inside \u003cstrong\u003elib/data/datasources/remote\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-meta\"\u003e@RestApi\u003c/span\u003e(baseUrl: \u003cspan class=\"hljs-string\"\u003e'https://api.example.com/'\u003c/span\u003e)\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserDataSource\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003efactory\u003c/span\u003e UserDataSource(Dio dio) = _UserDataSource;\n\n  \u003cspan class=\"hljs-meta\"\u003e@GET\u003c/span\u003e(\u003cspan class=\"hljs-string\"\u003e'/users/{id}'\u003c/span\u003e)\n  Future\u0026lt;UserEntity\u0026gt; getUserById(\u003cspan class=\"hljs-meta\"\u003e@Path\u003c/span\u003e() \u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNow, create \u003ccode\u003edata_source_injection.dart\u003c/code\u003e file inside \u003cstrong\u003elib/config/injection\u003c/strong\u003e and inject the \u003cstrong\u003eUserDataSource\u003c/strong\u003e class as a module:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:your_package_name/data/datasources/remote/user_data_source.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:injectable/injectable.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-meta\"\u003e@module\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataSourceInjectableModule\u003c/span\u003e \u003c/span\u003e{\n  UserDataSource getUserDataSource(Dio dio) =\u0026gt; UserDataSource(dio);\n  \u003cspan class=\"hljs-comment\"\u003e// Add more data dources as needed\u003c/span\u003e\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-repositories-implementation\"\u003e\u003cstrong\u003eRepositories (Implementation)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eRepositories act as intermediaries between Data Sources and the Domain Layer. They provide a clean and consistent API for accessing data. Repositories are defined as abstract classes in the Domain Layer, listing methods like \u003ccode\u003egetUserById()\u003c/code\u003e or \u003ccode\u003efetchPosts()\u003c/code\u003e. The concrete implementations of these repositories, found in the Data Layer, interact with Data Sources to fetch and manipulate data. Repositories also manage data caching and storage.\u003c/p\u003e\n\u003cp\u003eBefore we implement the \u003cstrong\u003eUserRepository\u003c/strong\u003e class, we need to create a base class called \u003cstrong\u003eBaseApiRepository\u003c/strong\u003e, create a folder inside the \u003cstrong\u003edata\u003c/strong\u003e folder and name it \u003cstrong\u003ebase\u003c/strong\u003e, then create a file inside it and name it \u003ccode\u003ebase_api_repository.dart\u003c/code\u003e which contains the following:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'dart:io'\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eshow\u003c/span\u003e HttpStatus;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:retrofit/dio.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:your_project_name/utils/resources/data_state.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:flutter/material.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBaseApiRepository\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-meta\"\u003e@protected\u003c/span\u003e\n  Future\u0026lt;DataState\u0026lt;T?\u0026gt;\u0026gt; getStateOf\u0026lt;T\u0026gt;({\n    \u003cspan class=\"hljs-keyword\"\u003erequired\u003c/span\u003e Future\u0026lt;Ht"])</script>
538<script>self.__next_f.push([1,"tpResponse\u0026lt;T\u0026gt;\u0026gt; \u003cspan class=\"hljs-built_in\"\u003eFunction\u003c/span\u003e() request,\n  }) \u003cspan class=\"hljs-keyword\"\u003easync\u003c/span\u003e {\n    \u003cspan class=\"hljs-keyword\"\u003etry\u003c/span\u003e {\n      \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e httpResponse = \u003cspan class=\"hljs-keyword\"\u003eawait\u003c/span\u003e request();\n      \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (httpResponse.response.statusCode == HttpStatus.created || httpResponse.response.statusCode == HttpStatus.ok) {\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e DataSuccess(httpResponse.data);\n      } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (httpResponse.response.statusCode == HttpStatus.unauthorized) {\n        \u003cspan class=\"hljs-keyword\"\u003ethrow\u003c/span\u003e DataFailed(DioException(\n          response: httpResponse.response,\n          requestOptions: httpResponse.response.requestOptions,\n        ));\n      } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e {\n        \u003cspan class=\"hljs-keyword\"\u003ethrow\u003c/span\u003e DioException(\n          response: httpResponse.response,\n          requestOptions: httpResponse.response.requestOptions,\n        );\n      }\n    } \u003cspan class=\"hljs-keyword\"\u003eon\u003c/span\u003e DioException \u003cspan class=\"hljs-keyword\"\u003ecatch\u003c/span\u003e (error) {\n      \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e DataFailed(error);\n    }\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis base class plays an important role here, which wraps our API call that comes from the data source \u003ccode\u003euser_data_source.dart\u003c/code\u003e and return a \u003cstrong\u003eDataState\u003c/strong\u003e object instead of \u003cstrong\u003eHttpResponse\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eBy doing so, we’re reducing the boilerplate code in each method without the need to write “\u003cstrong\u003etry-catch”\u003c/strong\u003e or “\u003cstrong\u003eif”\u003c/strong\u003e statements everywhere because you only need to pass the request in the “\u003cstrong\u003egetStateOf\u003c/strong\u003e” method and this should do the rest and return the result based on the generic “\u003cstrong\u003eT\u003c/strong\u003e” type.\u003c/p\u003e\n\u003cp\u003eNow create \u003ccode\u003euser_repository_impl.dart\u003c/code\u003e inside \u003cstrong\u003elib/data/repositories\u003c/strong\u003e \u0026amp; implement the previously defined \u003cstrong\u003eUserRepository\u003c/strong\u003e class as below:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-meta\"\u003e@Injectable\u003c/span\u003e(\u003cspan class=\"hljs-keyword\"\u003eas\u003c/span\u003e: UserRepository)\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserRepositoryImpl\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBaseApiRepository\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eimplements\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserRepository\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e UserDataSource _userDataSource;\n  UserRepositoryImpl(\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e._userDataSource);\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  Future\u0026lt;DataState\u0026lt;User?\u0026gt;\u0026gt; getUserById(\u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id) {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e getStateOf\u0026lt;User\u0026gt;(\n      request: () =\u0026gt; _userDataSource.getUserById(id),\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"heading-presentation-layer\"\u003ePresentation Layer\u003c/h2\u003e\n\u003cp\u003eLast but not least, we arrive at the Presentation Layer, where we build the user interface, controllers, screens, and common widgets.\u003c/p\u003e\n\u003cp\u003eThe benefit of using \u003cstrong\u003ePresentation Layer\u003c/strong\u003e is that it makes the codebase more modular and maintainable. By separating the user interface from the business logic and infrastru
538cture, it becomes easier to modify and extend the application over time. Also, it allows for different user interfaces to be developed and used with the same underlying business logic. For example, a \u003cstrong\u003eweb application\u003c/strong\u003e, a \u003cstrong\u003emobile application\u003c/strong\u003e, and a \u003cstrong\u003edesktop application\u003c/strong\u003e could all use the same Domain Layer but have different Presentation Layers that are optimized for their respective platforms.\u003c/p\u003e\n\u003ch3 id=\"heading-controllers\"\u003eControllers\u003c/h3\u003e\n\u003cp\u003eControllers manage the state and business logic of our app. Here's a snippet of a controller \u003ccode\u003euser_controller.dart\u003c/code\u003e inside \u003cstrong\u003elib/presentation/controllers\u003c/strong\u003e using GetX:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class="])</script>
538<script>self.__next_f.push([1,"\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserController\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eGetxController\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e UserController \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e call =\u0026gt; Get.find();\n\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e UserRepository _userRepository = locator\u0026lt;UserRepository\u0026gt;();\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e Rx\u0026lt;User\u0026gt; user = Rx\u0026lt;User\u0026gt;(User(id: \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e, name: \u003cspan class=\"hljs-string\"\u003e''\u003c/span\u003e));\n\n  Future\u0026lt;\u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e\u0026gt; fetchUser(\u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id) \u003cspan class=\"hljs-keyword\"\u003easync\u003c/span\u003e {\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e result = \u003cspan class=\"hljs-keyword\"\u003eawait\u003c/span\u003e _userRepository.getUserById(id);\n    user.value = result;\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-screens\"\u003eScreens\u003c/h3\u003e\n\u003cp\u003eScreens are where the magic happens. Screens correspond to the different views or pages of the app. Each screen is typically represented by a Flutter widget. Screens are responsible for rendering the UI and displaying data to the user.\u003c/p\u003e\n\u003cp\u003eYou can create screen files inside \u003cstrong\u003elib/presentation/screens\u003c/strong\u003e folder. For example, here's \u003ccode\u003euser_details_screen.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserDetailsScreen\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eStatefulWidget\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e UserDetailsScreen({\u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e.key});\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  State\u0026lt;UserDetailsScreen\u0026gt; createState() =\u0026gt; _UserDetailsScreenState();\n}\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003e_UserDetailsScreenState\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eUserDetailsScreen\u003c/span\u003e\u0026gt; \u003c/span\u003e{\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  \u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e initState() {\n    \u003cspan class=\"hljs-comment\"\u003e// Initialize \u0026amp; inject UserController() using Get.put()\u003c/span\u003e\n    Get.put\u0026lt;UserController\u0026gt;(UserController(), permanent: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e);\n    \u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e.initState();\n  }\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  Widget build(BuildContext context) {\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e controller = UserController.call;\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e userIdController = TextEditingController();\n\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Scaffold(\n      appBar: AppBar(\n        title: Text(\u003cspan class=\"hljs-string\"\u003e'User Details'\u003c/span\u003e),\n      ),\n      body: Padding(\n        padding: \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e EdgeInsets.all(\u003cspan class=\"hljs-number\"\u003e16.0\u003c/span\u003e),\n        child: Column(\n          children: [\n            TextField(\n              controller: userIdController,\n              decoration: InputDecoration(labelText: \u003cspan class=\"hljs-string\"\u003e'Enter User ID'\u003c/span\u003e),\n              keyboardType: TextInputType.number,\n            ),\n            SizedBox(height: \u003cspan class=\"hljs-number\"\u003e16.0\u003c/span\u003e),\n            ElevatedButton(\n              onPressed: () {\n                \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e userId = \u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e.tryParse(userIdController.text);\n                \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (userId != \u003cspan class=\"hljs-keyword\"\u003enull\u003c/span\u003e) {\n                  controller.fetchUser(userId);\n                } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e {\n                  \u003cspan class=\"hljs-comment\"\u003e// Handle invalid input\u003c/span\u003e\n                  Get.snackbar(\u003cspan class=\"hljs-string\"\u003e'Invalid Input'\u003c/span\u003e, \u003cspan class=\"hljs-string\"\u003e'Please enter a valid User ID'\u003c/span\u003e,\n                      snackPosition: SnackPosition.BOTTOM);\n                }\n              },\n              child: Text(\u003cs"])</script>
538<script>self.__next_f.push([1,"pan class=\"hljs-string\"\u003e'Fetch User'\u003c/span\u003e),\n            ),\n            SizedBox(height: \u003cspan class=\"hljs-number\"\u003e16.0\u003c/span\u003e),\n            Obx(() {\n              \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (controller.isLoading.value) {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e CircularProgressIndicator();\n              } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (controller.user.value.id == \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e) {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Text(\u003cspan class=\"hljs-string\"\u003e'User details will appear here'\u003c/span\u003e);\n              } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    Text(\u003cspan class=\"hljs-string\"\u003e'User ID: \u003cspan class=\"hljs-subst\"\u003e${controller.user.value.id}\u003c/span\u003e'\u003c/span\u003e),\n                    Text(\u003cspan class=\"hljs-string\"\u003e'Name: \u003cspan class=\"hljs-subst\"\u003e${controller.user.value.name}\u003c/span\u003e'\u003c/span\u003e),\n                    \u003cspan class=\"hljs-comment\"\u003e// Add more user details as needed\u003c/span\u003e\n                  ],\n                );\n              }\n            }),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-run\"\u003eRun\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eInstall Dependencies\u003c/strong\u003e: Ensure that you have all the required packages and dependencies installed. Run the following command:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-bash\"\u003e flutter pub get\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eGenerate Code\u003c/strong\u003e: Since our app uses code generation tools like \u003ccode\u003efreezed\u003c/code\u003e, \u003ccode\u003ejson_serializable\u003c/code\u003e, etc., it's important to generate necessary code files. Run the following command:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-bash\"\u003e flutter pub run build_runner build --delete-conflicting-outputs\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e This command generates code that's needed for serialization, deserialization, and more. The \u003ccode\u003e--delete-conflicting-outputs\u003c/code\u003e flag helps resolve any conflicts that may arise during code generation.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eRun the App\u003c/strong\u003e: Finally, you can launch the app using the following command:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-bash\"\u003e flutter run\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eCongratulations! 🥳 You've just embarked on a thrilling journey of implementing clean architecture in Flutter using GetX. Clean architecture can be a bit challenging at first, but once you get the hang of it, it makes your code more maintainable and scalable.\u003c/p\u003e\n\u003cp\u003eSo, go forth and create amazing Flutter apps with clean architecture. Happy coding! 🚀📱✨\u003c/p\u003e\n19:T5a71,Hello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure, this guide will help you build organized and maintainable Flutter apps. So, grab your coding capes, and let's dive in!\n\n## What is Clean Architecture?\n\nBefore we delve into the nitty-gritty of Flutter clean architecture with GetX, let's start with a quick introduction to clean architecture.\n\n**Pros of Clean Architecture:**\n\n* **Modularity:** Clean architecture promotes a modular approach, making your co
538debase easier to understand and maintain.\n    \n* **Testability:** It encourages writing unit tests and makes your codebase more test-friendly.\n    \n* **Scalability:** Your app can grow and evolve without major architectural changes.\n    \n* **Independence:** The architecture keeps external frameworks and libraries at bay, preventing them from tightly coupling with your code.\n    \n\n**Cons of Clean Architecture:**\n\n* **Initial Complexity:** Setting up a clean architecture project might seem more daunting compared to a simple structure.\n    \n* **Learning Curve:** Developers new to clean architecture may take some time to fully grasp its concepts.\n    \n\nNow that we're clear on the basics, let's roll up our sleev"])</script>
538<script>self.__next_f.push([1,"es and get started!\n\n---\n\n## Project Setup\n\nThis project is written using the current latest stable versions of:\n\n* [**Flutter**](https://docs.flutter.dev/development/tools/sdk/releases): v3.10.6\n    \n* [**Dart**](https://dart.dev/guides/language/evolution): v3.0.6\n    \n\n### Packages\n\nBefore we start coding, let's gather our tools and libraries. Here are the essential packages we'll use:\n\n* [**getX**](https://pub.dev/packages/get)**:** The Swiss Army knife for state management. (You can use any kind of state management you prefer to work with such as [**provider**](https://pub.dev/packages/provider)**or**[**flutter\\_bloc**](https://pub.dev/packages/flutter_bloc))\n    \n* [**freezed**](https://pub.dev/packages/freezed)**:** A code generation package for creating immutable data models.\n    \n* [**retrofit**](https://pub.dev/packages/retrofit) **\u0026** [**dio**](https://pub.dev/packages/dio)**:** For handling HTTP requests.\n    \n* [**get\\_it**](https://pub.dev/packages/get_it) **\u0026** [**injectable**](https://pub.dev/packages/injectable)**:** For dependency injection.\n    \n* [**go\\_router**](https://pub.dev/packages/go_router)**:** A declarative routing package that provides a convenient, URL-based API for navigating between different screens.\n    \n* [**pretty\\_dio\\_logger**](https://pub.dev/packages/pretty_dio_logger)**:** A delightful logger for HTTP requests.\n    \n\nLet's include these packages in our `pubspec.yaml`:\n\n```yaml\ndependencies:\n  get: ^4.6.6\n  retrofit: ^4.0.2\n  dio: ^5.3.3\n  get_it: ^7.6.4\n  injectable: ^2.3.0\n  go_router: ^11.1.2\n  pretty_dio_logger: ^1.3.1\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^2.0.0\n  build_runner: ^2.4.6\n  freezed: ^2.4.3\n  json_serializable: ^6.7.1\n  retrofit_generator: ^7.0.8\n  injectable_generator: ^2.4.0\n```\n\n### Folder Structure\n\nOrganization is key in clean architecture. Here's a recommended folder structure:\n\n```c\nlib/\n  ├── config/\n  │     ├── injection/\n  │     ├── router/\n  │     └── themes/\n  ├── data/\n  │     ├── datasources/\n  │     │    ├── local/\n  │     │    └── remote/\n  │     └── repositories/\n  ├── domain/\n  │     ├── models/\n  │     └── repositories/\n  ├── presentation/\n  │     ├── controllers/\n  │     ├── pages/\n  │     └── widgets/\n  ├── utils/\n  │     ├── constants/\n  │     ├── extensions/\n  │     └── resources/\n  └── main.dart\n```\n\nThe **config** folder includes the configuration of the app (**themes**, **router**, ..etc) and anything else related to the app’s configurations.\n\nThe **utils** folder, on the other hand, includes (**constants**, **extensions**, **resources**, ..etc) and anything related to that.\n\nThe (**data**, **domain** and **presentation**) folders will be explained later in detail.\n\n### Architecture and Dependency Rules\n\nThe clean architecture consists of three primary layers:\n\n1. **Data Layer:** Responsible for data fetching and caching.\n    \n2. **Domain Layer:** Houses the core business logic.\n    \n3. **Presentation Layer:** Manages the user interface and user interactions.\n    \n\nEach layer has its unique responsibilities, and dependencies flow inward. The Data Layer is independent, the Domain Layer depends on Data, and the Presentation Layer relies on both Data and Domain.\n\n![](https://cdn.hashnode.com/res/hashnode/image/upload/v1696499988717/67904f8e-432e-4d19-86d3-491d790d0f1a.png align=\"center\")\n\n---\n\n## App Configuration\n\nBefore diving into the layers, let's configure our app. Here's a sneak peek:\n\n### Routing\n\nRouting is a fundamental aspect of any Flutter app. We'll use the `go_router` package to handle navigation efficiently.\n\n\u003e *You can absolutely choose whatever the routing package you prefer, and that suits your needs.*\n\nNow, after adding the package to your `pubspec.yaml` file as we talked about it at the beginning of the article, create a folder in this path **lib/config** and call it **router**, then inside the folder create a file and name it `app_r"])</script>
538<script>self.__next_f.push([1,"outer.dart` as shown below:\n\n```dart\n@singleton\nclass AppRouter {\n  final GoRouter _router = GoRouter(\n    debugLogDiagnostics: true,\n    initialLocation: '/',\n    routes: [\n      GoRoute(\n        path: '/',\n        builder: (context, state) =\u003e const HomeScreen(),\n      ),\n      GoRoute(\n        path: '/profile',\n        builder: (context, state) =\u003e const UserDetailsScreen(),\n      ),\n      ...\n      // Add more routes as needed\n  );\n\n  GoRouter get router =\u003e _router;\n}\n```\n\n### Theme\n\nDefining a global theme ensures consistency in your app's appearance. Let's create a file `app_theme.dart` inside **lib/config/themes**:\n\n```dart\nimport 'package:dtdcopedia/config/themes/app_colors.dart';\nimport 'package:flutter/material.dart';\n\nabstract class AppTheme {\n  static ThemeData get light {\n    return ThemeData(\n      appBarTheme: const AppBarTheme(\n        elevation: 0,\n        color: AppColors.primary,\n      ),\n      scaffoldBackgroundColor: AppColors.background,\n      primaryColor: AppColors.primary,\n      splashColor: Colors.transparent,\n      fontFamily: 'Hind',\n    );\n  }\n\n  static ThemeData get dark {\n    return ThemeData(\n      appBarTheme: const AppBarTheme(\n        elevation: 0,\n        color: AppColors.primary,\n      ),\n      scaffoldBackgroundColor: AppColors.text,\n      primaryColor: AppColors.primary,\n      splashColor: Colors.transparent,\n      fontFamily: 'Hind',\n    );\n  }\n}\n```\n\nThis sets up your app's global theme with the specified colors, fonts, and more. You can customize it the way you like.\n\n\u003cdiv data-node-type=\"callout\"\u003e\n\u003cdiv data-node-type=\"callout-emoji\"\u003e💡\u003c/div\u003e\n\u003cdiv data-node-type=\"callout-text\"\u003eUse \u003cstrong\u003eThemeExtension\u003c/strong\u003e for creating custom themes: \u003ca target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa\" style=\"pointer-events: none\"\u003ehttps://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa\u003c/a\u003e\u003c/div\u003e\n\u003c/div\u003e\n\n### **Utils**\n\nIn the `utils` folder, you can organize your utility functions, constants, extensions, and resources neatly. Here's an example structure:\n\n```dart\nlib/\n  ├── utils/\n  │     ├── constants/\n  │     │     └── app_constants.dart\n  │     ├── extensions/\n  │     │     ├── string_extensions.dart\n  │     │     └── date_extensions.dart\n  │     ├── resources/\n  │     │     └── data_state.dart\n  │     └── ...\n  └── ...\n```\n\nFor example, in `app_constants.dart`:\n\n```dart\nclass AppConstants {\n  static const String appTitle = 'Flutter Clean Architecture';\n\n // Networking and APIs\n  static const String baseUrl = 'https://appi.example.com/'; \n  static const String defaultApiKey = 'YOUR_API_KEY';\n  \n // Storage and Databases\n static const String appTableName = 'app_table';\n static const String databaseName = 'app_database.db';\n}\n```\n\nAnd in `string_extensions.dart`:\n\n```dart\nextension StringExtensions on String {\n  String capitalize() {\n    return \"${this[0].toUpperCase()}${this.substring(1)}\";\n  }\n}\n```\n\nIn the **lib/utils/resources** create a file and name it `data_state.dart` which contains the following code:\n\n```dart\nimport 'package:dio/dio.dart';\n\nabstract class DataState\u003cT\u003e {\n    final T data;\n    final DioError error;\n    const DataState({this.data, this.error});\n}\n\nclass DataSuccess\u003cT\u003e extends DataState\u003cT\u003e { \n    const DataSuccess(T data): super(data: data);\n}\n\nclass DataFailed\u003cT\u003e extends DataState\u003cT\u003e { \n    const DataFailed (DioError error): super(error: error);\n}\n```\n\nThis wrapper class `DataState` can be used to wrap our entire network call in order to determine the state of the request being sent to the server and its response.\n\nAs you can see in the code, we’ve two different states, one when we get a successful response 
538(**DataSuccess**), and the other when an error occurs while sending the request or receiving the response (**DataFailed**).\n\nThis is very important as later on when we will have too many requests and logic, you will see how minimized the code will become.\n\n### Dependency In"])</script>
538<script>self.__next_f.push([1,"jection\n\nDependency injection is a crucial part of clean architecture. We use `get_it` and `injectable` to manage our dependencies efficiently. Here's a glimpse of how it works:\n\nFirst, inside **lib/config/injection,** create a file named `injection.dart`:\n\n```dart\nimport 'package:get_it/get_it.dart';\nimport 'package:injectable/injectable.dart';\nimport 'injection.config.dart';\n\nfinal locator = GetIt.instance;\n\n@InjectableInit()\nFuture\u003cvoid\u003e configureDependencies() {\n  return locator.init();\n}\n```\n\nNow, create a file named `register_module.dart` inside the same folder. Here we can inject third party packages such as **SharedPreferences** \u0026 **Dio**:\n\n```dart\nimport 'dart:io';\n\nimport 'package:dio/dio.dart';\nimport 'package:injectable/injectable.dart';\nimport 'package:pretty_dio_logger/pretty_dio_logger.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\n@module\nabstract class RegisterModule {\n  // Inject SharedPreferences\n  @preResolve\n  Future\u003cSharedPreferences\u003e get prefs =\u003e SharedPreferences.getInstance();\n\n  // Inject Dio\n  @lazySingleton\n  Dio dio(SharedPreferences sharedPreferences) {\n    final token = sharedPreferences.getString('token') ?? '';\n\n    return Dio(\n      BaseOptions(\n        baseUrl: AppConstants.baseUrl,\n        headers: {\n          if (token.isNotEmpty) HttpHeaders.authorizationHeader: \"Bearer $token\",\n        },\n      ),\n    )..interceptors.add(\n        PrettyDioLogger(\n          requestHeader: true,\n          requestBody: true,\n          responseHeader: true,\n          maxWidth: 120,\n        ),\n      );\n  }\n}\n```\n\nNow you can import and use these utility functions, constants \u0026 injected modules throughout your project for a clean and organized codebase. Finally, the `main.dart` file will look like this:\n\n```dart\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n  await configureDependencies();\n  runApp(const MyApp());\n}\n\nfinal _router = locator\u003cAppRouter\u003e().router;\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp.router(\n      debugShowCheckedModeBanner: false,\n      title: AppConstants.appTitle,\n      theme: AppTheme.light,\n      darkTheme: AppTheme.dark,\n      routerConfig: _router,\n    );\n  }\n}\n```\n\n---\n\n## **Domain Layer**\n\nNow, let's dive into the Domain Layer. The Domain Layer is the core of the application's business logic. It encapsulates the rules, behaviours, and core functionality that define how the application operates. This layer is entirely independent of external factors such as databases, network calls, or the user interface.\n\nIn **Clean Architecture**, the **Domain Layer** should be independent of any specific implementation or framework. This means that the code in the Domain Layer should be written in a **technology-agnostic** way, with no dependencies on external systems or libraries.\n\n### **Entities (Models)**\n\nEntities/Models are plain Dart classes that represent the core business objects of the application. They model the data that the app operates on and typically contain minimal logic. Entities are defined solely based on the business requirements, and they do not contain any database-specific or UI-related code.\n\n**Freezed** helps us create immutable data models effortlessly. Here's a sample domain model `user.dart` inside **lib/domain/models**:\n\n```dart\npart 'user.freezed.dart';\n\n@freezed\nclass User with _$User {\n  const factory User({\n    required int id,\n    required String name,\n    // Add more fields as 
538needed\n  }) = _User;\n}\n```\n\n### **Repositories (Abstract Definitions)**\n\nIn the Domain Layer, repositories are defined as abstract classes or interfaces. These abstract definitions declare the methods for interacting with data without specifying how data is fetched or stored. The concrete implementations of these repositories are located in the Data Layer.\n\nRepositories are interfaces that define data sources for our domain layer. For example, `user_repository.dart` inside **lib/domain/repositories**:\n\n```dart\nabstract class UserRepository {\n  Futu"])</script>
538<script>self.__next_f.push([1,"re\u003cUserEntity\u003e getUserById(int id);\n}\n```\n\n## **Data Layer**\n\nThe Data Layer is the foundational layer responsible for handling data operations in a Flutter app. Its primary purpose is to abstract and encapsulate data sources, ensuring that the rest of the application doesn't need to know the specifics of data retrieval and storage.\n\n### **Data Sources**\n\nData Sources are responsible for interacting with external data providers, such as APIs, databases, or local storage. They define the interfaces for fetching and storing data. This layer is typically implemented using packages like `Retrofit`, `Dio`, or other HTTP client libraries for API interactions, as well as database libraries like `SQFLite` for local storage.\n\nHere's a sample data source service `user_data_source.dart` inside **lib/data/datasources/remote**:\n\n```dart\n@RestApi(baseUrl: 'https://api.example.com/')\nabstract class UserDataSource {\n  factory UserDataSource(Dio dio) = _UserDataSource;\n\n  @GET('/users/{id}')\n  Future\u003cUserEntity\u003e getUserById(@Path() int id);\n}\n```\n\nNow, create `data_source_injection.dart` file inside **lib/config/injection** and inject the **UserDataSource** class as a module:\n\n```dart\nimport 'package:dio/dio.dart';\nimport 'package:your_package_name/data/datasources/remote/user_data_source.dart';\nimport 'package:injectable/injectable.dart';\n\n@module\nabstract class DataSourceInjectableModule {\n  UserDataSource getUserDataSource(Dio dio) =\u003e UserDataSource(dio);\n  // Add more data dources as needed\n}\n```\n\n### **Repositories (Implementation)**\n\nRepositories act as intermediaries between Data Sources and the Domain Layer. They provide a clean and consistent API for accessing data. Repositories are defined as abstract classes in the Domain Layer, listing methods like `getUserById()` or `fetchPosts()`. The concrete implementations of these repositories, found in the Data Layer, interact with Data Sources to fetch and manipulate data. Repositories also manage data caching and storage.\n\nBefore we implement the **UserRepository** class, we need to create a base class called **BaseApiRepository**, create a folder inside the **data** folder and name it **base**, then create a file inside it and name it `base_api_repository.dart` which contains the following:\n\n```dart\nimport 'dart:io' show HttpStatus;\nimport 'package:retrofit/dio.dart';\n\nimport 'package:dio/dio.dart';\nimport 'package:your_project_name/utils/resources/data_state.dart';\nimport 'package:flutter/material.dart';\n\nabstract class BaseApiRepository {\n  @protected\n  Future\u003cDataState\u003cT?\u003e\u003e getStateOf\u003cT\u003e({\n    required Future\u003cHttpResponse\u003cT\u003e\u003e Function() request,\n  }) async {\n    try {\n      final httpResponse = await request();\n      if (httpResponse.response.statusCode == HttpStatus.created || httpResponse.response.statusCode == HttpStatus.ok) {\n        return DataSuccess(httpResponse.data);\n      } else if (httpResponse.response.statusCode == HttpStatus.unauthorized) {\n        throw DataFailed(DioException(\n          response: httpResponse.response,\n          requestOptions: httpResponse.response.requestOptions,\n        ));\n      } else {\n        throw DioException(\n          response: httpResponse.response,\n          requestOptions: httpResponse.response.requestOptions,\n        );\n      }\n    } on DioException catch (error) {\n      return DataFailed(error);\n    }\n  }\n}\n```\n\nThis base class plays an important role here, which wraps our API call that comes from the data source `user_data_source.dart` and return a **DataState** object instead of **HttpResponse**.\n\nBy doing so, we’re reducing the boilerplate code in each method without the need to write “**try-catch”** or “**if”** statements everywhere because you only need to pass the request in the “**getStateOf**” method and this should do the rest and return the result based on the generic “**T**” type.\n\nNow create `user_repository_impl.dart` inside **lib/data/repositories** \u0026 implement the previously defined **UserRepository** class as below:\n\n```dart\n@Injectable(as: UserRepository)\nclass UserRepositoryImpl extends BaseApiRe"])</script>
538<script>self.__next_f.push([1,"pository implements UserRepository {\n  final UserDataSource _userDataSource;\n  UserRepositoryImpl(this._userDataSource);\n\n  @override\n  Future\u003cDataState\u003cUser?\u003e\u003e getUserById(int id) {\n    return getStateOf\u003cUser\u003e(\n      request: () =\u003e _userDataSource.getUserById(id),\n    );\n  }\n}\n```\n\n## Presentation Layer\n\nLast but not least, we arrive at the Presentation Layer, where we build the user interface, controllers, screens, and common widgets.\n\nThe benefit of using **Presentation Layer** is that it makes the codebase more modular and maintainable. By separating the user interface from the business logic and infrastructure, it becomes easier to modify and extend the application over time. Also, it allows for different user interfaces to be developed and used with the same underlying business logic. For example, a **web application**, a **mobile application**, and a **desktop application** could all use the same Domain Layer but have different Presentation Layers that are optimized for their respective platforms.\n\n### Controllers\n\nControllers manage the state and business logic of our app. Here's a snippet of a controller `user_controller.dart` inside **lib/presentation/controllers** using GetX:\n\n```dart\nclass UserController extends GetxController {\n  static UserController get call =\u003e Get.find();\n\n  final UserRepository _userRepository = locator\u003cUserRepository\u003e();\n  final Rx\u003cUser\u003e user = Rx\u003cUser\u003e(User(id: 0, name: ''));\n\n  Future\u003cvoid\u003e fetchUser(int id) async {\n    final result = await _userRepository.getUserById(id);\n    user.value = result;\n  }\n}\n```\n\n### Screens\n\nScreens are where the magic happens. Screens correspond to the different views or pages of the app. Each screen is typically represented by a Flutter widget. Screens are responsible for rendering the UI and displaying data to the user.\n\nYou can create screen files inside **lib/presentation/screens** folder. For example, here's `user_details_screen.dart`:\n\n```dart\nclass UserDetailsScreen extends StatefulWidget {\n  const UserDetailsScreen({super.key});\n\n  @override\n  State\u003cUserDetailsScreen\u003e createState() =\u003e _UserDetailsScreenState();\n}\n\nclass _UserDetailsScreenState extends State\u003cUserDetailsScreen\u003e {\n  @override\n  void initState() {\n    // Initialize \u0026 inject UserController() using Get.put()\n    Get.put\u003cUserController\u003e(UserController(), permanent: true);\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final controller = UserController.call;\n    final userIdController = TextEditingController();\n\n    return Scaffold(\n      appBar: AppBar(\n        title: Text('User Details'),\n      ),\n      body: Padding(\n        padding: const EdgeInsets.all(16.0),\n        child: Column(\n          children: [\n            TextField(\n              controller: userIdController,\n              decoration: InputDecoration(labelText: 'Enter User ID'),\n              keyboardType: TextInputType.number,\n            ),\n            SizedBox(height: 16.0),\n            ElevatedButton(\n              onPressed: () {\n                final userId = int.tryParse(userIdController.text);\n                if (userId != null) {\n                  controller.fetchUser(userId);\n                } else {\n                  // Handle invalid input\n                  Get.snackbar('Invalid Input', 'Please enter a valid User ID',\n                      snackPosition: SnackPosition.BOTTOM);\n                }\n              },\n              child: Text('Fetch User'),\n            ),\n            SizedBox(height: 16.0),\n            Obx(() {\n              if (controller.isLoading.value) {\n                return CircularProgressIndicator();\n              } else if (controller.user.value.id == 0) {\n                return Text('User details will appear here');\n              } else {\n                return Column(\n                  crossAxisAlignment: CrossAxisAlignment.
538start,\n                  children: [\n                    Text('User ID: ${controller.user.value.id}'),\n                    Text('Name: ${controller.user.value.name}'),\n                    // Add more user details as needed\n          "])</script>
538<script>self.__next_f.push([1,"        ],\n                );\n              }\n            }),\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\n---\n\n## Run\n\n1. **Install Dependencies**: Ensure that you have all the required packages and dependencies installed. Run the following command:\n    \n    ```bash\n    flutter pub get\n    ```\n    \n2. **Generate Code**: Since our app uses code generation tools like `freezed`, `json_serializable`, etc., it's important to generate necessary code files. Run the following command:\n    \n    ```bash\n    flutter pub run build_runner build --delete-conflicting-outputs\n    ```\n    \n    This command generates code that's needed for serialization, deserialization, and more. The `--delete-conflicting-outputs` flag helps resolve any conflicts that may arise during code generation.\n    \n3. **Run the App**: Finally, you can launch the app using the following command:\n    \n    ```bash\n    flutter run\n    ```\n    \n\n---\n\n## Conclusion\n\nCongratulations! 🥳 You've just embarked on a thrilling journey of implementing clean architecture in Flutter using GetX. Clean architecture can be a bit challenging at first, but once you get the hang of it, it makes your code more maintainable and scalable.\n\nSo, go forth and create amazing Flutter apps with clean architecture. Happy coding! 🚀📱✨b:[\"$\",\"main\",null,{\"id\":\"main-content\",\"className\":\"min-h-screen bg-transparent\",\"children\":[[\"$\",\"$L17\",null,{\"publication\":{\"__typename\":\"Publication\",\"id\":\"5f7ff042addab004867dfb38\",\"url\":\"https://blog.adityasharma.co\",\"canonicalURL\":\"https://blog.adityasharma.co\",\"urlPattern\":\"simple\",\"title\":\"Aditya's Blog\",\"displayTitle\":\"Aditya's Blog\",\"hasBadges\":true,\"descriptionSEO\":\"I convert scrap to sculptures!\",\"seo\":{\"title\":null,\"description\":null,\"__typename\":\"SEO\"},\"publicMembers\":{\"totalDocuments\":1,\"__typename\":\"PublicMembers\"},\"about\":{\"html\":\"\u003cp\u003eI convert scrap to sculptures!\u003c/p\u003e\\n\",\"text\":\"I convert scrap to sculptures!\\n\",\"__typename\":\"Content\"},\"features\":{\"proTeam\":{\"isEnabled\":false,\"__typename\":\"ProTeamFeature\"},\"newsletter\":{\"isEnabled\":true,\"__typename\":\"NewsletterFeature\"},\"viewCount\":{\"isEnabled\":true,\"__typename\":\"ViewCountFeature\"},\"readTime\":{\"isEnabled\":true,\"__typename\":\"ReadTimeFeature\"},\"textSelectionSharer\":{\"isEnabled\":true,\"__typename\":\"TextSelectionSharerFeature\"},\"gptBotCrawling\":{\"__typename\":\"GPTBotCrawlingFeature\",\"isEnabled\":true},\"headlessCMS\":{\"isEnabled\":false,\"__typename\":\"HeadlessCMSFeature\"},\"__typename\":\"PublicationFeatures\"},\"aeoSettings\":{\"isEnabled\":false,\"llmsTxtEnabled\":true,\"crawlers\":{\"gptBot\":true,\"claudeBot\":true,\"perplexityBot\":true,\"googleExtended\":true,\"ccBot\":true,\"__typename\":\"AeoCrawlerSettings\"},\"__typename\":\"PublicationAeoSettings\"},\"metaTags\":\"\",\"ogMetaData\":{\"image\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602221771564/ozrDuqA5z.jpeg\",\"__typename\":\"OpenGraphMetaData\"},\"author\":{\"__typename\":\"User\",\"id\":\"5f7fed13addab004867dfb1c\",\"name\":\"Aditya Sharma\",\"username\":\"BetaPundit\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\"},\"preferences\":{\"__typename\":\"PublicationPreferences\",\"logo\":null,\"dar
538kMode\":{\"__typename\":\"DarkModePreferences\",\"logo\":null,\"enabled\":false},\"navbarItems\":[{\"__typename\":\"PublicationNavbarItem\",\"id\":\"604632d4d0457f4d5c58364e\",\"label\":\"Portfolio\",\"url\":null,\"type\":\"page\",\"series\":null,\"page\":{\"__typename\":\"StaticPage\",\"id\":\"5f800864addab004867dfcc6\",\"slug\":\"portfolio\"}}],\"enabledPages\":{\"__typename\":\"EnabledPages\",\"badges\":true,\"newsletter\":false,\"members\":false},\"layout\":\"magazine\",\"disableFooterBranding\":false,\"isSubscriptionModalDisabled\":false},\"favicon\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602225116875/Bf-bb_MDw.jpeg\",\"headerColor\":\"#42BBAA\",\"integrations\":{\"fbPixelID\":\"\",\"fathomSiteID\":\"\",\"fathomCustomDomainEnabled\":false,\"fathomCustomDomain\":\"\",\"hotjarSiteID\":\"\",\"matomoSiteID\":null,\"matomoURL\":\"\",\"gaTrackingID\":\"\",\"gTagManagerID\":null,\"plausibleAnalyticsEnabled\":true,\"wmPaymentPointer\":\"\",\"koalaPublicKey\":null,\"msClarityID\":null,\"openPanelClientId\":null,\"consentManagement\":{\"enabled\":fal"])</script>
538<script>self.__next_f.push([1,"se,\"version\":1,\"privacyPolicyURL\":null,\"__typename\":\"ConsentManagement\"},\"__typename\":\"PublicationIntegrations\"},\"imprintV2\":null,\"postsCount\":{\"totalDocuments\":10,\"__typename\":\"PostConnection\"},\"isTeam\":true,\"links\":{\"twitter\":\"https://twitter.com/BetaPundit\",\"instagram\":null,\"github\":\"https://github.com/BetaPundit\",\"website\":\"https://adityasharma.co\",\"hashnode\":\"https://hashnode.com/@BetaPundit\",\"youtube\":\"\",\"dailydev\":\"\",\"linkedin\":\"https://linkedin.com/in/aditya-sharma26\",\"mastodon\":null,\"facebook\":null,\"bluesky\":null,\"__typename\":\"PublicationLinks\"},\"domainInfo\":{\"__typename\":\"DomainInfo\",\"hashnodeSubdomain\":\"betapundit\",\"domain\":{\"__typename\":\"Domain\",\"host\":\"blog.adityasharma.co\",\"ready\":true},\"wwwPrefixedDomain\":null},\"redirectionRules\":[],\"customRules\":[{\"type\":301,\"path\":\"/portfolio\",\"destination\":\"https://adityasharma.co\",\"__typename\":\"CustomRule\"}],\"totalRecommendedPublications\":0,\"allowContributorEdits\":false,\"widgets\":[],\"post\":{\"__typename\":\"Post\",\"id\":\"651e8bf5f457ef18124ee464\",\"cuid\":\"clnd0q99w00000aif536jalop\",\"title\":\"Building Flutter Apps with Clean Architecture Using GetX\",\"subtitle\":\"Mastering Clean Architecture and GetX for High-Performing Flutter Apps\",\"slug\":\"building-flutter-apps-with-clean-architecture-using-getx\",\"brief\":\"Hello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure...\",\"featured\":false,\"publishedAt\":\"2023-10-05T10:12:05.108Z\",\"updatedAt\":\"2024-05-24T17:35:34.293Z\",\"url\":\"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx\",\"readTimeInMinutes\":12,\"views\":7341,\"reactionCount\":0,\"responseCount\":0,\"replyCount\":0,\"author\":{\"__typename\":\"User\",\"id\":\"5f7fed13addab004867dfb1c\",\"name\":\"Aditya Sharma\",\"username\":\"BetaPundit\",\"deactivated\":false,\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\",\"bio\":{\"html\":\"\u003cp\u003eI am a full stack developer, a Machine Learning enthusiast \u0026amp; a Flutter Developer. Also, I am a budding Entrepreneur! \u003c/p\u003e\\n\u003cp\u003eI am a student of Life and believe in building tech for people.\u003c/p\u003e\\n\u003cp\u003eI love contributing to open-source projects and use my knowledge to develop a software solution for general problems arising in the community. \u003c/p\u003e\\n\",\"__typename\":\"Content\"},\"socialMediaLinks\":{\"website\":\"https://adityasharma.co\",\"github\":\"https://github.com/BetaPundit\",\"twitter\":\"https://twitter.com/BetaPundit\",\"facebook\":\"\",\"stackoverflow\":\"https://stackoverflow.com/users/9568463/aditya\",\"linkedin\":\"https://linkedin.com/in/aditya-sharma26\",\"__typename\":\"SocialMediaLinks\"}},\"coAuthors\":[],\"seo\":{\"title\":\"Flutter App Development: Clean Architecture with GetX\",\"description\":\"Elevate your Flutter app development with Clean Architecture and GetX. Learn to build efficient, organized, and high-performance apps. Dive in now!\",\"__typename\":\"SEO\"},\"coverImage\":{\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1696485327830/8b86432c-2243-4136-8241-d2224d5cee4f.png\",\"isPortrait\":false,\"attribution\":\"\",\"isAttributionHidden\":false,\"photographer\":\"\",\"__typename\":\"CoverImage\"},\"content\":{\"html\":\"$18\",\"markdown\":\"$19\",\"__typename\":\"Content\"},\"preferences\":{\"pinnedToBlog\":false,\"disableComments\":false,\"stickCoverToBottom\":false,\"isDelisted\":false,\"__typename\":\"PostPreferences\"},\"series\":{\"id\":\"6124bfce663d427e19ff9ca4\",\"name\":\"What the Flutter\",\"slug\":\"what-the-flutter\",\"__typename\":\"Series\"},\"tags\":[{\"id\":\"56744722958ef13879b9507c\",\"slug\":\"flutter\",\"name\":\"Flutter\",\"__typename\":\"Tag\"},{\"id\":\"56744720958ef13879b947c4\",\"slug\":\"app-development\",\"name\":\"app development\",\"__typename\":\"Tag\"},{\"id\":\"568a9b8ce4c4e23aef243c1f\",\"slug\":\"mobile-development\",\"name\":\"Mobile Development\",\"__typename\":\"Tag\"},{\"id\":\"573504d39835efadc8742016\",\"slug\":\"clean-code\",\"name\":\"clean code\",\"__typename\":\"Tag\"},{\"id\":\"56744723958ef13879b952d7\",\"slug\":\"developer\",\"name\":\"Developer\",\"__typename\":\"Tag\"}],\"ogMetaData\":{\"image\":\"https://cdn.hashno"])</script>
538<script>self.__next_f.push([1,"de.com/res/hashnode/image/upload/v1696500681728/c3e1000f-3ed5-48d7-a5f8-08fc8242a340.png\",\"__typename\":\"OpenGraphMetaData\"},\"canonicalUrl\":null,\"hasLatexInPost\":false,\"faq\":[],\"features\":{\"tableOfContents\":{\"isEnabled\":true,\"items\":[{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-0\",\"level\":2,\"slug\":\"what-is-clean-architecture\",\"title\":\"What is Clean Architecture?\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-1\",\"level\":2,\"slug\":\"project-setup\",\"title\":\"Project Setup\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-2\",\"level\":3,\"slug\":\"packages\",\"title\":\"Packages\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-3\",\"level\":3,\"slug\":\"folder-structure\",\"title\":\"Folder Structure\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-4\",\"level\":3,\"slug\":\"architecture-and-dependency-rules\",\"title\":\"Architecture and Dependency Rules\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-5\",\"level\":2,\"slug\":\"app-configuration\",\"title\":\"App Configuration\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-6\",\"level\":3,\"slug\":\"routing\",\"title\":\"Routing\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-7\",\"level\":3,\"slug\":\"theme\",\"title\":\"Theme\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-8\",\"level\":3,\"slug\":\"utils\",\"title\":\"Utils\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-9\",\"level\":3,\"slug\":\"dependency-injection\",\"title\":\"Dependency Injection\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-10\",\"level\":2,\"slug\":\"domain-layer\",\"title\":\"Domain Layer\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-11\",\"level\":3,\"slug\":\"entities-models\",\"title\":\"Entities (Models)\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-12\",\"level\":3,\"slug\":\"repositories-abstract-definitions\",\"title\":\"Repositories (Abstract Definitions)\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-13\",\"level\":2,\"slug\":\"data-layer\",\"title\":\"Data Layer\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-14\",\"level\":3,\"slug\":\"data-sources\",\"title\":\"Data Sources\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-15\",\"level\":3,\"slug\":\"repositories-implementation\",\"title\":\"Repositories (Implementation)\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-16\",\"level\":2,\"slug\":\"presentation-layer\",\"title\":\"Presentation Layer\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-17\",\"level\":3,\"slug\":\"controllers\",\"title\":\"Controllers\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-18\",\"level\":3,\"slug\":\"screens\",\"title\":\"Screens\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-19\",\"level\":2,\"slug\":\"run\",\"title\":\"Run\",\"parentId\":null},{\"__typename\":\"TableOfContentsItem\",\"id\":\"toc-20\",\"level\":2,\"slug\":\"conclusion\",\"title\":\"Conclusion\",\"parentId\":null}],\"__typename\":\"TableOfContents\"},\"__typename\":\"PostFeatures\"}},\"posts\":{\"edges\":[{\"node\":{\"__typename\":\"Post\",\"id\":\"691d6e27e99bd74af5fcbb3a\",\"title\":\"Scalable Microfrontends: Architecting a Decoupled UI with Module Federation\",\"slug\":\"scalable-microfrontends-architecting-a-decoupled-ui-with-module-federation\",\"publishedAt\":\"2025-11-19T07:13:43.400Z\",\"cuid\":\"cmi5o1x48000202l784vobvv2\",\"url\":\"https://blog.adityasharma.co/scalable-microfrontends-architecting-a-decoupled-ui-with-module-federation\",\"subtitle\":null,\"brief\":\"Have you ever worked on a frontend codebase so large that you feared deploying a CSS change to the Settings page because it might somehow break the Checkout flow? Or perhaps y
538ou've struggled to align three different teams working on the same React re...\",\"readTimeInMinutes\":7,\"views\":242,\"tags\":[{\"id\":\"56744723958ef13879b95434\",\"slug\":\"reactjs\",\"name\":\"React\",\"__typename\":\"Tag\"},{\"id\":\"5d19108fbcd7782076b8a4ba\",\"slug\":\"microfrontend\",\"name\":\"Microfrontend\",\"__typename\":\"Tag\"},{\"id\":\"56744722958ef13879b94f1b\",\"slug\":\"web-development\",\"name\":\"Web Development\",\"__typename\":\"Tag\"},{\"id\":\"56744723958ef13879b952af\",\"slug\":\"webdev\",\"name\":\"webdev\",\"__typename\":\"Tag\""])</script>
538<script>self.__next_f.push([1,"}],\"author\":{\"__typename\":\"User\",\"id\":\"5f7fed13addab004867dfb1c\",\"username\":\"BetaPundit\",\"name\":\"Aditya Sharma\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\",\"followersCount\":12},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1763536062182/2941dcbc-787b-4892-9dad-d0671898a96b.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"},{\"node\":{\"__typename\":\"Post\",\"id\":\"66d6d787b993bebc69066b29\",\"title\":\"Mastering WebSockets in Flutter: A Comprehensive Guide\",\"slug\":\"mastering-websockets-in-flutter-a-comprehensive-guide\",\"publishedAt\":\"2024-09-03T09:31:51.647Z\",\"cuid\":\"cm0m8d1pb000709mffq4bhgs9\",\"url\":\"https://blog.adityasharma.co/mastering-websockets-in-flutter-a-comprehensive-guide\",\"subtitle\":\"Real-Time Communication in Flutter: A Comprehensive Guide to Implementing WebSockets for Seamless Connectivity\",\"brief\":\"WebSockets provide a powerful way to establish a full-duplex communication channel between the client and server, allowing real-time data exchange. This is particularly useful in applications that require live updates, such as chat apps, online gamin...\",\"readTimeInMinutes\":5,\"views\":778,\"tags\":[{\"id\":\"56744722958ef13879b9507c\",\"slug\":\"flutter\",\"name\":\"Flutter\",\"__typename\":\"Tag\"},{\"id\":\"568a9b8ce4c4e23aef243c1f\",\"slug\":\"mobile-development\",\"name\":\"Mobile Development\",\"__typename\":\"Tag\"},{\"id\":\"56744721958ef13879b94a0f\",\"slug\":\"websockets\",\"name\":\"websockets\",\"__typename\":\"Tag\"},{\"id\":\"56744720958ef13879b947c4\",\"slug\":\"app-development\",\"name\":\"app development\",\"__typename\":\"Tag\"},{\"id\":\"5f08f6a1b0bf5b3c273ea78b\",\"slug\":\"flutter-examples\",\"name\":\"Flutter Examples\",\"__typename\":\"Tag\"},{\"id\":\"56744723958ef13879b95245\",\"slug\":\"apis\",\"name\":\"APIs\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"5f7fed13addab004867dfb1c\",\"username\":\"BetaPundit\",\"name\":\"Aditya Sharma\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\",\"followersCount\":12},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1725355834204/675ae9ac-bcfd-49cf-b9e5-923718f17489.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"},{\"node\":{\"__typename\":\"Post\",\"id\":\"65cec8b51d663ec31691ba69\",\"title\":\"Mastering FCM in Flutter: Your Ultimate Guide to Engaging Notifications\",\"slug\":\"mastering-fcm-in-flutter-your-ultimate-guide-to-engaging-notifications\",\"publishedAt\":\"2024-02-16T02:30:13.274Z\",\"cuid\":\"clso18g0p000008l0hvn7b25z\",\"url\":\"https://blog.adityasharma.co/mastering-fcm-in-flutter-your-ultimate-guide-to-engaging-notifications\",\"subtitle\":\"Elevate Your Flutter App's Notification Game: Master Firebase Cloud Messaging Like a Pro!\",\"brief\":\"Introduction\\nHey there, fellow Flutter enthusiast! Are you ready to level up your app's notification game? Buckle up because we're about to dive into the world of Firebase Cloud Messaging (FCM) and Flutter. In this guide, we'll explore how to set up ...\",\"readTimeInMinutes\":8,\"views\":966,\"tags\":[{\"id\":\"56744722958ef13879b9507c\",\"slug\":\"flutter\",\"name\":\"Flutter\",\"__typename\":\"Tag\"},{\"id\":\"56744721958ef13879b94df0\",\"slug\":\"dart\",\"name\":\"Dart\",\"__typename\":\"Tag\"},{\"id\":\"5f08f6a1b0bf5b3c273ea78b\",\"slug\":\"flutter-examples\",\"name\":\"Flutter Examples\",\"__typename\":\"Tag\"},{\"id\":\"56744722958ef13879b94e99\",\"slug\":\"firebase\",\"name\":\"Firebase\",\"__typename\":\"Tag\"},{\"id\":\"56a399f292921b8f79d3633c\",\"slug\":\"frontend-development\",\"name\":\"Frontend Development\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"5f7fed13addab004867dfb1c\",\"username\":\"BetaPundit\",\"name\":\"Aditya Sharma\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\",\"followersCount\":12},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1708024953706/32db23ef-4729-49ff-a9fd-ccb8a47f56ce.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"}
538,{\"node\":{\"__typename\":\"Post\",\"id\":\"651e8bf5f457ef18124ee46"])</script>
538<script>self.__next_f.push([1,"4\",\"title\":\"Building Flutter Apps with Clean Architecture Using GetX\",\"slug\":\"building-flutter-apps-with-clean-architecture-using-getx\",\"publishedAt\":\"2023-10-05T10:12:05.108Z\",\"cuid\":\"clnd0q99w00000aif536jalop\",\"url\":\"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx\",\"subtitle\":\"Mastering Clean Architecture and GetX for High-Performing Flutter Apps\",\"brief\":\"Hello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure...\",\"readTimeInMinutes\":12,\"views\":7341,\"tags\":[{\"id\":\"56744722958ef13879b9507c\",\"slug\":\"flutter\",\"name\":\"Flutter\",\"__typename\":\"Tag\"},{\"id\":\"56744720958ef13879b947c4\",\"slug\":\"app-development\",\"name\":\"app development\",\"__typename\":\"Tag\"},{\"id\":\"568a9b8ce4c4e23aef243c1f\",\"slug\":\"mobile-development\",\"name\":\"Mobile Development\",\"__typename\":\"Tag\"},{\"id\":\"573504d39835efadc8742016\",\"slug\":\"clean-code\",\"name\":\"clean code\",\"__typename\":\"Tag\"},{\"id\":\"56744723958ef13879b952d7\",\"slug\":\"developer\",\"name\":\"Developer\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"5f7fed13addab004867dfb1c\",\"username\":\"BetaPundit\",\"name\":\"Aditya Sharma\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\",\"followersCount\":12},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1696485327830/8b86432c-2243-4136-8241-d2224d5cee4f.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"},{\"node\":{\"__typename\":\"Post\",\"id\":\"62aedf620924134a8048919b\",\"title\":\"Dart's Extension Methods: Demystified\",\"slug\":\"darts-extension-methods-demystified\",\"publishedAt\":\"2022-06-19T08:33:38.684Z\",\"cuid\":\"cl4l1xqra03jodynvcuvh8lay\",\"url\":\"https://blog.adityasharma.co/darts-extension-methods-demystified\",\"subtitle\":\"Unleash the power of Dart's Extension Methods with this simple guide\",\"brief\":\"Introduction\\nAs a Flutter Developer, you must have used packages/libraries and wanted to add some additional functionality to them based on your use cases. But you cannot just add methods to that class as the original class is in someone else's libra...\",\"readTimeInMinutes\":6,\"views\":482,\"tags\":[{\"id\":\"56744722958ef13879b9507c\",\"slug\":\"flutter\",\"name\":\"Flutter\",\"__typename\":\"Tag\"},{\"id\":\"56744721958ef13879b94df0\",\"slug\":\"dart\",\"name\":\"Dart\",\"__typename\":\"Tag\"},{\"id\":\"568a9b8ce4c4e23aef243c1f\",\"slug\":\"mobile-development\",\"name\":\"Mobile Development\",\"__typename\":\"Tag\"},{\"id\":\"56744723958ef13879b952d7\",\"slug\":\"developer\",\"name\":\"Developer\",\"__typename\":\"Tag\"}],\"author\":{\"__typename\":\"User\",\"id\":\"5f7fed13addab004867dfb1c\",\"username\":\"BetaPundit\",\"name\":\"Aditya Sharma\",\"profilePicture\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\",\"followersCount\":12},\"coverImage\":{\"__typename\":\"CoverImage\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1655627438428/aSqa2GT_L.png\",\"isPortrait\":false,\"isAttributionHidden\":false}},\"__typename\":\"PostEdge\"}],\"__typename\":\"PostConnection\"},\"redirectedPost\":null,\"staticPage\":null},\"posts\":[\"$b:props:children:0:props:publication:post\"],\"host\":\"blog.adityasharma.co\"}],\"$L1a\",\"$L1b\",\"$L1c\",\"$L1d\",\"$L1e\",null,\"$L1f\"]}]\n20:I[62031,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"ReadingModeToggle\"]\n21:I[81356,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"stat"])</script>
538<script>self.__next_f.push([1,"ic/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"Image\"]\n22:I[52619,[\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"8974\",\"static/chunks/app/page-cc18ae1a3a7a5ed8.js\"],\"\"]\n23:I[33884,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"Avatar\"]\n24:I[33884,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"AvatarImage\"]\n25:I[33884,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"AvatarFallback\"]\n29:I[51285,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"CommentsSection\"]\n2a:I[20730,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"default\"]\n1a:[\"$\",\"section\",null,{\"className\":\"py-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"px-4 sm:px-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]\",\"children\":[\"$\",\"div\",null,{\"className\":\"grid gap-5 max-w-full\",\"children\":[\"$\",\"article\",null,{\"className\":\"w-full max-w-full rounded-xl border bg-card text-card-foreground\",\"children"])</script>
538<script>self.__next_f.push([1,"\":[[\"$\",\"div\",null,{\"className\":\"px-5 pt-12 pb-8 sm:px-8\",\"children\":[\"$\",\"header\",null,{\"className\":\"space-y-5\",\"children\":[\"$\",\"div\",null,{\"className\":\"space-y-4 text-center max-w-4xl mx-auto\",\"children\":[[\"$\",\"h1\",null,{\"className\":\"text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold leading-tight tracking-tight text-balance text-foreground\",\"children\":\"Building Flutter Apps with Clean Architecture Using GetX\"}],[\"$\",\"p\",null,{\"className\":\"text-lg sm:text-xl leading-normal font-normal text-muted-foreground text-balance\",\"children\":\"Mastering Clean Architecture and GetX for High-Performing Flutter Apps\"}],[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center justify-center gap-3 text-sm text-muted-foreground\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-2\",\"children\":[[\"$\",\"span\",null,{\"children\":\"Updated\"}],[\"$\",\"time\",null,{\"dateTime\":\"2024-05-24T17:35:34.293Z\",\"children\":\"May 24, 2024\"}]]}],[[\"$\",\"span\",null,{\"className\":\"text-muted-foreground\",\"children\":\"•\"}],[\"$\",\"span\",null,{\"children\":[12,\" min read\"]}]],[\"$\",\"span\",null,{\"className\":\"text-muted-foreground\",\"children\":\"•\"}],[\"$\",\"$L20\",null,{\"slug\":\"building-flutter-apps-with-clean-architecture-using-getx\"}]]}]]}]}]}],[\"$\",\"div\",null,{\"className\":\"w-full\",\"children\":[\"$\",\"$L21\",null,{\"src\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1696485327830/8b86432c-2243-4136-8241-d2224d5cee4f.png\",\"alt\":\"Building Flutter Apps with Clean Architecture Using GetX\",\"width\":1200,\"height\":750,\"className\":\"w-full h-auto\",\"priority\":true}]}],[\"$\",\"div\",null,{\"className\":\"px-5 py-12 sm:px-8 space-y-8\",\"children\":[[\"$\",\"div\",null,{\"className\":\"max-w-2xl mx-auto\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center gap-5\",\"children\":[[\"$\",\"div\",\"5f7fed13addab004867dfb1c\",{\"className\":\"flex items-center gap-5\",\"children\":[false,[\"$\",\"div\",null,{\"className\":\"flex gap-3 items-start\",\"children\":[[\"$\",\"$L22\",null,{\"href\":\"https://hashnode.com/@BetaPundit\",\"target\":\"_blank\",\"rel\":\"noopener\",\"children\":[\"$\",\"$L23\",null,{\"className\":\"h-10 w-10 border border-border transition-opacity hover:opacity-80\",\"children\":[[\"$\",\"$L24\",null,{\"src\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg?auto=compress,format\u0026format=webp\",\"alt\":\"Aditya Sharma\"}],[\"$\",\"$L25\",null,{\"className\":\"bg-muted text-muted-foreground font-semibold text-lg\",\"children\":\"A\"}]]}]}],[\"$\",\"div\",null,{\"className\":\"min-w-0\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex items-center gap-2\",\"children\":[[\"$\",\"$L22\",null,{\"href\":\"https://hashnode.com/@BetaPundit\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"font-medium text-foreground hover:text-primary transition-colors\",\"children\":\"Aditya Sharma\"}],[\"$\",\"div\",null,{\"className\":\"flex items-center gap-1.5 text-sm\",\"children\":[[\"$\",\"$L22\",null,{\"href\":\"https://twitter.com/BetaPundit\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground hover:text-foreground transition-colors\",\"aria-label\":\"X (Twitter)\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-x-twitter\",\"aria-hidden\":true}]}],[\"$\",\"$L22\",null,{\"href\":\"https://linkedin.com/in/aditya-sharma26\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground hover:text-foreground transition-colors\",\"aria-label\":\"LinkedIn\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-linkedin\",\"aria-hidden\":true}]}]]}]]}],[\"$\",\"div\",null,{\"className\":\"text-sm text-muted-foreground line-clamp-2\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cp\u003eI am a full stack developer, a Machine Learning enthusiast \u0026amp; a Flutter Developer. Also, I am a budding Entrepreneur! \u003c/p\u003e\\n\u003cp\u003eI am a student of Life and believe in building tech for people.\u003c/p\u003e\\n\u003cp\u003eI love contributing to open-source projects and use my knowledge to develop a software solution for general problems arising in the community. \u003c/p\u003e\\n\"}}]]}]]}]]}]]}]}],\"$L26\",\"$L27\",false,\"$L28\"]}]]}]}]}]}]}]\n1b:[\"$\",\"section\",null,{\"className\":\"pb-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"px-4 sm:px-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max"])</script>
538<script>self.__next_f.push([1,"-w-[90rem]\",\"children\":[\"$\",\"div\",null,{\"className\":\"max-w-4xl w-full mx-auto\",\"children\":[\"$\",\"div\",null,{\"className\":\"rounded-xl border bg-card text-card-foreground p-5 sm:p-8\",\"children\":[\"$\",\"$L29\",null,{\"host\":\"blog.adityasharma.co\",\"slug\":\"building-flutter-apps-with-clean-architecture-using-getx\",\"postUrl\":\"https://hashnode.com/posts/building-flutter-apps-with-clean-architecture-using-getx/651e8bf5f457ef18124ee464\",\"initialEdges\":[],\"initialPageInfo\":{\"hasNextPage\":false,\"endCursor\":\"10\",\"totalDocuments\":0,\"__typename\":\"PageInfo\"},\"totalComments\":0}]}]}]}]}]}]\n1c:[\"$\",\"section\",null,{\"className\":\"pb-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"px-4 sm:px-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]\",\"children\":[\"$\",\"div\",null,{\"className\":\"max-w-4xl w-full mx-auto\",\"children\":[\"$\",\"div\",null,{\"className\":\"rounded-xl border bg-card text-card-foreground overflow-hidden\",\"children\":[[\"$\",\"div\",null,{\"className\":\"p-5 sm:p-6 space-y-3\",\"children\":[[\"$\",\"$L22\",null,{\"href\":\"/series/what-the-flutter\",\"className\":\"group flex items-center gap-2\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-jelly fa-layer-group h-4 w-4 text-muted-foreground\",\"aria-hidden\":true}],[\"$\",\"h2\",null,{\"className\":\"text-base font-semibold leading-snug tracking-tight text-foreground group-hover:text-primary transition-colors\",\"children\":\"What the Flutter\"}]]}],[\"$\",\"div\",null,{\"className\":\"flex items-center gap-3 text-sm text-muted-foreground\",\"children\":[[\"$\",\"span\",null,{\"children\":[\"Part \",3,\" of \",5]}],[\"$\",\"div\",null,{\"className\":\"flex-1 h-1 rounded-full bg-muted overflow-hidden\",\"children\":[\"$\",\"div\",null,{\"className\":\"h-full rounded-full bg-primary transition-all\",\"style\":{\"width\":\"60%\"}}]}]]}],[\"$\",\"p\",null,{\"className\":\"text-sm leading-relaxed text-muted-foreground line-clamp-2\",\"children\":\"In this series, I will cover interesting micro articles on how to get more comfortable with Flutter \u0026 Dart.\"}]]}],[\"$\",\"$L22\",null,{\"href\":\"/darts-extension-methods-demystified\",\"className\":\"block border-t border-border p-5 sm:p-6 hover:bg-muted transition-colors\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"children\":\"Up next\"}],[\"$\",\"h3\",null,{\"className\":\"text-base sm:text-lg font-semibold leading-snug tracking-tight text-foreground mt-1\",\"children\":\"Dart's Extension Methods: Demystified\"}],[\"$\",\"p\",null,{\"className\":\"text-sm leading-relaxed text-muted-foreground line-clamp-2 mt-1\",\"children\":\"Unleash the power of Dart's Extension Methods with this simple guide\"}]]}]]}]}]}]}]}]\n1d:[\"$\",\"section\",null,{\"className\":\"pb-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"px-4 sm:px-8\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-full xl:mx-auto xl:max-w-[80rem] 2xl:max-w-[90rem]\",\"children\":[\"$\",\"div\",null,{\"className\":\"max-w-4xl w-full mx-auto\",\"children\":[\"$\",\"section\",null,{\"className\":\"space-y-6\",\"children\":[[\"$\",\"h2\",null,{\"className\":\"text-2xl font-bold tracking-tight text-balance\",\"children\":\"More from this blog\"}],[\"$\",\"div\",null,{\"className\":\"flex flex-col gap-6\",\"children\":[[\"$\",\"$L2a\",\"691d6e27e99bd74af5fcbb3a\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:0:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"$L2a\",\"66d6d787b993bebc69066b29\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:1:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"$L2a\",\"65cec8b51d663ec31691ba69\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:2:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"$L2a\",\"62aedf620924134a8048919b\",{\"post\":\"$b:props:children:0:props:publication:posts:edges:4:node\",\"showReadTime\":true,\"showViews\":true}],[\"$\",\"div\",\"author-card\",{\"ref\":\"$undefined\",\"className\":\"rounded-xl border bg-card text-card-foreground transition hover:border-slate-400 dark:hover:border-slate-600 p-5 flex flex-col\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center text-center gap-5\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center gap-3\",\"children\":[[\"$\",\"$L23\",null,{\"className\":\"h-16 w-16 shrink-0\",\"children"])</script>
538<script>self.__next_f.push([1,"\":[[\"$\",\"$L24\",null,{\"src\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602225116875/Bf-bb_MDw.jpeg?auto=compress,format\u0026format=webp\",\"alt\":\"Publication avatar\"}],[\"$\",\"$L25\",null,{\"className\":\"bg-muted text-muted-foreground font-medium text-lg\",\"children\":\"A\"}]]}],[\"$\",\"div\",null,{\"className\":\"space-y-1\",\"children\":[[\"$\",\"p\",null,{\"className\":\"text-base font-semibold text-foreground leading-tight\",\"children\":\"Aditya's Blog\"}],[\"$\",\"p\",null,{\"className\":\"text-sm text-muted-foreground\",\"children\":[10,\" posts\"]}]]}]]}],[\"$\",\"div\",null,{\"className\":\"prose prose-sm dark:prose-invert max-w-xs mx-auto text-muted-foreground\",\"dangerouslySetInnerHTML\":{\"__html\":\"\u003cp\u003eI convert scrap to sculptures!\u003c/p\u003e\\n\"}}],[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center justify-center gap-3\",\"children\":[[],[[\"$\",\"a\",\"Twitter\",{\"href\":\"https://twitter.com/BetaPundit\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"Twitter\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-x-twitter\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"GitHub\",{\"href\":\"https://github.com/BetaPundit\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"GitHub\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-github\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"LinkedIn\",{\"href\":\"https://linkedin.com/in/aditya-sharma26\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"LinkedIn\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-linkedin\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"Website\",{\"href\":\"https://adityasharma.co\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"Website\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-jelly fa-globe\",\"aria-hidden\":true}]}],[\"$\",\"a\",\"Hashnode\",{\"href\":\"https://hashnode.com/@BetaPundit\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"text-muted-foreground transition-colors hover:text-foreground\",\"aria-label\":\"Hashnode\",\"children\":[\"$\",\"i\",null,{\"className\":\"fa-brands fa-hashnode\",\"aria-hidden\":true}]}]]]}]]}]}]]}]]}]}]}]}]}]\n2b:T4c7,{\"@context\":\"https://schema.org\",\"@type\":\"Article\",\"url\":\"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx\",\"mainEntityOfPage\":\"https://blog.adityasharma.co/building-flutter-apps-with-clean-architecture-using-getx\",\"headline\":\"Building Flutter Apps with Clean Architecture Using GetX\",\"description\":\"Hello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure...\",\"author\":{\"@type\":\"Person\",\"name\":\"Aditya Sharma\",\"url\":\"https://hashnode.com/@BetaPundit\",\"image\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1602219284226/8R5Zs52xB.jpeg\",\"sameAs\":\"https://x.com/BetaPundit\"},\"publisher\":{\"@type\":\"Organization\",\"name\":\"Aditya's Blog\",\"url\":\"https://blog.adityasharma.co\"},\"datePublished\":\"2023-10-05T10:12:05.108Z\",\"dateModified\":\"2024-05-24T17:35:34.293Z\",\"image\":{\"@type\":\"ImageObject\",\"url\":\"https://cdn.hashnode.com/res/hashnode/image/upload/v1696485327830/8b86432c-2243-4136-8241-d2224d5cee4f.png\"},\"keywords\":\"Flutter, app development, Mobile Development, clean code, Developer\"}1e:[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$2b\"}}]\n2c:I[78655,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.
538js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce"])</script>
538<script>self.__next_f.push([1,"41bf29cdfb993a.js\"],\"TableOfContents\"]\n26:[\"$\",\"div\",null,{\"className\":\"max-w-2xl mx-auto\",\"children\":[\"$\",\"$L22\",null,{\"href\":\"/series/what-the-flutter\",\"className\":\"group flex items-center gap-3 rounded-lg border border-border px-3 py-2 transition hover:border-primary hover:bg-accent\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-jelly fa-layer-group h-4 w-4 text-muted-foreground group-hover:text-primary transition-colors\",\"aria-hidden\":true}],[\"$\",\"div\",null,{\"className\":\"flex flex-col\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-sm text-muted-foreground\",\"children\":\"Part of series\"}],[\"$\",\"span\",null,{\"className\":\"text-sm font-medium text-foreground group-hover:text-primary transition-colors\",\"children\":\"What the Flutter\"}]]}]]}]}]\n2d:T976f,\u003cp\u003eHello, Flutter aficionados! 🚀 In this blog post, we're about to embark on an exciting journey into the world of Flutter clean architecture using the incredible GetX package. Whether you're a seasoned developer or just starting your Flutter adventure, this guide will help you build organized and maintainable Flutter apps. So, grab your coding capes, and let's dive in!\u003c/p\u003e\n\u003ch2 id=\"heading-what-is-clean-architecture\"\u003eWhat is Clean Architecture?\u003c/h2\u003e\n\u003cp\u003eBefore we delve into the nitty-gritty of Flutter clean architecture with GetX, let's start with a quick introduction to clean architecture.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePros of Clean Architecture:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eModularity:\u003c/strong\u003e Clean architecture promotes a modular approach, making your codebase easier to understand and maintain.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eTestability:\u003c/strong\u003e It encourages writing unit tests and makes your codebase more test-friendly.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eScalability:\u003c/strong\u003e Your app can grow and evolve without major architectural changes.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eIndependence:\u003c/strong\u003e The architecture keeps external frameworks and libraries at bay, preventing them from tightly coupling with your code.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eCons of Clean Architecture:\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eInitial Complexity:\u003c/strong\u003e Setting up a clean architecture project might seem more daunting compared to a simple structure.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eLearning Curve:\u003c/strong\u003e Developers new to clean architecture may take some time to fully grasp its concepts.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eNow that we're clear on the basics, let's roll up our sleeves and get started!\u003c/p\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-project-setup\"\u003eProject Setup\u003c/h2\u003e\n\u003cp\u003eThis project is 
538written using the current latest stable versions of:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://docs.flutter.dev/development/tools/sdk/releases\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eFlutter\u003c/strong\u003e\u003c/a\u003e: v3.10.6\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://dart.dev/guides/language/evolution\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eDart\u003c/strong\u003e\u003c/a\u003e: v3.0.6\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"heading-packages\"\u003ePackages\u003c/h3\u003e\n\u003cp\u003eBefore we start coding, let's gather our tools and libraries. Here are the essential packages we'll use:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/get\" rel=\"noopener ugc\"\u003e\u003cstrong\u003egetX\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e The Swiss Army knife for state management. (You can use any kind of state management you prefer to work with such as \u003ca target=\"_blank\" href=\"https://pub.dev/packages/provider\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eprovider\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003eor\u003c/strong\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/flutter_bloc\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eflutter_bloc\u003c/strong\u003e\u003c/a\u003e)\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/freezed\" rel=\"noopener ugc\"\u003e\u003cstrong\u003efreezed\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e A code generation package for creating immutable data models.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/retrofit\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eretrofit\u003c/strong\u003e\u003c/a\u003e \u003cstrong\u003e\u0026amp;\u003c/strong\u003e \u003ca target=\"_blank\" href=\"https://pub.dev/packages/dio\" rel=\"noopener ugc\"\u003e\u003cstrong\u003edio\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e For handling HTTP requests.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/get_it\" rel=\"noopener ugc\"\u003e\u003cstrong\u003eg"])</script>
538<script>self.__next_f.push([1,"et_it\u003c/strong\u003e\u003c/a\u003e \u003cstrong\u003e\u0026amp;\u003c/strong\u003e \u003ca target=\"_blank\" href=\"https://pub.dev/packages/injectable\" rel=\"noopener ugc\"\u003e\u003cstrong\u003einjectable\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e For dependency injection.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/go_router\" rel=\"noopener ugc\"\u003e\u003cstrong\u003ego_router\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e A declarative routing package that provides a convenient, URL-based API for navigating between different screens.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003ca target=\"_blank\" href=\"https://pub.dev/packages/pretty_dio_logger\" rel=\"noopener ugc\"\u003e\u003cstrong\u003epretty_dio_logger\u003c/strong\u003e\u003c/a\u003e\u003cstrong\u003e:\u003c/strong\u003e A delightful logger for HTTP requests.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLet's include these packages in our \u003ccode\u003epubspec.yaml\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-yaml\"\u003e\u003cspan class=\"hljs-attr\"\u003edependencies:\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eget:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^4.6.6\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eretrofit:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^4.0.2\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003edio:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^5.3.3\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eget_it:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^7.6.4\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003einjectable:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.3.0\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003ego_router:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^11.1.2\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003epretty_dio_logger:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^1.3.1\u003c/span\u003e\n\n\u003cspan class=\"hljs-attr\"\u003edev_dependencies:\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eflutter_test:\u003c/span\u003e\n    \u003cspan class=\"hljs-attr\"\u003esdk:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003eflutter\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eflutter_lints:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.0.0\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003ebuild_runner:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.4.6\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003efreezed:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.4.3\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003ejson_serializable:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^6.7.1\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003eretrofit_generator:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^7.0.8\u003c/span\u003e\n  \u003cspan class=\"hljs-attr\"\u003einjectable_generator:\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e^2.4.0\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-folder-structure\"\u003eFolder Structure\u003c/h3\u003e\n\u003cp\u003eOrganization is key in clean architecture. Here's a recommended folder structure:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-c\"\u003elib/\n  ├── config/\n  │     ├── injection/\n  │     ├── router/\n  │     └── themes/\n  ├── data/\n  │     ├── datasources/\n  │     │    ├── local/\n  │     │    └── remote/\n  │     └── repositories/\n  ├── domain/\n  │     ├── models/\n  │     └── repositories/\n  ├── presentation/\n  │     ├── controllers/\n  │     ├── pages/\n  │     └── widgets/\n  ├── utils/\n  │     ├── constants/\n  │     ├── extensions/\n  │     └── resources/\n  └── main.dart\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe \u003cstrong\u003econfig\u003c/strong\u003e folder includes the configuration of the app (\u003cstrong\u003ethemes\u003c/strong\u003e, \u003cstrong\u003erouter\u003c/strong\u003e, ..etc) and anything else related to the app’s configurations.\u003c/p\u003e\n\u003cp\u003eThe \u003cstrong\u003eutils\u003c/strong\u003e folder, on the other hand, includes (\u003cstrong\u003econstants\u003c/strong\u003e, \u003cstrong\u003eextensions\u003c/strong\u003e, \u003cstrong\u003eresources\u003c/strong\u003e, ..etc) and anything related to that.\u003c/p\u003e\n\u003cp\u003eThe (\u003cstrong\u003edata\u003c/strong\u003e, \u003cstrong\u003edomain\u003c/strong\u003e and \u003cstrong\u003epresentation\u003c/strong\u003e) folders will be explained later in detail.\u003c/p\u003e\n\u003ch3 id=\"heading-architecture-and-dependency-rules\"\u003eArchitecture and Dependency Rules\u003c/h3\u003e\n\u003cp\u003eThe clean architecture consists of three primary layers:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eData Layer:\u003c/strong\u003e Responsible for data fetching and caching.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eDomain Layer:\u003c/strong\u003e Houses the core business logic.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003ePresentation Layer:\u003c/strong\u003e Manages the user interface and user interactions.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eEac
538h layer has its unique responsibilities, and dependencies flow inward. The Data Layer is independent, the Domain Layer depends on Data, and the Presentation Layer relies on both Data and Domain.\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"h"])</script>
538<script>self.__next_f.push([1,"ttps://cdn.hashnode.com/res/hashnode/image/upload/v1696499988717/67904f8e-432e-4d19-86d3-491d790d0f1a.png\" alt class=\"image--center mx-auto\" /\u003e\u003c/p\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-app-configuration\"\u003eApp Configuration\u003c/h2\u003e\n\u003cp\u003eBefore diving into the layers, let's configure our app. Here's a sneak peek:\u003c/p\u003e\n\u003ch3 id=\"heading-routing\"\u003eRouting\u003c/h3\u003e\n\u003cp\u003eRouting is a fundamental aspect of any Flutter app. We'll use the \u003ccode\u003ego_router\u003c/code\u003e package to handle navigation efficiently.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003eYou can absolutely choose whatever the routing package you prefer, and that suits your needs.\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eNow, after adding the package to your \u003ccode\u003epubspec.yaml\u003c/code\u003e file as we talked about it at the beginning of the article, create a folder in this path \u003cstrong\u003elib/config\u003c/strong\u003e and call it \u003cstrong\u003erouter\u003c/strong\u003e, then inside the folder create a file and name it \u003ccode\u003eapp_router.dart\u003c/code\u003e as shown below:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-meta\"\u003e@singleton\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eAppRouter\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e GoRouter _router = GoRouter(\n    debugLogDiagnostics: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n    initialLocation: \u003cspan class=\"hljs-string\"\u003e'/'\u003c/span\u003e,\n    routes: [\n      GoRoute(\n        path: \u003cspan class=\"hljs-string\"\u003e'/'\u003c/span\u003e,\n        builder: (context, state) =\u0026gt; \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e HomeScreen(),\n      ),\n      GoRoute(\n        path: \u003cspan class=\"hljs-string\"\u003e'/profile'\u003c/span\u003e,\n        builder: (context, state) =\u0026gt; \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e UserDetailsScreen(),\n      ),\n      ...\n      \u003cspan class=\"hljs-comment\"\u003e// Add more routes as needed\u003c/span\u003e\n  );\n\n  GoRouter \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e router =\u0026gt; _router;\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-theme\"\u003eTheme\u003c/h3\u003e\n\u003cp\u003eDefining a global theme ensures consistency in your app's appearance. Let's create a file \u003ccode\u003eapp_theme.dart\u003c/code\u003e inside \u003cstrong\u003elib/config/themes\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dtdcopedia/config/themes/app_colors.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:flutter/material.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eAppTheme\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e ThemeData \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e light {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e ThemeData(\n      appBarTheme: \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e AppBarTheme(\n        elevation: \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e,\n        color: AppColors.primary,\n      ),\n      scaffoldBackgroundColor: AppColors.background,\n      primaryColor: AppColors.primary,\n      splashColor: Colors.transparent,\n      fontFamily: \u003cspan class=\"hljs-string\"\u003e'Hind'\u003c/span\u003e,\n    );\n  }\n\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e ThemeData \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e dark {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e ThemeData(\n      appBarTheme: \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e AppBarTheme(\n        elevation: \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e,\n        color: AppColors.primary,\n      ),\n      scaffoldBackgroun
538dColor: AppColors.text,\n      primaryColor: AppColors.primary,\n      splashColor: Colors.transparent,\n      fontFamily: \u003cspan class=\"hljs-string\"\u003e'Hind'\u003c/span\u003e,\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis sets up your app's global theme with the specified colors, fonts, and more. You can customize it the way you like.\u003c/p\u003e\n\u003cdiv data-node-type=\"callout\"\u003e\n\u003cdiv data-node-type=\"callout-emoji\"\u003e💡\u003c/div\u003e\n\u003cdiv data-node-type=\"callout-text\"\u003eUse \u003cstrong\u003eThemeExtension\u003c/strong\u003e for creating custom themes: \u003ca target=\"_blank\" href=\"https://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa\" rel=\"noopener ugc\"\u003ehttps://medium.com/geekculture/flutter-3-how-to-extend-themedata-56b8923bf1aa\u003c/a\u003e\u003c/div\u003e\n\u003c/div\u003e\n\n\u003ch3 id=\"heading-utils\"\u003e\u003cstrong\u003eUtils\u003c"])</script>
538<script>self.__next_f.push([1,"/strong\u003e\u003c/h3\u003e\n\u003cp\u003eIn the \u003ccode\u003eutils\u003c/code\u003e folder, you can organize your utility functions, constants, extensions, and resources neatly. Here's an example structure:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003elib/\n  ├── utils/\n  │     ├── constants/\n  │     │     └── app_constants.dart\n  │     ├── extensions/\n  │     │     ├── string_extensions.dart\n  │     │     └── date_extensions.dart\n  │     ├── resources/\n  │     │     └── data_state.dart\n  │     └── ...\n  └── ...\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eFor example, in \u003ccode\u003eapp_constants.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eAppConstants\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e appTitle = \u003cspan class=\"hljs-string\"\u003e'Flutter Clean Architecture'\u003c/span\u003e;\n\n \u003cspan class=\"hljs-comment\"\u003e// Networking and APIs\u003c/span\u003e\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e baseUrl = \u003cspan class=\"hljs-string\"\u003e'https://appi.example.com/'\u003c/span\u003e; \n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e defaultApiKey = \u003cspan class=\"hljs-string\"\u003e'YOUR_API_KEY'\u003c/span\u003e;\n\n \u003cspan class=\"hljs-comment\"\u003e// Storage and Databases\u003c/span\u003e\n \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e appTableName = \u003cspan class=\"hljs-string\"\u003e'app_table'\u003c/span\u003e;\n \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e databaseName = \u003cspan class=\"hljs-string\"\u003e'app_database.db'\u003c/span\u003e;\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eAnd in \u003ccode\u003estring_extensions.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eextension\u003c/span\u003e StringExtensions \u003cspan class=\"hljs-keyword\"\u003eon\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e {\n  \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e capitalize() {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e\"\u003cspan class=\"hljs-subst\"\u003e${\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e[\u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e].toUpperCase()}\u003c/span\u003e\u003cspan class=\"hljs-subst\"\u003e${\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.substring(\u003cspan class=\"hljs-number\"\u003e1\u003c/span\u003e)}\u003c/span\u003e\"\u003c/span\u003e;\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIn the \u003cstrong\u003elib/utils/resources\u003c/strong\u003e create a file and name it \u003ccode\u003edata_state.dart\u003c/code\u003e which contains the following code:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003c/span\u003e{\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e T data;\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e DioError error;\n    \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e DataState({\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.data, \u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e.error});\n}\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataSuccess\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003c/span\u003e{ \n    \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e DataSuccess(T data): \u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e(data: data);\n}\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataFailed\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eT\u003c/span\u003e\u0026gt; \u003c/span\u003e{ \n    \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e DataFailed (DioError error): \u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e(error: error);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis wrapper class \u003ccode\u003eDataState\u003c/c"])</script>
538<script>self.__next_f.push([1,"ode\u003e can be used to wrap our entire network call in order to determine the state of the request being sent to the server and its response.\u003c/p\u003e\n\u003cp\u003eAs you can see in the code, we’ve two different states, one when we get a successful response (\u003cstrong\u003eDataSuccess\u003c/strong\u003e), and the other when an error occurs while sending the request or receiving the response (\u003cstrong\u003eDataFailed\u003c/strong\u003e).\u003c/p\u003e\n\u003cp\u003eThis is very important as later on when we will have too many requests and logic, you will see how minimized the code will become.\u003c/p\u003e\n\u003ch3 id=\"heading-dependency-injection\"\u003eDependency Injection\u003c/h3\u003e\n\u003cp\u003eDependency injection is a crucial part of clean architecture. We use \u003ccode\u003eget_it\u003c/code\u003e and \u003ccode\u003einjectable\u003c/code\u003e to manage our dependencies efficiently. Here's a glimpse of how it works:\u003c/p\u003e\n\u003cp\u003eFirst, inside \u003cstrong\u003elib/config/injection,\u003c/strong\u003e create a file named \u003ccode\u003einjection.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:get_it/get_it.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:injectable/injectable.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'injection.config.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e locator = GetIt.instance;\n\n\u003cspan class=\"hljs-meta\"\u003e@InjectableInit\u003c/span\u003e()\nFuture\u0026lt;\u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e\u0026gt; configureDependencies() {\n  \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e locator.init();\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNow, create a file named \u003ccode\u003eregister_module.dart\u003c/code\u003e inside the same folder. Here we can inject third party packages such as \u003cstrong\u003eSharedPreferences\u003c/strong\u003e \u0026amp; \u003cstrong\u003eDio\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'dart:io'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:injectable/injectable.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:pretty_dio_logger/pretty_dio_logger.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:shared_preferences/shared_preferences.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-meta\"\u003e@module\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eRegisterModule\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-comment\"\u003e// Inject SharedPreferences\u003c/span\u003e\n  \u003cspan class=\"hljs-meta\"\u003e@preResolve\u003c/span\u003e\n  Future\u0026lt;SharedPreferences\u0026gt; \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e prefs =\u0026gt; SharedPreferences.getInstance();\n\n  \u003cspan class=\"hljs-comment\"\u003e// Inject Dio\u003c/span\u003e\n  \u003cspan class=\"hljs-meta\"\u003e@lazySingleton\u003c/span\u003e\n  Dio dio(SharedPreferences sharedPreferences) {\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e token = sharedPreferences.getString(\u003cspan class=\"hljs-string\"\u003e'token'\u003c/span\u003e) ?? \u003cspan class=\"hljs-string\"\u003e''\u003c/span\u003e;\n\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Dio(\n      BaseOptions(\n        baseUrl: AppConstants.baseUrl,\n        headers: {\n          \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (token.isNotEmpty) HttpHeaders.authorizationHeader: \u003cspan class=\"hljs-string\"\u003e\"Bearer \u003cspan class=\"hljs-subst\"\u003e$token\u003c/span\u003e\"\u003c/span\u003e,\n        },\n      ),\n    )..interceptors.add(\n        PrettyDioLogger(\n          requestHeader: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n          requestBody: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n          responseHeader: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e,\n          maxWidth: \u003cspan class=\"hljs-number\"\u003e120\u003c/span\u003e,\n        ),\n      );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNow you can import and use these utility functions, constants \u0026amp; injected modules throughout your project for a clean and organized codebase. Finally, the \u003ccode\u003emain.dart\u003c/code\u003e file will look like this:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e main() \u003cspan class=\"hljs-keyword\"\u003easync\u003c/span\u003e {\n  WidgetsFlutterBinding.ensureInitialized();\n  \u003cspan c"])</script>
538<script>self.__next_f.push([1,"lass=\"hljs-keyword\"\u003eawait\u003c/span\u003e configureDependencies();\n  runApp(\u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e MyApp());\n}\n\n\u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e _router = locator\u0026lt;AppRouter\u0026gt;().router;\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eMyApp\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eStatelessWidget\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e MyApp({\u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e.key});\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  Widget build(BuildContext context) {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e MaterialApp.router(\n      debugShowCheckedModeBanner: \u003cspan class=\"hljs-keyword\"\u003efalse\u003c/span\u003e,\n      title: AppConstants.appTitle,\n      theme: AppTheme.light,\n      darkTheme: AppTheme.dark,\n      routerConfig: _router,\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-domain-layer\"\u003e\u003cstrong\u003eDomain Layer\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eNow, let's dive into the Domain Layer. The Domain Layer is the core of the application's business logic. It encapsulates the rules, behaviours, and core functionality that define how the application operates. This layer is entirely independent of external factors such as databases, network calls, or the user interface.\u003c/p\u003e\n\u003cp\u003eIn \u003cstrong\u003eClean Architecture\u003c/strong\u003e, the \u003cstrong\u003eDomain Layer\u003c/strong\u003e should be independent of any specific implementation or framework. This means that the code in the Domain Layer should be written in a \u003cstrong\u003etechnology-agnostic\u003c/strong\u003e way, with no dependencies on external systems or libraries.\u003c/p\u003e\n\u003ch3 id=\"heading-entities-models\"\u003e\u003cstrong\u003eEntities (Models)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eEntities/Models are plain Dart classes that represent the core business objects of the application. They model the data that the app operates on and typically contain minimal logic. Entities are defined solely based on the business requirements, and they do not contain any database-specific or UI-related code.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eFreezed\u003c/strong\u003e helps us create immutable data models effortlessly. Here's a sample domain model \u003ccode\u003euser.dart\u003c/code\u003e inside \u003cstrong\u003elib/domain/models\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003epart\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'user.freezed.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-meta\"\u003e@freezed\u003c/span\u003e\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUser\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003ewith\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003e_\u003c/span\u003e$\u003cspan class=\"hljs-title\"\u003eUser\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003efactory\u003c/span\u003e User({\n    \u003cspan class=\"hljs-keyword\"\u003erequired\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id,\n    \u003cspan class=\"hljs-keyword\"\u003erequired\u003c/span\u003e \u003cspan class=\"hljs-built_in\"\u003eString\u003c/span\u003e name,\n    \u003cspan class=\"hljs-comment\"\u003e// Add more fields as 
538needed\u003c/span\u003e\n  }) = _User;\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-repositories-abstract-definitions\"\u003e\u003cstrong\u003eRepositories (Abstract Definitions)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eIn the Domain Layer, repositories are defined as abstract classes or interfaces. These abstract definitions declare the methods for interacting with data without specifying how data is fetched or stored. The concrete implementations of these repositories are located in the Data Layer.\u003c/p\u003e\n\u003cp\u003eRepositories are interfaces that define data sources for our domain layer. For example, \u003ccode\u003euser_repository.dart\u003c/code\u003e inside \u003cstrong\u003elib/domain/repositories\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserRepository\u003c/span\u003e \u003c/span\u003e{\n  Future\u0026lt;UserEntity\u0026gt; getUserById(\u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"heading-data-layer\"\u003e\u003cstrong\u003eData Layer\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003eThe Data Layer is the foundational layer responsible for handling data operations in a Flutter app. Its primary purpose is to abstract and encapsulate data sources, ensuring that the rest of the application doesn't need to know the specifics"])</script>
538<script>self.__next_f.push([1," of data retrieval and storage.\u003c/p\u003e\n\u003ch3 id=\"heading-data-sources\"\u003e\u003cstrong\u003eData Sources\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eData Sources are responsible for interacting with external data providers, such as APIs, databases, or local storage. They define the interfaces for fetching and storing data. This layer is typically implemented using packages like \u003ccode\u003eRetrofit\u003c/code\u003e, \u003ccode\u003eDio\u003c/code\u003e, or other HTTP client libraries for API interactions, as well as database libraries like \u003ccode\u003eSQFLite\u003c/code\u003e for local storage.\u003c/p\u003e\n\u003cp\u003eHere's a sample data source service \u003ccode\u003euser_data_source.dart\u003c/code\u003e inside \u003cstrong\u003elib/data/datasources/remote\u003c/strong\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-meta\"\u003e@RestApi\u003c/span\u003e(baseUrl: \u003cspan class=\"hljs-string\"\u003e'https://api.example.com/'\u003c/span\u003e)\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserDataSource\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003efactory\u003c/span\u003e UserDataSource(Dio dio) = _UserDataSource;\n\n  \u003cspan class=\"hljs-meta\"\u003e@GET\u003c/span\u003e(\u003cspan class=\"hljs-string\"\u003e'/users/{id}'\u003c/span\u003e)\n  Future\u0026lt;UserEntity\u0026gt; getUserById(\u003cspan class=\"hljs-meta\"\u003e@Path\u003c/span\u003e() \u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id);\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eNow, create \u003ccode\u003edata_source_injection.dart\u003c/code\u003e file inside \u003cstrong\u003elib/config/injection\u003c/strong\u003e and inject the \u003cstrong\u003eUserDataSource\u003c/strong\u003e class as a module:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:your_package_name/data/datasources/remote/user_data_source.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:injectable/injectable.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-meta\"\u003e@module\u003c/span\u003e\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eDataSourceInjectableModule\u003c/span\u003e \u003c/span\u003e{\n  UserDataSource getUserDataSource(Dio dio) =\u0026gt; UserDataSource(dio);\n  \u003cspan class=\"hljs-comment\"\u003e// Add more data dources as needed\u003c/span\u003e\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-repositories-implementation\"\u003e\u003cstrong\u003eRepositories (Implementation)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eRepositories act as intermediaries between Data Sources and the Domain Layer. They provide a clean and consistent API for accessing data. Repositories are defined as abstract classes in the Domain Layer, listing methods like \u003ccode\u003egetUserById()\u003c/code\u003e or \u003ccode\u003efetchPosts()\u003c/code\u003e. The concrete implementations of these repositories, found in the Data Layer, interact with Data Sources to fetch and manipulate data. Repositories also manage data caching and storage.\u003c/p\u003e\n\u003cp\u003eBefore we implement the \u003cstrong\u003eUserRepository\u003c/strong\u003e class, we need to create a base class called \u003cstrong\u003eBaseApiRepository\u003c/strong\u003e, create a folder inside the \u003cstrong\u003edata\u003c/strong\u003e folder and name it \u003cstrong\u003ebase\u003c/strong\u003e, then create a file inside it and name it \u003ccode\u003ebase_api_repository.dart\u003c/code\u003e which contains the following:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'dart:io'\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eshow\u003c/span\u003e HttpStatus;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:retrofit/dio.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:dio/dio.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:your_project_name/utils/resources/data_state.dart'\u003c/span\u003e;\n\u003cspan class=\"hljs-keyword\"\u003eimport\u003c/span\u003e \u003cspan class=\"hljs-string\"\u003e'package:flutter/material.dart'\u003c/span\u003e;\n\n\u003cspan class=\"hljs-keyword\"\u003eabstract\u003c/span\u003e \u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBaseApiRepository\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-meta\"\u003e@protected\u003c/span\u003e\n  Future\u0026lt;DataState\u0026lt;T?\u0026gt;\u0026gt; getStateOf\u0026lt;T\u0026gt;({\n    \u003cspan class=\"hljs-keyword\"\u003erequired\u003c/span\u003e Future\u0026lt;HttpResponse\u0026lt;T\u0026gt;\u0026gt; \u003cspan class=\"hljs-built_in\"\u003e"])</script>
538<script>self.__next_f.push([1,"Function\u003c/span\u003e() request,\n  }) \u003cspan class=\"hljs-keyword\"\u003easync\u003c/span\u003e {\n    \u003cspan class=\"hljs-keyword\"\u003etry\u003c/span\u003e {\n      \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e httpResponse = \u003cspan class=\"hljs-keyword\"\u003eawait\u003c/span\u003e request();\n      \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (httpResponse.response.statusCode == HttpStatus.created || httpResponse.response.statusCode == HttpStatus.ok) {\n        \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e DataSuccess(httpResponse.data);\n      } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (httpResponse.response.statusCode == HttpStatus.unauthorized) {\n        \u003cspan class=\"hljs-keyword\"\u003ethrow\u003c/span\u003e DataFailed(DioException(\n          response: httpResponse.response,\n          requestOptions: httpResponse.response.requestOptions,\n        ));\n      } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e {\n        \u003cspan class=\"hljs-keyword\"\u003ethrow\u003c/span\u003e DioException(\n          response: httpResponse.response,\n          requestOptions: httpResponse.response.requestOptions,\n        );\n      }\n    } \u003cspan class=\"hljs-keyword\"\u003eon\u003c/span\u003e DioException \u003cspan class=\"hljs-keyword\"\u003ecatch\u003c/span\u003e (error) {\n      \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e DataFailed(error);\n    }\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThis base class plays an important role here, which wraps our API call that comes from the data source \u003ccode\u003euser_data_source.dart\u003c/code\u003e and return a \u003cstrong\u003eDataState\u003c/strong\u003e object instead of \u003cstrong\u003eHttpResponse\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eBy doing so, we’re reducing the boilerplate code in each method without the need to write “\u003cstrong\u003etry-catch”\u003c/strong\u003e or “\u003cstrong\u003eif”\u003c/strong\u003e statements everywhere because you only need to pass the request in the “\u003cstrong\u003egetStateOf\u003c/strong\u003e” method and this should do the rest and return the result based on the generic “\u003cstrong\u003eT\u003c/strong\u003e” type.\u003c/p\u003e\n\u003cp\u003eNow create \u003ccode\u003euser_repository_impl.dart\u003c/code\u003e inside \u003cstrong\u003elib/data/repositories\u003c/strong\u003e \u0026amp; implement the previously defined \u003cstrong\u003eUserRepository\u003c/strong\u003e class as below:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-meta\"\u003e@Injectable\u003c/span\u003e(\u003cspan class=\"hljs-keyword\"\u003eas\u003c/span\u003e: UserRepository)\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserRepositoryImpl\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eBaseApiRepository\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eimplements\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserRepository\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e UserDataSource _userDataSource;\n  UserRepositoryImpl(\u003cspan class=\"hljs-keyword\"\u003ethis\u003c/span\u003e._userDataSource);\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  Future\u0026lt;DataState\u0026lt;User?\u0026gt;\u0026gt; getUserById(\u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id) {\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e getStateOf\u0026lt;User\u0026gt;(\n      request: () =\u0026gt; _userDataSource.getUserById(id),\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"heading-presentation-layer\"\u003ePresentation Layer\u003c/h2\u003e\n\u003cp\u003eLast but not least, we arrive at the Presentation Layer, where we build the user interface, controllers, screens, and common widgets.\u003c/p\u003e\n\u003cp\u003eThe benefit of using \u003cstrong\u003ePresentation Layer\u003c/strong\u003e is that it makes the codebase more modular and maintainable. By separating the user interface from the business logic and infrastru
538cture, it becomes easier to modify and extend the application over time. Also, it allows for different user interfaces to be developed and used with the same underlying business logic. For example, a \u003cstrong\u003eweb application\u003c/strong\u003e, a \u003cstrong\u003emobile application\u003c/strong\u003e, and a \u003cstrong\u003edesktop application\u003c/strong\u003e could all use the same Domain Layer but have different Presentation Layers that are optimized for their respective platforms.\u003c/p\u003e\n\u003ch3 id=\"heading-controllers\"\u003eControllers\u003c/h3\u003e\n\u003cp\u003eControllers manage the state and business logic of our app. Here's a snippet of a controller \u003ccode\u003euser_controller.dart\u003c/code\u003e inside \u003cstrong\u003elib/presentation/controllers\u003c/strong\u003e using GetX:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e"])</script>
538<script>self.__next_f.push([1," \u003cspan class=\"hljs-title\"\u003eUserController\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eGetxController\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003estatic\u003c/span\u003e UserController \u003cspan class=\"hljs-keyword\"\u003eget\u003c/span\u003e call =\u0026gt; Get.find();\n\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e UserRepository _userRepository = locator\u0026lt;UserRepository\u0026gt;();\n  \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e Rx\u0026lt;User\u0026gt; user = Rx\u0026lt;User\u0026gt;(User(id: \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e, name: \u003cspan class=\"hljs-string\"\u003e''\u003c/span\u003e));\n\n  Future\u0026lt;\u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e\u0026gt; fetchUser(\u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e id) \u003cspan class=\"hljs-keyword\"\u003easync\u003c/span\u003e {\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e result = \u003cspan class=\"hljs-keyword\"\u003eawait\u003c/span\u003e _userRepository.getUserById(id);\n    user.value = result;\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"heading-screens\"\u003eScreens\u003c/h3\u003e\n\u003cp\u003eScreens are where the magic happens. Screens correspond to the different views or pages of the app. Each screen is typically represented by a Flutter widget. Screens are responsible for rendering the UI and displaying data to the user.\u003c/p\u003e\n\u003cp\u003eYou can create screen files inside \u003cstrong\u003elib/presentation/screens\u003c/strong\u003e folder. For example, here's \u003ccode\u003euser_details_screen.dart\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-dart\"\u003e\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eUserDetailsScreen\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eStatefulWidget\u003c/span\u003e \u003c/span\u003e{\n  \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e UserDetailsScreen({\u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e.key});\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  State\u0026lt;UserDetailsScreen\u0026gt; createState() =\u0026gt; _UserDetailsScreenState();\n}\n\n\u003cspan class=\"hljs-class\"\u003e\u003cspan class=\"hljs-keyword\"\u003eclass\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003e_UserDetailsScreenState\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eextends\u003c/span\u003e \u003cspan class=\"hljs-title\"\u003eState\u003c/span\u003e\u0026lt;\u003cspan class=\"hljs-title\"\u003eUserDetailsScreen\u003c/span\u003e\u0026gt; \u003c/span\u003e{\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  \u003cspan class=\"hljs-keyword\"\u003evoid\u003c/span\u003e initState() {\n    \u003cspan class=\"hljs-comment\"\u003e// Initialize \u0026amp; inject UserController() using Get.put()\u003c/span\u003e\n    Get.put\u0026lt;UserController\u0026gt;(UserController(), permanent: \u003cspan class=\"hljs-keyword\"\u003etrue\u003c/span\u003e);\n    \u003cspan class=\"hljs-keyword\"\u003esuper\u003c/span\u003e.initState();\n  }\n\n  \u003cspan class=\"hljs-meta\"\u003e@override\u003c/span\u003e\n  Widget build(BuildContext context) {\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e controller = UserController.call;\n    \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e userIdController = TextEditingController();\n\n    \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Scaffold(\n      appBar: AppBar(\n        title: Text(\u003cspan class=\"hljs-string\"\u003e'User Details'\u003c/span\u003e),\n      ),\n      body: Padding(\n        padding: \u003cspan class=\"hljs-keyword\"\u003econst\u003c/span\u003e EdgeInsets.all(\u003cspan class=\"hljs-number\"\u003e16.0\u003c/span\u003e),\n        child: Column(\n          children: [\n            TextField(\n              controller: userIdController,\n              decoration: InputDecoration(labelText: \u003cspan class=\"hljs-string\"\u003e'Enter User ID'\u003c/span\u003e),\n              keyboardType: TextInputType.number,\n            ),\n            SizedBox(height: \u003cspan class=\"hljs-number\"\u003e16.0\u003c/span\u003e),\n            ElevatedButton(\n              onPressed: () {\n                \u003cspan class=\"hljs-keyword\"\u003efinal\u003c/span\u003e userId = \u003cspan class=\"hljs-built_in\"\u003eint\u003c/span\u003e.tryParse(userIdController.text);\n                \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (userId != \u003cspan class=\"hljs-keyword\"\u003enull\u003c/span\u003e) {\n                  controller.fetchUser(userId);\n                } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e {\n                  \u003cspan class=\"hljs-comment\"\u003e// Handle invalid input\u003c/span\u003e\n                  Get.snackbar(\u003cspan class=\"hljs-string\"\u003e'Invalid Input'\u003c/span\u003e, \u003cspan class=\"hljs-string\"\u003e'Please enter a valid User ID'\u003c/span\u003e,\n                      snackPosition: SnackPosition.BOTTOM);\n                }\n              },\n              child: Text(\u003cspan class=\"hljs-string\"\u003e'Fetch User'\u003c/span\u003e),\n      "])</script>
538<script>self.__next_f.push([1,"      ),\n            SizedBox(height: \u003cspan class=\"hljs-number\"\u003e16.0\u003c/span\u003e),\n            Obx(() {\n              \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (controller.isLoading.value) {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e CircularProgressIndicator();\n              } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e \u003cspan class=\"hljs-keyword\"\u003eif\u003c/span\u003e (controller.user.value.id == \u003cspan class=\"hljs-number\"\u003e0\u003c/span\u003e) {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Text(\u003cspan class=\"hljs-string\"\u003e'User details will appear here'\u003c/span\u003e);\n              } \u003cspan class=\"hljs-keyword\"\u003eelse\u003c/span\u003e {\n                \u003cspan class=\"hljs-keyword\"\u003ereturn\u003c/span\u003e Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    Text(\u003cspan class=\"hljs-string\"\u003e'User ID: \u003cspan class=\"hljs-subst\"\u003e${controller.user.value.id}\u003c/span\u003e'\u003c/span\u003e),\n                    Text(\u003cspan class=\"hljs-string\"\u003e'Name: \u003cspan class=\"hljs-subst\"\u003e${controller.user.value.name}\u003c/span\u003e'\u003c/span\u003e),\n                    \u003cspan class=\"hljs-comment\"\u003e// Add more user details as needed\u003c/span\u003e\n                  ],\n                );\n              }\n            }),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-run\"\u003eRun\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eInstall Dependencies\u003c/strong\u003e: Ensure that you have all the required packages and dependencies installed. Run the following command:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-bash\"\u003e flutter pub get\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eGenerate Code\u003c/strong\u003e: Since our app uses code generation tools like \u003ccode\u003efreezed\u003c/code\u003e, \u003ccode\u003ejson_serializable\u003c/code\u003e, etc., it's important to generate necessary code files. Run the following command:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-bash\"\u003e flutter pub run build_runner build --delete-conflicting-outputs\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e This command generates code that's needed for serialization, deserialization, and more. The \u003ccode\u003e--delete-conflicting-outputs\u003c/code\u003e flag helps resolve any conflicts that may arise during code generation.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cp\u003e\u003cstrong\u003eRun the App\u003c/strong\u003e: Finally, you can launch the app using the following command:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"lang-bash\"\u003e flutter run\n\u003c/code\u003e\u003c/pre\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr /\u003e\n\u003ch2 id=\"heading-conclusion\"\u003eConclusion\u003c/h2\u003e\n\u003cp\u003eCongratulations! 🥳 You've just embarked on a thrilling journey of implementing clean architecture in Flutter using GetX. Clean architecture can be a bit challenging at first, but once you get the hang of it, it makes your code more maintainable and scalable.\u003c/p\u003e\n\u003cp\u003eSo, go forth and create amazing Flutter apps with clean architecture. Happy coding! 🚀📱✨\u003c/p\u003e\n27:[\"$\",\"div\",null,{\"className\":\"relative\",\"children\":[[\"$\",\"aside\",null,{\"className\":\"hidden xl:block absolute top-0 left-0 h-full\",\"children\":[\"$\",\"nav\",null,{\"className\":\"sticky top-22 w-44\",\"children\":[\"$\",\"$L2c\",null,{\"headings\":[{\"id\":\"heading-what-is-clean-architecture\",\"text\":\"What is Clean Architecture?\",\"level\":2},{\"id\":\"heading-project-setup\",\"text\":\"Project Setup\",\"level\":2},{\"id\":\"heading-packages\",\"text\":\"Packages\",\"level\":3},{\"id\":\"heading-folder-structure\",\"text\":\"Folder Structure\",\"level\":3},{\"id\":\"heading-architecture-and-dependency-rules\",\"text\":\"Architecture and Dependency Rules\",\"level\":3},{\"id\":\"heading-app-configuration\",\"text\":\"App Configuration\",\"level\":2},{\"id\":\"heading-routing\",\"text\":\"Routing\",\"level\":3},{\"id\":\"heading-theme\",\"text\":\"Theme\",\"level\":3},{\"id\":\"heading-utils\",\"text\":\"Utils\",\"level\":3},{\"id\":\"heading-dependency-injection\",\"text\":\"Dependency Injection\",\"level\":3},{\"id\":\"heading-domain-layer\",\"text\":\"Domain Layer\",\"level\":2},{\"id\":\"heading-entities-models\",\"text\":\"Entities (Models)\",\"level\":3},{\"id\":\"heading-repositories-abstract-definitions\",\"text\":\"Repositories (Abstract Definitions)\",\"level\":3},{\"id\":\"heading-data-layer\",\"text\":\"Data Layer\",\"level\":2},{\"id\":\"heading-data-sources\",\"text\":\"Data Sources\",\"level\":3},{\"id\":\"heading-repositories-implementation\",\"text\":\"Repositories (Implementation)\",\"level\":3},{\"id\":\"heading-presentation-layer\",\"text\":\"Presentation Layer\",\"level"])</script>
538<script>self.__next_f.push([1,"\":2},{\"id\":\"heading-controllers\",\"text\":\"Controllers\",\"level\":3},{\"id\":\"heading-screens\",\"text\":\"Screens\",\"level\":3},{\"id\":\"heading-run\",\"text\":\"Run\",\"level\":2},{\"id\":\"heading-conclusion\",\"text\":\"Conclusion\",\"level\":2}]}]}]}],[\"$\",\"div\",null,{\"className\":\"max-w-2xl mx-auto min-w-0 contain-[inline-size]\",\"children\":[\"$\",\"div\",null,{\"className\":\"prose dark:prose-invert prose-pre:bg-foreground prose-pre:text-background prose-pre:border prose-pre:border-border prose-pre:rounded prose-pre:px-5 prose-pre:py-4 dark:prose-pre:bg-muted dark:prose-pre:text-foreground dark:prose-pre:border-border prose-pre:overflow-x-auto max-w-none text-base sm:text-lg [\u0026\u003ediv\u003ep:first-child]:mt-0 [\u0026\u003ediv\u003ep:first-child]:pt-0 min-w-0 wrap-break-word [\u0026_a]:break-all **:max-w-full\",\"children\":[[],[\"$\",\"div\",null,{\"suppressHydrationWarning\":true,\"dangerouslySetInnerHTML\":{\"__html\":\"$2d\"}}],[],\"$L2e\",\"$L2f\",\"$L30\",\"$L31\",\"$L32\"]}]}]]}]\n28:[\"$\",\"div\",null,{\"className\":\"max-w-2xl mx-auto border-t border-border pt-8 space-y-5\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center gap-2\",\"children\":[[\"$\",\"$L22\",\"56744722958ef13879b9507c\",{\"href\":\"/tag/flutter\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"flutter\"]}],[\"$\",\"$L22\",\"56744720958ef13879b947c4\",{\"href\":\"/tag/app-development\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"app-development\"]}],[\"$\",\"$L22\",\"568a9b8ce4c4e23aef243c1f\",{\"href\":\"/tag/mobile-development\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"mobile-development\"]}],[\"$\",\"$L22\",\"573504d39835efadc8742016\",{\"href\":\"/tag/clean-code\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"clean-code\"]}],[\"$\",\"$L22\",\"56744723958ef13879b952d7\",{\"href\":\"/tag/developer\",\"className\":\"inline-flex items-center gap-1.5 rounded-full border border-border px-3 py-1 text-sm text-muted-foreground transition hover:border-primary hover:text-primary\",\"children\":[\"#\",\"developer\"]}]]}],[\"$\",\"div\",null,{\"className\":\"flex items-center gap-2 text-sm text-muted-foreground\",\"title\":\"7,341 views\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-regular fa-chart-simple h-4 w-4\",\"aria-hidden\":true}],[\"$\",\"span\",null,{\"className\":\"font-medium\",\"children\":[\"7.3K\",\" views\"]}]]}]]}]\n33:I[32478,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"CodeHighlighter\"]\n34:I[7714,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"EmbedHandler\"]\n35:I[54960,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e"])</script>
538<script>self.__next_f.push([1,"3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"ImageZoom\"]\n36:I[6671,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"MathJaxHandler\"]\n37:I[91337,[\"590\",\"static/chunks/0dbeb660-9e82e99813552576.js\",\"4554\",\"static/chunks/4554-984b480a9db5f3fe.js\",\"7563\",\"static/chunks/7563-4dfa0994a9b0e37c.js\",\"5001\",\"static/chunks/5001-fae0bebdd93f95e3.js\",\"2423\",\"static/chunks/2423-c8b1388240338d7d.js\",\"9216\",\"static/chunks/9216-9272709ba40c1cfd.js\",\"1545\",\"static/chunks/1545-7abf96433ff6553f.js\",\"3091\",\"static/chunks/3091-bf1537ff6571a1bd.js\",\"652\",\"static/chunks/652-f40dcb480b4e356f.js\",\"730\",\"static/chunks/730-8191af9e9424b148.js\",\"3048\",\"static/chunks/app/%5B...slug%5D/page-ce41bf29cdfb993a.js\"],\"MermaidHandler\"]\n2e:[\"$\",\"$L33\",null,{}]\n2f:[\"$\",\"$L34\",null,{}]\n30:[\"$\",\"$L35\",null,{}]\n31:[\"$\",\"$L36\",null,{\"hasLatex\":false}]\n32:[\"$\",\"$L37\",null,{}]\n38:Tc98,M268.379 39q-6.322 0-10.35-4.108-4.002-4.134-4.002-10.483 0-6.296 4.002-10.43 4-4.135 10.163-4.135 6.028 0 9.817 3.734 3.814 3.735 3.814 9.497v2.8h-24.248v-4.054h17.633q-.187-2.747-2.054-4.508-1.868-1.76-4.962-1.76-3.28-.001-5.388 2.374-2.108 2.373-2.108 6.375 0 4.188 2.348 6.695 2.347 2.481 5.895 2.481 1.68 0 2.934-.453a8.5 8.5 0 0 0 2.321-1.307q1.041-.854 2.268-2.614l4.694 2.96q-1.707 2.748-3.494 4.135a12.7 12.7 0 0 1-3.975 2.081q-2.187.72-5.308.72m-33.496 0q-5.575 0-9.283-4.028-3.681-4.029-3.681-10.484 0-6.43 3.761-10.51 3.76-4.108 9.47-4.108 2.8 0 5.175 1.2 2.4 1.174 3.628 3.148l.026-.08V0h7.016v38.2h-6.722v-4.722l.64.8-.694.133q-1.28 2.108-3.788 3.361Q237.924 39 234.883 39m1.627-6.002q3.308 0 5.495-2.428t2.188-6.135q0-3.762-2.214-6.109-2.214-2.374-5.469-2.374-3.28 0-5.522 2.347-2.214 2.348-2.214 6.163 0 3.76 2.214 6.162 2.214 2.374 5.522 2.374M205.811 39q-6.376 0-10.537-4.161-4.161-4.188-4.161-10.404 0-6.321 4.161-10.456 4.188-4.135 10.537-4.135 6.348 0 10.537 4.161 4.188 4.161 4.188 10.43 0 6.216-4.188 10.404Q212.186 39 205.811 39m0-6.162q3.307 0 5.575-2.374 2.267-2.374 2.267-6.055 0-3.707-2.294-6.056-2.267-2.347-5.548-2.347-3.308 0-5.575 2.374-2.268 2.348-2.268 6.029t2.241 6.055q2.268 2.374 5.602 2.374M162.586 38.2V10.644h6.802v5.015l-.8-.987h.906q1.308-2.187 3.762-3.494 2.48-1.335 5.228-1.334 4.615 0 7.256 2.8 2.667 2.776 2.667 7.603V38.2h-7.069V21.794q0-2.854-1.387-4.401t-4.134-1.547q-2.614 0-4.402 1.974-1.76 1.974-1.76 4.881V38.2zm-30.181 0V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-7.069V21.794q0-2.827-1.387-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.814 2.027-1.814 4.962v15.391zm-14.416.8q-2.882 0-5.015-.64-2.108-.614-3.761-1.84-1.654-1.228-2.668-2.695l-.987-1.467 4.695-3.174.773 1.067a9.1 9.1 0 0 0 1.788 1.814 8.2 8.2 0 0 0 2.32 1.2q1.308.4 3.068.4 2.161 0 3.441-.8 1.308-.8 1.307-2.294 0-1.36-1.147-2.108-1.12-.747-5.068-1.654-5.335-1.253-7.656-3.334-2.32-2.107-2.321-5.362 0-3.76 3.148-6.002 3.148-2.267 7.923-2.267 2.454 0 4.348.507t3.388 1.493q1.493.988 2.454 2.188l.987 1.2-4.482 3.255-.667-.774a7.4 7.4 0 0 0-1.627-1.387 6.4 6.4 0 0 0-2.054-.88 9.5 9.5 0 0 0-2.401-.294q-1.974 0-3.201.8-1.2.774-1.2 1.921 0 1.254 1.12 2.027 1.147.748 5.229 1.681 5.334 1.227 7.629 3.308 2.294 2.055 2.294 5.308 0 4.029-3.228 6.43Q123.217 39 117.989 39M87.61 39q-4.508 0-7.335-2.454-2.828-2.481-2.828-6.482 0-4.188 3.2"])</script>
538<script>self.__next_f.push([1,"54-6.669t8.483-2.48q2.082 0 4.028.426 1.974.427 3.361 1.12v-1.6q0-2.588-1.813-4.082-1.788-1.493-4.615-1.493-1.628 0-2.801.373a8.7 8.7 0 0 0-2.188.987q-.987.614-1.84 1.414l-.854.8-4.081-3.788 1.307-1.173a18.7 18.7 0 0 1 2.934-2.108q1.601-.933 3.601-1.44t4.589-.507q5.868 0 9.229 2.88 3.361 2.882 3.361 8.084V38.2h-6.749v-5.922l1.654 2.187h-1.76q-1.308 2.055-3.655 3.308Q90.545 39 87.611 39m1.814-4.988q3.148 0 5.149-2.187 2-2.215 2-5.309v-.08q-1.12-.693-2.72-1.094a13 13 0 0 0-3.388-.426q-2.828 0-4.562 1.2-1.734 1.173-1.734 3.521 0 2.028 1.468 3.201t3.787 1.174M49 38.2V0h6.962v14.698h.134q1.2-2.187 3.574-3.521 2.375-1.335 5.229-1.334 4.615 0 7.255 2.828 2.668 2.801 2.668 7.522V38.2h-7.07V21.794q0-2.827-1.386-4.402-1.387-1.573-4.055-1.573-2.587 0-4.428 2.027-1.815 2.027-1.814 4.962v15.391z1f:[\"$\",\"footer\",null,{\"className\":\"px-4 pt-8 pb-20 sm:px-8 text-muted-foreground\",\"aria-label\":\"Footer\",\"children\":[\"$\",\"div\",null,{\"className\":\"w-full max-w-4xl mx-auto\",\"children\":[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center gap-4 text-center text-sm\",\"children\":[false,[\"$\",\"p\",null,{\"className\":\"font-medium\",\"children\":[[\"$\",\"time\",null,{\"dateTime\":\"2026\",\"children\":[[\"$\",\"span\",null,{\"className\":\"font-sans\",\"children\":\"©\"}],\" \",2026]}],\" \",\"Aditya's Blog\"]}],[\"$\",\"nav\",null,{\"aria-label\":\"Footer navigation\",\"children\":[\"$\",\"ul\",null,{\"className\":\"flex flex-wrap justify-center gap-4 text-sm tracking-wide\",\"children\":[[\"$\",\"li\",\"/archive\",{\"children\":[\"$\",\"$L22\",null,{\"href\":\"/archive\",\"className\":\"transition hover:text-foreground\",\"target\":\"$undefined\",\"rel\":\"$undefined\",\"children\":\"Archive\"}]}],[\"$\",\"li\",\"https://hashnode.com/privacy-policy\",{\"children\":[\"$\",\"$L22\",null,{\"href\":\"https://hashnode.com/privacy-policy\",\"className\":\"transition hover:text-foreground\",\"target\":\"_blank\",\"rel\":\"noopener\",\"children\":\"Privacy\"}]}],[\"$\",\"li\",\"https://hashnode.com/terms\",{\"children\":[\"$\",\"$L22\",null,{\"href\":\"https://hashnode.com/terms\",\"className\":\"transition hover:text-foreground\",\"target\":\"_blank\",\"rel\":\"noopener\",\"children\":\"Terms\"}]}]]}]}],[\"$\",\"div\",null,{\"className\":\"pt-8 flex justify-center gap-6\",\"children\":[[\"$\",\"$L22\",null,{\"href\":\"/sitemap.xml\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"flex items-center gap-2 text-sm transition hover:text-foreground\",\"title\":\"Sitemap\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-regular fa-sitemap\"}],[\"$\",\"span\",null,{\"children\":\"Sitemap\"}]]}],[\"$\",\"$L22\",null,{\"href\":\"/rss.xml\",\"target\":\"_blank\",\"rel\":\"noopener\",\"className\":\"flex items-center gap-2 text-sm transition hover:text-foreground\",\"title\":\"RSS Feed\",\"children\":[[\"$\",\"i\",null,{\"className\":\"fa-regular fa-rss\"}],[\"$\",\"span\",null,{\"children\":\"RSS\"}]]}]]}],[\"$\",\"div\",null,{\"className\":\"pt-10 text-center\",\"children\":[\"$\",\"$L22\",null,{\"href\":\"https://hashnode.com/?utm_source=https%3A%2F%2Fblog.adityasharma.co\u0026utm_medium=referral\u0026utm_campaign=blog_footer_logo\u0026utm_content=logo\",\"target\":\"_blank\",\"rel\":\"noopener\",\"aria-label\":\"Visit Hashnode (opens in a new tab)\",\"children\":[\"$\",\"svg\",null,{\"fill\":\"none\",\"viewBox\":\"0 0 282 39\",\"aria-hidden\":\"true\",\"className\":\"h-6 w-auto min-w-[9.5rem] text-muted-foreground transition hover:text-foreground\",\"children\":[[\"$\",\"path\",null,{\"fill\":\"currentColor\",\"d\":\"M2.68 13.032c-3.573 3.505-3.573 9.363 0 12.936L13.032 36.32c3.505 3.573 9.363 3.573 12.936 0L36.32 25.968c3.573-3.573 3.573-9.431 0-12.936L25.968 2.68c-3.573-3.573-9.431-3.573-12.936 0zm12.211 1.935c2.507-2.521 6.582-2.544 9.104-.038s2.544 6.582.038 9.104-6.582 2.544-9.104.038-2.544-6.582-.038-9.104\"}],[\"$\",\"path\",null,{\"fill\":\"currentColor\",\"d\":\"$38\"}]]}],\"className\":\"inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors focus-visible:outline-hidden focu
538s-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [\u0026_svg]:pointer-events-none [\u0026_svg]:size-4 [\u0026_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-10 rounded-md text-xl px-5 py-4\",\"ref\":null}]}]]}]}]}]\n"])</script>
538</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.