<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Maximize Your Flutter and Dart Fullstack Skills with These Essential Tips]]></title><description><![CDATA[Learn how to use Flutter, Dart, and Firebase to create powerful full-stack applications. Get expert tips, tricks, and techniques from our comprehensive guide.]]></description><link>https://saileshdahal.com.np</link><generator>RSS for Node</generator><lastBuildDate>Sat, 09 May 2026 13:57:08 GMT</lastBuildDate><atom:link href="https://saileshdahal.com.np/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Few takeaways on getting amazed with AI]]></title><description><![CDATA[On 24th Jan 2025, I attended an insightful session organized by Department of Artificial Intelligence at Kathmandu University (KU) . This session was by Dr. Saumendra Mohanty and was really insightful providing a different perspective on AI and it’s ...]]></description><link>https://saileshdahal.com.np/few-takeaways-on-getting-amazed-with-ai</link><guid isPermaLink="true">https://saileshdahal.com.np/few-takeaways-on-getting-amazed-with-ai</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[Machine Learning]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Sat, 25 Jan 2025 03:19:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737775033514/bc210a47-1ba9-448f-9ba5-3755a22a5a51.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On 24th Jan 2025, I attended an insightful session organized by Department of Artificial Intelligence at <a target="_blank" href="http://ku.edu.np">Kathmandu University (KU)</a> . This session was by <a target="_blank" href="https://www.lloydbusinessschool.edu.in/faculty/dr-saumendra-mohanty.html">Dr. Saumendra Mohanty</a> and was really insightful providing a different perspective on AI and it’s applications.</p>
<p>Here are few takeaways from the session.</p>
<p><a target="_blank" href="https://orangedatamining.com/">Orange Data Mining</a></p>
<p>This was my first time hearing about this tool and I was like “man this is good”. This is a no code tool that you can do a lot with drag and drop interface.</p>
<p>I know you may have a ‘eek’ by hearing ‘no code’ but this can be a huge time saver if you are trying to find the best model for your data set.</p>
<p>The instructor demonstrated how you can use multiple models/strategies in the same dataset to find the optimal one for your data set. I think the demo was around 15-mins long, but we tried models like Linear Regression, Random Forest, SVM, Multi-Layer Perceptron, KNN etc, and for that dataset, the Random Forest was more accurate.</p>
<p>We generate the confusion matrix for all these models, tried with multiple datasets, analyzed the accuracy without any code and within 15 mins.  It was surprisingly good. Game changer stuff.</p>
<p><a target="_blank" href="https://medium.com/@kirudang/job-resume-matching-part-1-2-obtaining-similarity-score-using-doc2vec-a6d07fe3b355">Cosine Similarity in Resume</a></p>
<p>He explained that how companies use cosine similarity to filter out potential spams resume, and how we can think through it and optimize our resume. We had a practical demonstration where we calculated how likely a resume is going to be passed to a second round for a given job description.</p>
<p>The working behind this is, the job description is converted into a vector ( i need to read more on how this is done, If you are familiar on this, feel free to comment) and similarly the resume is also converted into a vector. Once this is done, calculate the cosine of the angle between these vector.  The angles being closer to 0 means that the provided resume and the job description has a greater match.</p>
<p>Before you ask, yes we will also be taking into account all the irrelavent details in the resume, that is not the part of the job description.</p>
<p><strong>Sentiment Analysis</strong></p>
<p>We also got to know how sentiment analysis are being used in industries to optimize sales. We got to know about the Filpkart’s success story of a failing product turned to a top selling one by analyzing the reviews, followed by sentiment analysis on it, and then changing on the product. We briefly discussed other areas where sentiment analysis is being used like, Customer Feedback analysis, Product success prediction based on customer data, stock market trends (we did a on google data direct from yahoo finance with orange data mining) e.t.c.</p>
<p><strong>Localized AI</strong></p>
<p>We also discussed about current trends on LLM and what the next focus is going to be. Professor explained that the next big thing on LLMs is going to be on the regional languages, how the local government can help subsidize this, and it’s challenges to improve accessibility in the rural areas so that AI is accessible.</p>
<p><strong>Neural Networks and Reinforcement Learning</strong></p>
<p>We also briefly talked about the Neural Networks and machine learning method like Supervised Learning and Reinforcement Learning and its tradeoffs. He explained that reinforcement learning may not be the best for self driving car due to the accidents that may happen for the model to learn.</p>
<hr />
<p>It was a really fruitful session and I got to learn a lot. This session left me thinking about how rapidly things are evolving and how we are surrounded with AI.</p>
<p>What I think is, although we are surrounded by AI and most of the repetitive tasks are being replaced by AI, we will still have edge in sophisticated problem solving.</p>
<p>If you’re curious about Orange, cosine analysis, or just AI in general, let’s chat in the comments. Always eager to discuss and learn from others in this space!</p>
<p>PS, Yes I generate this thumbnail with AI</p>
<hr />
<p>That's it for today. If you have any questions on the topic you want me to write about, please feel free to write in comments. I will try to cover as much topics as I can.</p>
<p>Thank you for joining me on this journey, and happy coding! 💻👨💻</p>
]]></content:encoded></item><item><title><![CDATA[Flutter FAQ: codegen and bundle size]]></title><description><![CDATA[After 2 years of not writing and ditching my readers, I am trying to get back to writing again. The past couple of years have been really busy with freelancing on Upwork, where I delivered multiple apps for clients that are generating steady MRR.
I’m...]]></description><link>https://saileshdahal.com.np/flutter-faq-codegen-and-bundle-size</link><guid isPermaLink="true">https://saileshdahal.com.np/flutter-faq-codegen-and-bundle-size</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Firebase]]></category><category><![CDATA[#dart-for-beginners]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Fri, 24 Jan 2025 08:24:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737705811706/b6a2a0e2-106f-4320-aa76-e712b632b61b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After 2 years of not writing and ditching my readers, I am trying to get back to writing again. The past couple of years have been really busy with freelancing on Upwork, where I delivered multiple apps for clients that are generating steady MRR.</p>
<p>I’m starting with some topics that might sound trivial but can be confusing to many Flutter developers. These are the kind of questions that often get overlooked but are important to address for smoother development.</p>
<p>Let's try to answer these questions that you may have come across while using flutter and it's build_runner.</p>
<ol>
<li><p>Is codegen secretly bloating your app size? <strong>TLDR; It's not!</strong></p>
</li>
<li><p>Will static metaprogramming make tools like build_runner obsolete?</p>
</li>
<li><p>How are you handling generated files? Do you commit them to VCS?</p>
</li>
<li><p>Is build_runner getting slower as the project grows? Can you fix it? Yes? How?</p>
</li>
</ol>
<hr />
<blockquote>
<p><strong>What determines the flutter app size, and how to make it smaller?</strong></p>
</blockquote>
<p>Flutter provides tools to analyze the size breakdown of your app’s final build, helping you optimize the app size and make it smaller.</p>
<p>There is a special flag that many might not know about, which allows you to view the final contents of the release build. With this command, the Dart compiler will monitor package usage and code utilization within the app, generating a JSON report that details the build contents.</p>
<pre><code class="lang-bash">flutter build ipa --analyze-size
</code></pre>
<p>The <code>--analyze-size</code> flag lists all the components of the bundle, including the frameworks used, their sizes, and the total asset size of the final bundle. This is how it will look like.</p>
<p>This command also generates a JSON file that we can use to view a TreeMap with dart DevTools.</p>
<pre><code class="lang-bash">dart devtools --appSizeBase=ios-code-size-analysis_02.json
</code></pre>
<p>Once you run the command, you can see the TreeMap with all the details interactively. You can also click on one node and explore further.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737706498123/e785d2c3-f9e8-44f4-8c1e-5697da2d8644.png" alt="TreeMap view of the size breakdown" class="image--center mx-auto" /></p>
<p>Now it's clear that using codegen won't significantly increase the app size (it might only add a few kilobytes), so we don't need to worry about it bloating the app.</p>
<hr />
<blockquote>
<p><strong>What occurs if I list multiple packages in my pubspec.yaml file but don't actually use them?</strong></p>
</blockquote>
<p>When you have specified packages/plugins to use in your flutter project, the Dart compiler will treeshake all the unused dart code optimizing the app's size. The tricky part is with the plugin (ones containing native dependencies).</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/Y9WifT8aN6o">https://youtu.be/Y9WifT8aN6o</a></div>
<p> </p>
<p>If your pubspec.yaml contains unused plugins, the Dart compiler will treeshake unused Dart code, but native dependencies will remain, increasing the app size.</p>
<p>To prevent this, remove unused packages from pubspec.yaml and regularly check for unused dependencies.</p>
<p>You can use available tools to scan for your unused dependencies and assets from your codebase.</p>
<ol>
<li><p><a target="_blank" href="https://pub.dev/packages/dependency_validator">dependency_validator</a> reports any missing, under-promoted, over-promoted, and unused dependencies. Any package that either provides an executable or a builder that will be auto-applied via the <a target="_blank" href="https://github.com/dart-lang/build">dart build system</a> will be considered used even if it isn't imported.</p>
</li>
<li><p><a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=esentis.flutter-find-unused-assets-and-dart-files">Flutter: Find Unused Dart Files &amp; Assets</a> is a VS code extension which allows you to find unused assets, dart files &amp; dependencies in your project.</p>
</li>
</ol>
<hr />
<blockquote>
<p>If I list an asset in my pubspec.yaml but don't use it within the app, will it still get bundled into the application?</p>
</blockquote>
<p>Yes, If you list an asset in your pubspec.yaml but don't use it within your Flutter app, it will still be bundled into the application. This applies to most assets like images, sounds, or general files. The asset bundling process in Flutter does not remove unused assets based on their use in the code. That's why all the assets listed in pubspec.yaml are bundled into the app, regardless of their use, ultimately bloating your app.</p>
<blockquote>
<p>What about that message about fonts being treeshaken while I build for android?</p>
</blockquote>
<p>The message about fonts being "treeshaken" refers to Flutter's font tree-shaking feature. This feature is different from how assets like images or other files are handled.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737706640830/aa27d37d-84ee-44ee-bd42-4b6af6a5b95d.png" alt="Flutter fonts being treeshaken" class="image--center mx-auto" /></p>
<p>When you build your Flutter app in release mode, Flutter includes only the font glyphs (characters) that are actually used in your app.</p>
<p>Flutter analyzes your app's text usage and optimizes font files by including only the required characters. This is why you see the "fonts being treeshaken" message during the build.</p>
<blockquote>
<p>Will static metaprogramming make tools like build_runner obsolete?</p>
</blockquote>
<p>The <a target="_blank" href="https://dart.dev/language/macros/">Dart macro system</a> has introduced support for static meta-programming to the Dart language.</p>
<p>This means that, when the feature is live, we could use annotations and the dart compiler will JIT compile the annotations to code on the fly. Most of the notable packages are planning to adapt this approach to mitigate the short coming of the build_runner (time consuming).</p>
<p>Here's <a class="user-mention" href="https://hashnode.com/@remi_rousselet">Remi Rousselet</a> talking about rewriting Freezed with macros.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/AsF_liobO-c">https://youtu.be/AsF_liobO-c</a></div>
<p> </p>
<blockquote>
<p><strong>How are you handling generated files? Do you commit them to VCS?</strong></p>
</blockquote>
<p>Using tools like build_runner generates a significant number of files, which can be cumbersome to manage, potentially leading to issues like merge conflicts if not handled correctly.</p>
<p>Committing these files to a version control system would be a matter of preference and is somewhat opinionated.</p>
<p><em>Personally, I do not check these files into VCS when working on collaborative projects to avoid merge conflicts.</em></p>
<p>If you choose not to check these files into your VCS, you may need to add an additional build step in your CI process to generate these files, which might increase your CI build time.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stackoverflow.com/questions/893913/should-i-store-generated-code-in-source-control">https://stackoverflow.com/questions/893913/should-i-store-generated-code-in-source-control</a></div>
<p> </p>
<blockquote>
<p>Is build_runner getting slower as the project grows? Can you fix it? Yes? How?</p>
</blockquote>
<p>As your project expands with more files and additional build_runner generators, the overhead increases, leading to longer times for code generation.</p>
<p>This can be optimized by creating a build.yaml file and explicitly telling the generator which files to check for code generation. This can drastically decrease the codegen time.</p>
<p>Here's an example of such build.yaml file.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">targets:</span>
  <span class="hljs-string">$default:</span>
    <span class="hljs-attr">builders:</span>
      <span class="hljs-attr">freezed:freezed:</span>
        <span class="hljs-attr">generate_for:</span>
          <span class="hljs-attr">exclude:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
          <span class="hljs-attr">include:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*data/**/*.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*models/**/*.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*domain/**/*.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/core/error/failures/**/*_failure.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/features/**/*bloc/*_bloc.dart</span>

      <span class="hljs-attr">injectable_generator:injectable_builder:</span>
        <span class="hljs-attr">generate_for:</span>
          <span class="hljs-attr">exclude:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
          <span class="hljs-attr">include:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*_{bloc,data_source,interceptor,http_client,repository,service,plugin,viewmodel}.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*usecases/**/*.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/core/di/**/*.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/domain/**/*_{factory,api,filters,impl}.dart</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/network/**/*.dart</span>

      <span class="hljs-attr">json_serializable:json_serializable:</span>
        <span class="hljs-attr">generate_for:</span>
          <span class="hljs-attr">exclude:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
          <span class="hljs-attr">include:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*{data,models,domain}/**/*.dart</span>
        <span class="hljs-attr">options:</span>
          <span class="hljs-attr">create_factory:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">create_to_json:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">field_rename:</span> <span class="hljs-string">snake</span>
          <span class="hljs-attr">explicit_to_json:</span> <span class="hljs-literal">true</span>

      <span class="hljs-attr">retrofit_generator:</span>
        <span class="hljs-attr">generate_for:</span>
          <span class="hljs-attr">exclude:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
          <span class="hljs-attr">include:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*{data_source,http_client}.dart</span>

      <span class="hljs-attr">stacked_generator:</span>
        <span class="hljs-attr">stackedBottomsheetGenerator:</span>
          <span class="hljs-attr">generate_for:</span>
            <span class="hljs-attr">exclude:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
            <span class="hljs-attr">include:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*_bottom_sheet.dart</span>

        <span class="hljs-attr">stackedDialogGenerator:</span>
          <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span>

        <span class="hljs-attr">stackedLocatorGenerator:</span>
          <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span>

        <span class="hljs-attr">stackedFormGenerator:</span>
          <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">generate_for:</span>
            <span class="hljs-attr">exclude:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
            <span class="hljs-attr">include:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*_view.dart</span>

        <span class="hljs-attr">stackedLoggerGenerator:</span>
          <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">generate_for:</span>
            <span class="hljs-attr">exclude:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
            <span class="hljs-attr">include:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*_view.dart</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">lib/core/setup/config/logger.dart</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">lib/core/setup/circles_app.dart</span>

        <span class="hljs-attr">stackedRouterGenerator:</span>
          <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">generate_for:</span>
            <span class="hljs-attr">exclude:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">test/**.dart</span>
            <span class="hljs-attr">include:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">lib/**/*_{view,navigator,screen}.dart</span>
              <span class="hljs-bullet">-</span> <span class="hljs-string">lib/core/setup/config/routes.dart</span>
</code></pre>
<p>Although this reduces the time it takes to run codegen, using this method without proper code architecture will be a nightmare, causing even more problem in the longer run.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://johnthiriet.com/optimizing-your-flutter-build-runner-for-faster-development/">https://johnthiriet.com/optimizing-your-flutter-build-runner-for-faster-development/</a></div>
<p> </p>
<hr />
<p>That's it for today. If you have any questions on the topic you want me to write about, please feel free to write in comments. I will try to cover as much topics as I can.</p>
<p>Thank you for joining me on this journey, and happy coding! 💻👨💻</p>
]]></content:encoded></item><item><title><![CDATA[🤳 Effortless Sharing: From external apps to your Flutter app in no time]]></title><description><![CDATA[Sharing things like files, pictures, videos, and texts from external apps can be difficult, especially when done without any external packages.
Facilitating content sharing via external apps boosts user engagement, improves the user experience, and h...]]></description><link>https://saileshdahal.com.np/sharing-media-from-external-to-flutter-app</link><guid isPermaLink="true">https://saileshdahal.com.np/sharing-media-from-external-to-flutter-app</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Tue, 14 Feb 2023 03:31:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1676265778828/26014b9d-cf49-48ba-b601-03022f207aa4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sharing things like files, pictures, videos, and texts from external apps can be difficult, especially when done without any external packages.</p>
<p>Facilitating content sharing via external apps boosts user engagement, improves the user experience, and helps to spread the word about our app.</p>
<p>In this article, we'll be taking advantage of the awesome <a target="_blank" href="https://pub.dev/packages/share_handler">share_handler</a> package to make sharing from external apps to our Flutter app really easy!</p>
<p>We will be using a multi-flavored flutter app as our starting point for this tutorial. You can <a target="_blank" href="https://github.com/saileshbro/sharing_into_flutter_app.git">find the repo <strong>here</strong></a></p>
<blockquote>
<p><strong>🥳 Sounds great, right?</strong></p>
</blockquote>
<p>Let's get started by adding <code>share_handler</code> as a dependency in <code>pubspec.yaml</code></p>
<h2 id="heading-android-setup">Android setup 🤖</h2>
<p>We will start with the easy setup. In our <code>android/app/src/main/AndroidManifest.xml</code> file, we will add the intent filters and metadata for the media types we want to support.</p>
<p>For text-only support</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.SEND"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.DEFAULT"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"text/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
</code></pre>
<p>For image-only support</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.SEND"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.DEFAULT"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"image/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
</code></pre>
<p>For video-only support</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.SEND"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.DEFAULT"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"video/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
</code></pre>
<p>If you want to support any type of files</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.SEND"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.DEFAULT"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"**/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
</code></pre>
<blockquote>
<p><strong>💡 Tip:</strong> You can add <code>SHARE_MULTIPLE</code> action, if you want to support more than one media sharing.</p>
</blockquote>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.SEND_MULTIPLE"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.DEFAULT"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"image/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.SEND_MULTIPLE"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.DEFAULT"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"video/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.SEND_MULTIPLE"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.DEFAULT"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"*/*"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
</code></pre>
<blockquote>
<p><strong>💡Tip:</strong> You can change the <code>android:mimeType</code> accordingly for the file types.</p>
</blockquote>
<p>Also, if you want to prevent the incoming shares from opening a new activity each time, you can prevent this by changing <code>android:launchMode</code> to <code>singleTask</code></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">activity</span>
<span class="hljs-attr">...</span>
<span class="hljs-attr">android:lunchMode</span>=<span class="hljs-string">"singleTask"</span>
<span class="hljs-attr">...</span>
&gt;</span>
...
<span class="hljs-tag">&lt;/<span class="hljs-name">activity</span>&gt;</span>
</code></pre>
<p>For adding our app in share suggestions and shortcuts, we can create a new file <code>share_targets.xml</code> in <code>android/app/src/main/res/xml/share_targets.xml</code></p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">shortcuts</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">share-target</span>
    <span class="hljs-attr">android:targetClass</span>=<span class="hljs-string">"@{'np.com.saileshdahal.sharing'+@strings/app_id_suffix+'.dynamic_share_target'}"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"*/*"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">category</span>
      <span class="hljs-attr">android:name</span>=<span class="hljs-string">"@{'np.com.saileshdahal.sharing'+@strings/app_id_suffix+'.dynamic_share_target'}"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">share-target</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">shortcuts</span>&gt;</span>
</code></pre>
<blockquote>
<p><strong>💡 Tip:</strong> Make sure you have <code>app_id_suffix</code> resource string defined for each flavor.</p>
</blockquote>
<pre><code class="lang-apache"><span class="hljs-attribute">resValue</span> <span class="hljs-string">"string"</span>, <span class="hljs-string">"app_id_suffix"</span>, applicationIdSuffix
</code></pre>
<p>If you are <strong>not using flavors</strong>, you can define this in the following format.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="utf-8"?&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">shortcuts</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">share-target</span> <span class="hljs-attr">android:targetClass</span>=<span class="hljs-string">"{your.package.identifier}.MainActivity"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">data</span> <span class="hljs-attr">android:mimeType</span>=<span class="hljs-string">"*/*"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"{your.package.identifier}.dynamic_share_target"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">share-target</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">shortcuts</span>&gt;</span>
</code></pre>
<p>Now, we will add a metadata field in the <code>AndroidManifest.xml</code> file.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">meta-data</span>
    <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.service.chooser.chooser_target_service"</span>
    <span class="hljs-attr">android:value</span>=<span class="hljs-string">"androidx.sharetarget.ChooserTargetServiceCompat"</span> /&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">meta-data</span>
    <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.app.shortcuts"</span>
    <span class="hljs-attr">android:resource</span>=<span class="hljs-string">"@xml/share_targets"</span> /&gt;</span>
</code></pre>
<p>Once done, if you try to share something, you should see the app icon on the share page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676289713546/0efdd6d6-d920-41e8-91a9-457f328656b3.png" alt="App showing in share intent" class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-ios-setup">iOS setup 📱</h2>
<h3 id="heading-update-runner-infoplist">Update Runner <code>Info.plist</code></h3>
<p>For iOS, let's get started by editing our <code>Info.plist</code> file. We will be registering a deep link that will be launched by Share Extension.</p>
<p>For registering the deep link, add the following in your <code>Info.plist</code></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleURLTypes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleTypeRole<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>Editor<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleURLSchemes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
</code></pre>
<p>If you already have some entries for <code>CFBundleURLTypes</code>, you can add a new entry like this.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleURLTypes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
    ...
    <span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleTypeRole<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>Editor<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleURLSchemes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
    ...
<span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
</code></pre>
<blockquote>
<p><strong>💡 Tip:</strong> Make sure you have a user-defined variable <code>PRODUCT_BUNDLE_IDENTIFIER</code> for your iOS project.</p>
</blockquote>
<p>Make sure you have an entry for <code>NSPhotoLibraryUsageDescription</code> if you are planning to share images.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSPhotoLibraryUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>Photos can be shared to and used in this app<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
<p>If you are planning to add <code>AirDrop</code> support, add the following entry in <code>Info.plist</code>.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>LSSupportsOpeningDocumentsInPlace<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>No<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleDocumentTypes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleTypeName<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>ShareHandler<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>LSHandlerRank<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>Alternate<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>LSItemContentTypes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>public.file-url<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>public.image<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>public.text<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>public.movie<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>public.url<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>public.data<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
</code></pre>
<blockquote>
<p>💡 You can modify <code>LSItemContentTypes</code> array based on your need.</p>
</blockquote>
<h3 id="heading-share-extension-setup">Share Extension setup</h3>
<p>Now for this part, we will create a share extension from Xcode.</p>
<ul>
<li>Open <code>ios</code> folder of your flutter project in Xcode.</li>
</ul>
<pre><code class="lang-bash">open ios/Runner.xcworkspace
</code></pre>
<ul>
<li>Go to <code>File</code> -&gt; <code>New</code> -&gt; <code>Target</code></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676289784911/315f2d44-700d-4c8f-b816-348a5bc99e74.png" alt="Image showing how to create a new target" class="image--center mx-auto" /></p>
<ul>
<li>Search for <code>Share Extension</code> and hit <code>Next</code></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676289798585/fb9216af-8b17-4b2b-85f2-2aa3e53028e2.png" alt="Image showing how to select a Share Extension" class="image--center mx-auto" /></p>
<p>Now create the extension with <code>ShareExtension</code> name, and hit save. This will add a new target for your project.</p>
<blockquote>
<p><strong>🚨 Note:</strong> Make sure the name is <code>ShareExtension</code></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676289845846/c3d57abe-9d1a-4180-b745-ad8eaa6924a5.png" alt="Create a new Share Extension Prompt" class="image--center mx-auto" /></p>
<blockquote>
<p><strong>🚨 IMPORTANT:</strong> Make sure the minimum deployment version of both targets is the same.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676289961422/192e05e0-0731-4b0a-b919-0af9063176f2.png" alt="Setting min deployment version to 14" class="image--center mx-auto" /></p>
<p>For <code>Target</code> -&gt; <code>Runner</code>, set minimum deployment version to <code>14.0</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676289979648/2af298bf-0818-439f-ab10-bfd83df83f0d.png" alt="Setting min deployment version to 14" class="image--center mx-auto" /></p>
<p>Similarly for <code>Target</code>-&gt; <code>ShareExtension</code>, set minimum deployment version to <code>14.0</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676289992228/c8a9c274-7260-42e2-b569-031d02cb8255.png" alt="Setting min deployment version to 14" class="image--center mx-auto" /></p>
<p>Once this is done, open <code>ios/ShareExtension/ShareViewController.swift</code> and replace everything with following</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> share_handler_ios_models

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShareViewController</span>: <span class="hljs-title">ShareHandlerIosViewController</span> </span>{}
</code></pre>
<blockquote>
<p><strong>🚨 Note:</strong> Make sure to run <code>flutter pub get</code> and <code>pod install --repo-update</code></p>
</blockquote>
<p>If you have multiple flavors set up then we will also have to create multiple flavors for our share extension. We will create 3 different schemes for share extension too.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290018979/59d15593-eef5-433e-82ab-b741d3f14eb4.png" alt="Manage scheme option" class="image--center mx-auto" /></p>
<p>Go to <code>Manage Scheme</code>, and then we will duplicate and create 3 different schemes for <code>ShareExtension</code> as <code>ShareExtension-production</code>, <code>ShareExtension-development</code> and <code>ShareExtension-staging</code>.</p>
<p>Rename <code>ShareExtension</code> to <code>ShareExtension-production</code> that will handle one scheme, and then for staging, we can duplicate <code>ShareExtension-production</code> and rename it to <code>ShareExtension-staging</code> and assign the correct configuration.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290061564/62c12ca3-be04-4ddd-8d4c-ed45d3254211.jpeg" alt="Create a new Share Extension scheme for staging" class="image--center mx-auto" /></p>
<p>Similarly, we will create one scheme for <code>ShareExtension-development</code> as well.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290073981/a45b3dde-0a31-4ebc-9b34-bc71bb3277a5.png" alt="All final schemes" class="image--center mx-auto" /></p>
<blockquote>
<p>🚨 <strong>Note: You don't have to duplicate the schemes if you are not using flavors</strong>.</p>
</blockquote>
<h3 id="heading-share-extension-infoplist-update">Share Extension <code>Info.plist</code> update</h3>
<p>Once this is done, we will make some changes to <code>ios/ShareExtension/Info.plist</code></p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">plist</span> <span class="hljs-meta-keyword">PUBLIC</span> <span class="hljs-meta-string">"-//Apple//DTD PLIST 1.0//EN"</span> <span class="hljs-meta-string">"http://www.apple.com/DTDs/PropertyList-1.0.dtd"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">plist</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleVersion<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>$(FLUTTER_BUILD_NUMBER)<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtension<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionAttributes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>IntentsSupported<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>INSendMessageIntent<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationRule<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationSupportsWebURLWithMaxCount<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">integer</span>&gt;</span>999<span class="hljs-tag">&lt;/<span class="hljs-name">integer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationSupportsWebPageWithMaxCount<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">integer</span>&gt;</span>999<span class="hljs-tag">&lt;/<span class="hljs-name">integer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationSupportsMovieWithMaxCount<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">integer</span>&gt;</span>999<span class="hljs-tag">&lt;/<span class="hljs-name">integer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationSupportsImageWithMaxCount<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">integer</span>&gt;</span>999<span class="hljs-tag">&lt;/<span class="hljs-name">integer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationSupportsFileWithMaxCount<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">integer</span>&gt;</span>999<span class="hljs-tag">&lt;/<span class="hljs-name">integer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationSupportsAttachmentsWithMaxCount<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">integer</span>&gt;</span>999<span class="hljs-tag">&lt;/<span class="hljs-name">integer</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionActivationSupportsText<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">true</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>PHSupportedMediaTypes<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>Video<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>Image<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionMainStoryboard<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>MainInterface<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSExtensionPointIdentifier<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>com.apple.share-services<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">plist</span>&gt;</span>
</code></pre>
<p>Here you can make changes to the <code>NSExtensionActivationRule</code> field based on your requirements.</p>
<blockquote>
<p>Note: Here we have set the maximum file-sharing count to 999, but you can disable or enable them based on your requirements.</p>
</blockquote>
<p>Once this is done, make sure we have different bundle identifiers for each flavor and configuration for <code>Share Extension</code>. If you click <code>Target</code> -&gt; <code>Share Extension</code> -&gt; <code>Singing &amp; Capabilities</code> -&gt; <code>All</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290130000/678a49d8-2cef-4d4d-9218-fb118a2f387b.png" alt="All bundle ids for share extension" class="image--center mx-auto" /></p>
<blockquote>
<p><strong>🚨 Note:</strong> Make sure the <code>ShareExtension</code> bundle identifier for each config has a <code>.ShareExtension</code> prefix compared to the <code>Runner</code>.</p>
</blockquote>
<p>Once done, we need to assign both targets with a group identifier. For both <code>Runner</code> and <code>ShareExtension</code> target, go to <code>Signing &amp; Capabilities</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290144337/b32f39ad-7e5b-4e46-aa55-97644a3e2b34.png" alt="Show how to add app group" class="image--center mx-auto" /></p>
<p>Now, for each flavor, we will add an app group.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290163014/d90f88b6-7357-42f5-b16b-0a89a81ea540.png" alt="Show how to add app group" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290176925/0b4f0856-0a10-43f2-aeae-b851091f53fa.png" alt="Show how to add app group" class="image--center mx-auto" /></p>
<blockquote>
<p>🚨 Note: Make sure to add the group id as <code>group.&lt;bundle identifier&gt;</code> for each flavor.</p>
</blockquote>
<p>For <code>Debug-production</code>, <code>Release-production</code>, and <code>Profile-production</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290196245/04fb9de6-227c-4a9a-bf46-038303eddf60.png" alt="add new group id for production" class="image--center mx-auto" /></p>
<blockquote>
<p>💡 Tip: For matching group, you can select the check box</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290267867/dac703ea-93d6-421a-8291-9545ce4dbef3.png" alt="option showing the group id" class="image--center mx-auto" /></p>
<p>For <code>Debug-development</code>, <code>Release-development</code>, and <code>Profile-development</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290276924/2b178b2b-1bb3-42f4-aab3-d09fbb44bd25.png" alt="add new group id for development" class="image--center mx-auto" /></p>
<p>Similarly for <code>Debug-staging</code>, <code>Release-staging</code>, and <code>Profile-staging</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290291524/0f017a91-0600-46ec-8d4e-0eedb61cb93b.png" alt="add new group id for staging" class="image--center mx-auto" /></p>
<p>If you click on <code>Target</code> -&gt; <code>Runner</code> -&gt; <code>Signing &amp; Capabilities</code> -&gt; <code>All</code>, you should see the following.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290303653/04773b29-42fe-4a3a-8361-de3adb195cce.png" alt="All app groups linked" class="image--center mx-auto" /></p>
<p>This will create <code>entitlements</code> files for each configuration.</p>
<p>Similarly for <code>ShareExtension</code> we can quickly create <code>.entitlements</code> files. In <code>ios/ShareExtension</code> we will create an entitlements file for each config and each flavor. We will create 9 entitlements files for each flavor (<code>production</code>, <code>development</code>, and <code>staging</code>), and <code>Debug</code>, <code>Release</code>, and <code>Profile</code> config.</p>
<p>For <code>Debug</code>, <code>Release</code>, and <code>Profile</code> configuration for <code>development</code> flavor, we will create three files <code>ShareExtensionRelease-development.entitlements</code>, <code>ShareExtensionDebug-development.entitlements</code>, and <code>ShareExtensionProfile-development.entitlements</code>, with the following contents.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">plist</span> <span class="hljs-meta-keyword">PUBLIC</span> <span class="hljs-meta-string">"-//Apple//DTD PLIST 1.0//EN"</span> <span class="hljs-meta-string">"http://www.apple.com/DTDs/PropertyList-1.0.dtd"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">plist</span> <span class="hljs-attr">version</span>=<span class="hljs-string">"1.0"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">dict</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>com.apple.security.application-groups<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">array</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>group.np.com.saileshdahal.sharing.dev<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">array</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dict</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">plist</span>&gt;</span>
</code></pre>
<p>Similarly, for each configuration of <code>production</code> and <code>staging</code> flavor, we will create three entitlements files.</p>
<p>By the end, we will have the following files</p>
<pre><code class="lang-apache"><span class="hljs-attribute">ShareExtensionDebug</span>-development.entitlements
<span class="hljs-attribute">ShareExtensionDebug</span>-production.entitlements
<span class="hljs-attribute">ShareExtensionDebug</span>-staging.entitlements
<span class="hljs-attribute">ShareExtensionProfile</span>-development.entitlements
<span class="hljs-attribute">ShareExtensionProfile</span>-production.entitlements
<span class="hljs-attribute">ShareExtensionProfile</span>-staging.entitlements
<span class="hljs-attribute">ShareExtensionRelease</span>-development.entitlements
<span class="hljs-attribute">ShareExtensionRelease</span>-production.entitlements
<span class="hljs-attribute">ShareExtensionRelease</span>-staging.entitlements
</code></pre>
<p>Once this is done, link the entitlement files by clicking <code>+</code> button for app group capabilities for each flavor.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290359941/a5681d0c-2d34-4a64-9d15-657cc78085ea.png" alt="reverify your entitlements file" class="image--center mx-auto" /></p>
<blockquote>
<p><strong>💡Tip:</strong> Make sure the entitlements files are correctly linked, else this may not work properly for each flavor.</p>
</blockquote>
<p>Make sure, we have all the entitlements files linked up correctly. If we do everything right we will see the following.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290370952/47123d8c-caa9-4a75-9178-e2420c29dbb5.png" alt="all linked group ids" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290380424/6dbfe44f-3a78-4b7e-a677-5c04999e3a55.png" alt="final entitlement files" class="image--center mx-auto" /></p>
<blockquote>
<p>🚨 For iOS setup without flavors</p>
</blockquote>
<p>All the setup above can be taken reference for the apps without flavors as well. The only difference here is, we can have single entitlements file for each target, that is <code>Runner.entitlements</code> and <code>ShareExtension.entitlements</code>.</p>
<p>The bundle id for the <code>Share Extension</code> will be <code>np.com.saileshdahal.sharing.ShareExtension</code>, and the group bundle id will be <code>group.np.com.saileshdahal.sharing</code></p>
<p>Now, for the final setup, we will make some changes in <code>Podfile</code> and register the dependency.</p>
<pre><code class="lang-ruby">target <span class="hljs-string">'Runner'</span> <span class="hljs-keyword">do</span>
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE_<span class="hljs-number">_</span>))

  <span class="hljs-comment"># Add this block</span>
  target <span class="hljs-string">'ShareExtension'</span> <span class="hljs-keyword">do</span>
    inherit! <span class="hljs-symbol">:search_paths</span>
    pod <span class="hljs-string">"share_handler_ios_models"</span>, <span class="hljs-symbol">:path</span> =&gt; <span class="hljs-string">".symlinks/plugins/share_handler_ios/ios/Models"</span>
  <span class="hljs-keyword">end</span>
  <span class="hljs-comment"># End of block</span>

<span class="hljs-keyword">end</span>
</code></pre>
<p>Once this is done, run <code>pod install --repo-update</code> in <code>ios</code> directory.</p>
<blockquote>
<p>🚨Note that <code>pod install --repo-update</code> gives an error: [!] No podspec found for <code>share_handler_ios_models</code> in <code>.symlinks/plugins/share_handler_ios/ios/Models</code>until the project has been built at least once, at which point Flutter has created the referenced symlink.</p>
</blockquote>
<p>🥳 Thanks <a class="user-mention" href="https://hashnode.com/@gisborne">Guyren Howe</a> for mentioning this in the comments.</p>
<hr />
<h2 id="heading-testing-time">🧪 Testing time</h2>
<p>Let's check if we can see our app when we share something.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290414640/002962c1-c902-45e5-b31c-528e5ffbf932.png" alt="app showing in share intent" class="image--center mx-auto" /></p>
<p>Here, we can see both flavors appear while we try to share images from photos.</p>
<hr />
<h2 id="heading-getting-shared-media-in-flutter">Getting shared media in Flutter</h2>
<p>Now that we have both Android and iOS working, we can start implementing our share handler service to get the shared media.</p>
<p>The package exposes a stream of <code>SharedMedia</code>. We can subscribe to this stream and do side effects based on the shared media type.</p>
<p>Also, the package has a method to get the initial shared media. This will be helpful if the app is in terminated state, and we open the app from the share page.</p>
<p>I have added the code snippet from <a target="_blank" href="https://pub.dev/packages/share_handler"><code>share_handler</code></a></p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> HomePage({<span class="hljs-keyword">super</span>.key});

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_HomePageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">HomePage</span>&gt; </span>{
  StreamSubscription&lt;SharedMedia&gt;? _streamSubscription;
  SharedMedia? media;

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

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _streamSubscription?.cancel();
    <span class="hljs-keyword">super</span>.dispose();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; initPlatformState() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> handler = ShareHandlerPlatform.instance;
    media = <span class="hljs-keyword">await</span> handler.getInitialSharedMedia();
    _streamSubscription = handler.sharedMediaStream.listen((SharedMedia media) {
      <span class="hljs-keyword">if</span> (!mounted) <span class="hljs-keyword">return</span>;
      setState(() =&gt; <span class="hljs-keyword">this</span>.media = media);
    });
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Share Handler'</span>),
      ),
      body: ListView(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16</span>),
        children: &lt;Widget&gt;[
          Text(
            <span class="hljs-string">'Shared to conversation identifier: <span class="hljs-subst">${media?.conversationIdentifier}</span>'</span>,
          ),
          <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
          Text(<span class="hljs-string">'Shared text: <span class="hljs-subst">${media?.content}</span>'</span>),
          <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
          Text(<span class="hljs-string">'Shared files: <span class="hljs-subst">${media?.attachments?.length}</span>'</span>),
          ...(media?.attachments ?? []).map((attachment) {
            <span class="hljs-keyword">final</span> path = attachment?.path;
            <span class="hljs-keyword">if</span> (path != <span class="hljs-keyword">null</span> &amp;&amp;
                attachment?.type == SharedAttachmentType.image) {
              <span class="hljs-keyword">return</span> Column(
                children: [
                  ElevatedButton(
                    onPressed: () {
                      ShareHandlerPlatform.instance.recordSentMessage(
                        conversationIdentifier:
                            <span class="hljs-string">'custom-conversation-identifier'</span>,
                        conversationName: <span class="hljs-string">'John Doe'</span>,
                        conversationImageFilePath: path,
                        serviceName: <span class="hljs-string">'custom-service-name'</span>,
                      );
                    },
                    child: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Record message'</span>),
                  ),
                  <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                  Image.file(File(path)),
                ],
              );
            }
            <span class="hljs-keyword">return</span> Text(
              <span class="hljs-string">'<span class="hljs-subst">${attachment?.type}</span> Attachment: <span class="hljs-subst">${attachment?.path}</span>'</span>,
            );
          }),
        ],
      ),
    );
  }
}
</code></pre>
<p>Now, if we run this code and try to share an image from Photos, we should see something like this 🥳.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676290453387/c35ad321-b438-4991-b90e-02fc03cff5af.gif" alt="working demo of the gif" class="image--center mx-auto" /></p>
<hr />
<p>🎉 Congratulations, we've reached the end of this article on how to easily share content from external apps to your Flutter app using the share_handler package! 🚀 Hopefully, you found this tutorial helpful and have learned a lot about effortless sharing in Flutter.</p>
<p>As always, the source code for this tutorial can be found on the GitHub repository at <a target="_blank" href="https://github.com/saileshbro/sharing_into_flutter_app"><code>saileshbro/sharing_into_flutter_app</code></a>. If you found this article useful, don't forget to leave a ⭐️ and share it with others.</p>
<p>And if you're interested in learning more about setting up flavors in Flutter, be sure to check out <a target="_blank" href="https://saileshdahal.com.np/flavor-setup-flutter"><strong>🍰 Simplifying flavor setup in the existing Flutter app: A comprehensive guide</strong></a> to learn how to add different flavors to your app with unique configurations and build targets.</p>
<p>Thank you for joining me on this journey, and happy coding! 💻👨‍💻</p>
<h2 id="heading-references">References</h2>
<ul>
<li><a target="_blank" href="https://pub.dev/packages/share_handler"><code>share_handler</code></a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[🍰 Simplifying flavor setup in the existing Flutter app: A comprehensive guide]]></title><description><![CDATA[Flavors are build configurations in Flutter apps that allow developers to create separate environments using the same code base. They allow for customization at runtime based on the defined compile-time configurations.

Flavors allow efficient manage...]]></description><link>https://saileshdahal.com.np/flavor-setup-flutter</link><guid isPermaLink="true">https://saileshdahal.com.np/flavor-setup-flutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Dart]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Mon, 06 Feb 2023 15:28:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685205376/e19fbb28-7bf8-4c7e-8018-a891cab70ef1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Flavors are build configurations in Flutter apps that allow developers to create separate environments using the same code base. They allow for customization at runtime based on the defined compile-time configurations.</p>
<ul>
<li><p>Flavors allow efficient management of different development environments (development, staging, production)</p>
</li>
<li><p>Enables creation of multiple versions of the same app (free and paid versions, separate environments for feature development)</p>
</li>
<li><p>Simplifies setting different parameters (API endpoints, API keys, app icons) for each configuration</p>
</li>
<li><p>Makes it easier to manage different app versions</p>
</li>
</ul>
<blockquote>
<p>Apps created using the <a target="_blank" href="https://pub.dev/packages/very_good_cli"><code>very_good_cli</code></a> comes with three flavors - <code>development</code>, <code>staging</code>, and <code>production</code>.</p>
</blockquote>
<hr />
<h1 id="heading-adding-flavors-to-an-existing-flutter-app">Adding Flavors to an Existing Flutter App</h1>
<p>When it comes to adding flavors to an existing Flutter app, there are packages available that promise to automate the process, such as <a target="_blank" href="https://pub.dev/packages/flutter_flavorizr"><code>flutter_flavorizr</code></a>.</p>
<p>It's common for these solutions to encounter issues when the app has already made extensive use of native plugins and custom configurations.</p>
<p>As a result, manually adding flavors to an app may be the best course of action. This process requires a bit of human touch, but it provides complete control over the configuration of your app's flavors.</p>
<hr />
<h1 id="heading-setting-up-flavors">Setting Up Flavors 🛠️</h1>
<p>To get a hands-on experience, we will be adding flavors to <a target="_blank" href="https://github.com/saileshbro/fitness_app_clone"><strong>an existing app from here</strong></a></p>
<blockquote>
<p>We will be setting up <code>development</code>, <code>staging</code> and <code>production</code> flavors.</p>
</blockquote>
<h2 id="heading-create-target-files">Create Target Files 💻</h2>
<p>We will start by creating three main files for each flavor, <code>main_&lt;flavor&gt;.dart</code> and create a <code>main</code> function.</p>
<p>We can replace the outdated <code>main.dart</code> file with our new entry points. In the <code>bootstrap.dart file</code>, we'll create a new function called <code>bootstrap</code> which will accept the environment from each entry point and use it to launch the app.</p>
<ul>
<li><code>main_production.dart</code></li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">void</span> main() =&gt; bootstrap(<span class="hljs-string">'production'</span>);
</code></pre>
<ul>
<li><code>main_development.dart</code></li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">void</span> main() =&gt; bootstrap(<span class="hljs-string">'development'</span>);
</code></pre>
<ul>
<li><code>main_staging.dart</code></li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">void</span> main() =&gt; bootstrap(<span class="hljs-string">'staging'</span>);
</code></pre>
<ul>
<li><code>bootstrap.dart</code></li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">void</span> bootstrap(<span class="hljs-built_in">String</span> env) =&gt; runApp(MyApp(env: env));

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MyApp({<span class="hljs-keyword">super</span>.key, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.env});
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> env;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      debugShowCheckedModeBanner: <span class="hljs-keyword">false</span>,
      builder: (context, child) {
        <span class="hljs-keyword">if</span> (env == <span class="hljs-string">'production'</span>) <span class="hljs-keyword">return</span> child!;
        <span class="hljs-keyword">return</span> Banner(
          message: env.toUpperCase(),
          location: BannerLocation.topEnd,
          child: child,
        );
      },
      theme: ThemeData(
        fontFamily: <span class="hljs-string">'Montserrat'</span>,
      ),
      home: <span class="hljs-keyword">const</span> HomePage(),
    );
  }
}
</code></pre>
<p>Since setting up flavors in android is pretty straightforward, we will start with it so we can test it quickly.</p>
<h2 id="heading-using-flavors-in-android">Using flavors in Android 🤖</h2>
<p>To add <code>production</code>, <code>staging</code> and <code>development</code> flavors, let's make some changes in the app level <code>build.gradle</code> in <code>android/app/build.gradle</code> file.</p>
<p>Here, let's specify a <code>flavorDimensions</code> and a <code>productFlavors</code> object.</p>
<pre><code class="lang-apache">    <span class="hljs-attribute">flavorDimensions</span> 'default'
    <span class="hljs-attribute">productFlavors</span> {
        <span class="hljs-attribute">production</span> {
            <span class="hljs-attribute">dimension</span> 'default'
            <span class="hljs-attribute">resValue</span> <span class="hljs-string">"string"</span>, <span class="hljs-string">"app_name"</span>, <span class="hljs-string">"FITNESS"</span>
        }
        <span class="hljs-attribute">development</span> {
            <span class="hljs-attribute">dimension</span> 'default'
            <span class="hljs-attribute">applicationIdSuffix</span> '.dev'
            <span class="hljs-attribute">versionNameSuffix</span> '.dev'
            <span class="hljs-attribute">resValue</span> <span class="hljs-string">"string"</span>, <span class="hljs-string">"app_name"</span>, <span class="hljs-string">"FITNESS.DEV"</span>
        }

        <span class="hljs-attribute">staging</span> {
            <span class="hljs-attribute">dimension</span> 'default'
            <span class="hljs-attribute">applicationIdSuffix</span> '.stg'
            <span class="hljs-attribute">versionNameSuffix</span> '.stg'
            <span class="hljs-attribute">resValue</span> <span class="hljs-string">"string"</span>, <span class="hljs-string">"app_name"</span>, <span class="hljs-string">"FITNESS.STG"</span>
        }
    }
</code></pre>
<p>The <code>flavorDimensions</code> line defines the name of the flavor dimension, which is <code>default</code> in this case. Each flavor is defined within the <code>productFlavors</code> block. The <code>production</code> flavor is the base flavor of the app.</p>
<p>The <code>development</code> and <code>staging</code> flavors are similar, but with some differences. The <code>applicationIdSuffix</code> and <code>versionNameSuffix</code> lines add the <code>.dev</code> or <code>.stg</code> suffix to the application ID and version name, respectively. The <code>resValue</code> line sets the name of the app to <code>FITNESS.DEV</code> or <code>FITNESS.STG</code>, respectively.</p>
<blockquote>
<p>NOTE: Don't forget to add <code>android:label="@string/app_name"</code> to the <code>application</code> tag in <code>AndroidManifest.xml</code> file.</p>
</blockquote>
<hr />
<h2 id="heading-lets-test">Let's Test 🧪</h2>
<p>Now, we can run the app using the following commands.</p>
<pre><code class="lang-bash">flutter run --flavor development --target lib/main_development.dart
flutter run --flavor staging --target lib/main_staging.dart
flutter run --flavor production --target lib/production.dart
</code></pre>
<p>Once done, you should have three different apps with different names, versions, and bundle identifiers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685697832/2b172457-0674-4066-89dc-cb6c1755d472.png" alt="android flavors working" class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-ios-schemas">iOS Schemas</h2>
<p>We will now be creating schemas for each flavor in Xcode. We already have a <code>Runner</code> schema, which is the default schema. We will rename it to <code>production</code> and create two new schemas, <code>development</code> and <code>staging</code>.</p>
<ul>
<li>Open the ios folder in Xcode.</li>
</ul>
<pre><code class="lang-bash">open ios/Runner.xcworkspace
</code></pre>
<ul>
<li>Let's make some changes to our configuration.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685767350/9193336b-1b16-4e9e-acde-158c3d3b3f1c.png" alt="config-initial" class="image--center mx-auto" /></p>
<ul>
<li>In Project Runner, rename <code>Debug</code>, <code>Release</code>, and <code>Profile</code> adding <code>-production</code> suffixes to each.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685793314/58f491d2-f3dd-4795-bf5f-2f5229f1f616.png" alt="config-production" class="image--center mx-auto" /></p>
<ul>
<li>Once done, we will now create these 3 files for each <code>staging</code> and <code>development</code> as well. You can use duplicate.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685814141/8854c543-13a8-4f36-b679-5f27b2dfcc3e.png" alt="config-duplicate-button" class="image--center mx-auto" /></p>
<ul>
<li>Once done, we will have 9 configurations, 3 for each flavor.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685828283/2c1b695b-5aa4-40e1-a916-3c585dbb1796.png" alt="configuration" class="image--center mx-auto" /></p>
<ul>
<li>Select the Runner scheme and click the Manage Schemes button.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685852673/f2be4c69-88ec-4bfa-aedd-257b327bd970.png" alt="manage-scheme-button" class="image--center mx-auto" /></p>
<ul>
<li>Once, there rename <code>Runner</code> scheme to <code>production</code>, and duplicate <code>production</code> to two new <code>development</code> and <code>staging</code>.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685906070/131c1a60-3d98-411e-9b86-5db4fba1d6b9.png" alt="duplicate-button" class="image--center mx-auto" /></p>
<ul>
<li>Make sure to add the correct configuration while duplicating.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685935904/cf4fe682-11b5-458c-88aa-50cd3360cf29.jpeg" alt="staging-scheme" class="image--center mx-auto" /></p>
<p>Similarly, create a <code>development</code> scheme with the correct configuration.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685963735/70050902-c61c-4920-b9bd-71bd11905135.png" alt="development-scheme" class="image--center mx-auto" /></p>
<p>Once you are done with schemes, you will have something like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685978858/81dc9e70-2888-47f3-b86a-2cb5c37d8b74.png" alt="final-schemes" class="image--center mx-auto" /></p>
<blockquote>
<p><strong>NOTE</strong>: Make sure all three schemes have correct configuration, don't forget to recheck <code>production</code> scheme configuration.</p>
</blockquote>
<ul>
<li>Now, let's add the bundle identifier for each configuration. In <code>Target</code>-&gt; <code>Runner</code>, click <code>Build Settings</code> and search for <code>Product Bundle Identifier</code>.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675685992723/f8b3b271-b6f6-49ef-93c7-14977e69f8ef.jpeg" alt="bundle-identifier" class="image--center mx-auto" /></p>
<blockquote>
<p>Add the suffix as required.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686084412/1a9e836a-13fb-408b-9504-5032e7bf78b7.png" alt="final-bundle-ids" class="image--center mx-auto" /></p>
<p>Once you are done, you will have something like this.</p>
<ul>
<li>Finally, we will create a new <code>User Defined Variable</code> called <code>APP_DISPLAY_NAME</code> which will have a different name for each configuration.</li>
</ul>
<p>In same <code>Target</code> -&gt; <code>Runner</code> -&gt; <code>Build Settings</code>, click on the <code>+</code> button, and create a new user-defined variable.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686104706/8d88f4d4-2ccb-4723-bd53-12c030d5b47f.png" alt="user_defined_variable" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686120917/c0d571c2-5b53-49d0-86a3-a1bd0cdfd43f.png" alt="APP_DISPLAY_NAME" class="image--center mx-auto" /></p>
<p>Once, you are done with the user-defined variable, you need to change the <code>Info.plist</code> to use this variable.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleName<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>$(APP_DISPLAY_NAME)<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleDisplayName<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>$(APP_DISPLAY_NAME)<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
<hr />
<h2 id="heading-lets-test-1">Let's Test 🧪</h2>
<p>Similar to android, we can test iOS by running the following commands.</p>
<pre><code class="lang-bash">flutter run --flavor development --target lib/main_development.dart
flutter run --flavor staging --target lib/main_staging.dart
flutter run --flavor production --target lib/production.dart
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675696750786/806598ad-33cc-4e3b-8c85-df0e578cf60b.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-app-icons-for-android-and-ios">App Icons for Android and iOS 🎨</h2>
<blockquote>
<p>🎆 Let's make it look pretty!</p>
</blockquote>
<p>Now, let's have different icons based on the flavors. We will have 3 different icons for each flavor.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686180483/ae0ed32b-1b2e-43c9-8d23-32194e23ad45.png" alt="launch-icons" class="image--center mx-auto" /></p>
<p>We will use <a target="_blank" href="https://pub.dev/packages/flutter_launcher_icons"><code>flutter_launcher_icons</code></a> to generate the launch icons. Install it as a dev dependency.</p>
<pre><code class="lang-bash">flutter pub add flutter_launcher_icons --dev
</code></pre>
<p>Once this is done, we will create <code>flutter_launcher_icons-&lt;flavor&gt;.yaml</code> files for each flavor.</p>
<ul>
<li><code>flutter_launcher_icons-development.yaml</code></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">flutter_icons:</span>
  <span class="hljs-attr">android:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">ios:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">remove_alpha_ios:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">image_path:</span> <span class="hljs-string">"assets/icons/dev-icon.png"</span>
</code></pre>
<ul>
<li><code>flutter_launcher_icons-staging.yaml</code></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">flutter_icons:</span>
  <span class="hljs-attr">android:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">ios:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">remove_alpha_ios:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">image_path:</span> <span class="hljs-string">"assets/icons/stg-icon.png"</span>
</code></pre>
<ul>
<li><code>flutter_launcher_icons-production.yaml</code></li>
</ul>
<pre><code class="lang-yaml"><span class="hljs-attr">flutter_icons:</span>
  <span class="hljs-attr">android:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">ios:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">remove_alpha_ios:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">image_path:</span> <span class="hljs-string">"assets/icons/prod-icon.png"</span>
</code></pre>
<p>Now to generate the launch icons we need to run</p>
<pre><code class="lang-bash">flutter pub run flutter_launcher_icons:main -f flutter_launcher_icons-*
</code></pre>
<p>This will generate all the icons.</p>
<p>Finally, we will set the icons to be dynamic in Xcode.</p>
<ul>
<li>In <code>Runner</code> -&gt; <code>Assets.xcassets</code> we can see that we have all the required icons. We can remove the <code>AppIcon</code> , which is unused.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686204624/7b92cc30-14d2-46b1-8454-575049fa104d.png" alt="app-icons" class="image--center mx-auto" /></p>
<ul>
<li>Now, in <code>Target</code>-&gt; <code>Runner</code> -&gt; <code>Build Settings</code>, search for <code>primary app icon</code>.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686216871/01802647-e968-4bdb-9cfa-0cec4f07e1fa.png" alt="app-icon-change" class="image--center mx-auto" /></p>
<ul>
<li>Now, set the corresponding suffix to each one.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686232157/77259f1f-7b3f-4bef-93c1-0b488881e650.png" alt="final-app-icon" class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-final-result">Final Result 🎉</h2>
<p>Now, if we run the app, for all the flavors, we will have different icons, names, versions, and bundle identifiers.</p>
<blockquote>
<p>NOTE: Make sure to uninstall the previous builds before running the app.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686316246/d5f7c534-ee4a-49dc-a990-bb1957ffbc8c.png" alt="final-preview" class="image--center mx-auto" /></p>
<hr />
<h1 id="heading-bonus">🎊 Bonus</h1>
<p>For easy usage, we can create some launch configs for <code>Visual Studio Code</code> and <code>Android Studio</code>.</p>
<h2 id="heading-for-visual-studio-code">For Visual Studio Code</h2>
<p>Create a new file in <code>.vscode/launch.json</code> with the following content.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.2.0"</span>,
  <span class="hljs-attr">"configurations"</span>: [
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"DEV | DEBUG"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_development.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"development"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_development.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"debug"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"DEV | RELEASE"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_development.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"development"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_development.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"release"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"DEV | PROFILE"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_development.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"development"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_development.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"profile"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"STG | DEBUG"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_staging.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"staging"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_staging.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"debug"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"STG | RELEASE"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_staging.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"staging"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_staging.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"release"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"STG | PROFILE"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_staging.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"staging"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_staging.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"profile"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"PROD | DEBUG"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_production.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"production"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_production.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"debug"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"PROD | RELEASE"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_production.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"production"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_production.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"release"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"PROD | PROFILE"</span>,
      <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dart"</span>,
      <span class="hljs-attr">"program"</span>: <span class="hljs-string">"lib/main_production.dart"</span>,
      <span class="hljs-attr">"args"</span>: [
        <span class="hljs-string">"--flavor"</span>,
        <span class="hljs-string">"production"</span>,
        <span class="hljs-string">"--target"</span>,
        <span class="hljs-string">"lib/main_production.dart"</span>
      ],
      <span class="hljs-attr">"flutterMode"</span>: <span class="hljs-string">"profile"</span>
    },
  ]
}
</code></pre>
<p>This will create the launch config with all the args.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686338649/11fa8f57-66cb-4452-8ba2-d7f8dfa74b98.png" alt="vs-code-launch-settings" class="image--center mx-auto" /></p>
<h2 id="heading-for-android-studio">For Android Studio</h2>
<p>For Android Studio, you can get the <a target="_blank" href="https://github.com/saileshbro/fitness_app_clone/tree/master/.idea/runConfigurations">configuration files from here</a>, for all the configs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1675686357238/a3b68a58-d26d-4eb6-bd59-c0ce6d6a42a5.png" alt="android-studio-launch-settings" class="image--center mx-auto" /></p>
<hr />
<p>🎉 Congrats! We've made it to the end of our exploration of Flutter Flavors! 🚀 We've successfully learned how to add different flavors to our flutter application, each with its own unique configurations and build targets.</p>
<p>It's just the tip of the iceberg when it comes to the use cases of flavors in Flutter. For instance, you can inject dependencies based on the flavor you are using, which can entirely change the behavior of your app. The possibilities are endless!</p>
<p>As always, you can refer back to the Github repository for this tutorial at <a target="_blank" href="https://github.com/saileshbro/fitness_app_clone">https://github.com/saileshbro/fitness_app_clone</a> if you need any help. Don't forget to give it a ⭐️ and help spread the word about this amazing project. If you get stuck or need assistance, feel free to submit an issue or contribute a pull request.</p>
<p>Thank you for joining me on this journey; let's create even more amazing applications together. Have fun coding! 💻👨‍💻</p>
<hr />
<h2 id="heading-references">References</h2>
<ul>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=Vhm1Cv2uPko">Flutter Flavors, App Icons, and Firebase Tutorial - Marcus Ng</a></p>
</li>
<li><p><a target="_blank" href="https://docs.flutter.dev/deployment/flavors">Creating flavors for Flutter</a></p>
</li>
<li><p><a target="_blank" href="https://developer.android.com/studio/build/build-variants#flavor-dimensions">Android Developers - Build Variants: Flavor Dimensions</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 6]]></title><description><![CDATA[In earlier sections of this tutorial series, we covered the fundamental stages for creating a to-do application with Flutter and Dart.
We've covered everything from bootstrapping an empty Flutter project, importing necessary dependencies, setting up ...]]></description><link>https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-6</link><guid isPermaLink="true">https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-6</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[full stack]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Tue, 31 Jan 2023 15:47:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1675063055042/cfdc6e73-18c5-4cdb-b063-57937224b4d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In earlier sections of this tutorial series, we covered the fundamental stages for creating a to-do application with Flutter and Dart.</p>
<p>We've covered everything from bootstrapping an empty <a target="_blank" href="https://flutter.dev">Flutter</a> project, importing necessary dependencies, setting up the folder structure, integrating the frontend with the backend, and implementing the Todo data sources, repositories, and view models.</p>
<p>In this part, we'll show you how to utilize dart frog to establish user authentication with <a target="_blank" href="https://jwt.io">JSON Web Tokens (JWT)</a>. By the end of this article, we will be able to:</p>
<ul>
<li><p>Implement user login and register.</p>
</li>
<li><p>Create <code>User</code> model.</p>
</li>
<li><p>Create and implement <code>UserRepository</code> and <code>UserDataSource</code>.</p>
</li>
<li><p>Handle <code>UnauthorizedException</code>.</p>
</li>
<li><p>Create a new authorization middleware.</p>
</li>
<li><p>Implement user authentication using <a target="_blank" href="https://jwt.io">JWT</a>.</p>
</li>
<li><p>Secure routes with <code>Authorization</code> headers.</p>
</li>
</ul>
<p>Don't forget to check out the <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart">GitHub</a> repo for this article if you need any assistance along the way.</p>
<blockquote>
<p>Let's get started 🚀</p>
</blockquote>
<hr />
<h1 id="heading-overview">Overview 🔍</h1>
<blockquote>
<p>👀 Get a sneak peek of what's to come 🔮</p>
</blockquote>
<p>When we're done, we should have an app that supports the following requests:</p>
<ol>
<li><code>/todos*</code> - Guard all to-do routes with <code>Authorization</code> header.</li>
</ol>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/todos'</span> -H <span class="hljs-string">'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY5ZWFkZTg3LWRhNWUtNDRjZC1iNTRlLTQ3NGE4NWQ4ZGVlNCIsIm5hbWUiOiJTYWlsZXNoIERhaGFsIiwiZW1haWwiOiJzYWlsZXNoYnJvQGdtYWlsLmNvbSIsImNyZWF0ZWRfYXQiOiIyMDIzLTAxLTIzVDIyOjU0OjM4Ljg2NDkxMloiLCJwYXNzd29yZCI6IiQyYSQxMCR6VjBTZlI3cUpHOHlCelJWWThtNkRlMWo0UUtHL2VRdXl6NzduQ1lBL1luZENtb1ZSbzB0RyIsImlhdCI6MTY3NDQ5Mzc3OX0.W36mHXKFrxZDhz0Hrxt0Cmrz3WNVexiAZe2KAjrWMaE'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "title":"this is a title asdf",
    "description":"Not so great description asdf"
}'</span>
</code></pre>
<ol>
<li><code>/users/login</code> - Login user</li>
</ol>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/users/login'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "email":"saileshbro@gmail.com",
    "password":"6aMj@UBByu"
}'</span>
</code></pre>
<ol>
<li><code>/users/signup</code> - Register new user</li>
</ol>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/users/signup'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "email":"saileshbro@gmail.com",
    "name":"Sailesh Dahal",
    "password":"6aMj@UBByu%7BzN^C9tMe#Te4b!4cJrXwwFi#HgKrQ&amp;g&amp;"
}'</span>
</code></pre>
<hr />
<h1 id="heading-updating-database">Updating Database 🛠️</h1>
<blockquote>
<p>💾 Upgrading our database to its full potential 🚀</p>
</blockquote>
<p>To begin, we will create a new table called <code>users</code> and add a <a target="_blank" href="https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-foreign-key/"><code>foreign key</code></a> to our current <code>todos</code> table. This ensures that each to-do item is associated with a single user, and that data is accessible only to that person.</p>
<h2 id="heading-create-users-table">Create <code>users</code> table</h2>
<p>We will create a new <code>users</code> table with the following columns.</p>
<ul>
<li><p><code>id</code> : unique <code>uuid</code></p>
</li>
<li><p><code>name</code> : name of the user</p>
</li>
<li><p><code>email</code> : email of the user</p>
</li>
<li><p><code>password</code> : hashed password of the user</p>
</li>
<li><p><code>created_at</code> : timestamp of when the user was created</p>
</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> EXTENSION <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> <span class="hljs-string">"uuid-ossp"</span>;
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span>(
    <span class="hljs-keyword">id</span> <span class="hljs-keyword">uuid</span> <span class="hljs-keyword">DEFAULT</span> uuid_generate_v4() PRIMARY <span class="hljs-keyword">KEY</span>,
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">255</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    email <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
      <span class="hljs-keyword">password</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    created_at <span class="hljs-built_in">timestamp</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">current_timestamp</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>
);
</code></pre>
<blockquote>
<p>The <a target="_blank" href="https://www.postgresql.org/docs/10/uuid-ossp.html"><code>uuid-ossp</code></a> extension is used to generate unique IDs for each user.</p>
</blockquote>
<h2 id="heading-updating-todos-table">Updating <code>todos</code> table</h2>
<p>We will update the <code>todos</code> table to include a foreign key to the <code>users</code> table.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">TRUNCATE</span> <span class="hljs-keyword">TABLE</span> todos;
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> todos
    <span class="hljs-keyword">ADD</span> <span class="hljs-keyword">COLUMN</span> user_id <span class="hljs-keyword">uuid</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    <span class="hljs-keyword">ADD</span> <span class="hljs-keyword">CONSTRAINT</span> constraint_fk
        <span class="hljs-keyword">FOREIGN</span> <span class="hljs-keyword">KEY</span> (user_id)
            <span class="hljs-keyword">REFERENCES</span> <span class="hljs-keyword">users</span> (<span class="hljs-keyword">id</span>)
            <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">CASCADE</span>;
</code></pre>
<blockquote>
<p>The <a target="_blank" href="https://www.commandprompt.com/education/postgresql-delete-cascade-with-examples/"><code>ON DELETE CASCADE</code></a> clause ensures that when a user is deleted, all their to-do items will be deleted.</p>
</blockquote>
<p>Once we are done with the changes, we can proceed to create a user schema in our <code>models</code> package.</p>
<blockquote>
<p>More about <a target="_blank" href="https://www.postgresql.org/docs/current/ddl-constraints.html"><code>CONSTRAINTS</code></a> here.</p>
</blockquote>
<hr />
<h1 id="heading-create-user-model">Create <code>User</code> model</h1>
<blockquote>
<p>🧑‍💼 Defining our User Model 📝</p>
</blockquote>
<p>We will start by creating a <code>UserId</code> in our <code>typedefs</code> package.</p>
<h2 id="heading-create-userid-type">Create <code>UserId</code> type</h2>
<p>In <code>typedefs</code> package, we will make some changes. We will rename the older <code>src/typedefs.dart</code> file to <code>src/todo_types.dart</code> and create a new <code>src/user_types.dart</code> file.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">typedef</span> UserId = <span class="hljs-built_in">String</span>;
</code></pre>
<blockquote>
<p>💡 Make sure to update the exports.</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> typedefs;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/todo_types.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/user_types.dart'</span>;
</code></pre>
<h2 id="heading-update-folder-structure">Update folder structure</h2>
<p>In <code>models</code> package, we will create a new <code>user.dart</code> file. Since we are creating models for a different feature, we will mode the todo specific models to a new <code>todo</code> folder.</p>
<pre><code class="lang-apache">.
├── <span class="hljs-attribute">create_todo_dto</span>
│   └── <span class="hljs-attribute">create_todo_dto</span>.dart
├── <span class="hljs-attribute">todo</span>.dart
└── <span class="hljs-attribute">update_todo_dto</span>
    └── <span class="hljs-attribute">update_todo_dto</span>.dart
</code></pre>
<p>Also, make sure to export <code>create_todo_dto</code> and <code>update_todo_dto</code> from <code>todo.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/src/serializers/date_time_converter.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-string">'./create_todo_dto/create_todo_dto.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'./update_todo_dto/update_todo_dto.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'todo.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'todo.g.dart'</span>;
</code></pre>
<h2 id="heading-create-user-model-1">Create <code>User</code> model</h2>
<p>Now, we will create a <code>User</code> model in <code>src/user/user.dart</code> file.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/src/serializers/date_time_converter.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'user.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'user.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<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>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> User({
    <span class="hljs-keyword">required</span> UserId id,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> name,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> email,
    <span class="hljs-meta">@DateTimeConverter</span>() <span class="hljs-keyword">required</span> <span class="hljs-built_in">DateTime</span> createdAt,
    <span class="hljs-meta">@Default</span>(<span class="hljs-string">''</span>) <span class="hljs-meta">@JsonKey</span>(includeToJson: <span class="hljs-keyword">false</span>) <span class="hljs-built_in">String</span> password,
  }) = _User;

  <span class="hljs-keyword">factory</span> User.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt; _$UserFromJson(json);
}
</code></pre>
<blockquote>
<p>We are ignoring the <code>password</code> field when serializing the model to JSON. This is because we don't want to send the password to the client.</p>
</blockquote>
<p>Similarly, we will create <code>CreateUserDto</code> and <code>LoginUserDto</code> for creating and logging in users.</p>
<p>In <code>src/user/create_user_dto/create_user_dto.dart</code> file,</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:exceptions/exceptions.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'create_user_dto.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'create_user_dto.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreateUserDto</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">CreateUserDto</span> </span>{
  <span class="hljs-keyword">factory</span> CreateUserDto({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> name,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> email,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> password,
  }) = _CreateUserDto;

  <span class="hljs-keyword">factory</span> CreateUserDto.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$CreateUserDtoFromJson(json);

  <span class="hljs-keyword">static</span> Either&lt;ValidationFailure, CreateUserDto&gt; validated(
    <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json,
  ) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> errors = &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt;{};
      <span class="hljs-keyword">final</span> name = json[<span class="hljs-string">'name'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String?</span> ?? <span class="hljs-string">''</span>;
      <span class="hljs-keyword">final</span> email = json[<span class="hljs-string">'email'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String?</span> ?? <span class="hljs-string">''</span>;
      <span class="hljs-keyword">final</span> password = json[<span class="hljs-string">'password'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String?</span> ?? <span class="hljs-string">''</span>;
      <span class="hljs-keyword">if</span> (name.isEmpty) {
        errors[<span class="hljs-string">'name'</span>] = [<span class="hljs-string">'Name is required'</span>];
      }
      <span class="hljs-keyword">if</span> (email.isEmpty) {
        errors[<span class="hljs-string">'email'</span>] = [<span class="hljs-string">'Email is required'</span>];
      }
      <span class="hljs-keyword">if</span> (!email.contains(<span class="hljs-string">'@'</span>)) {
        errors[<span class="hljs-string">'email'</span>] = [<span class="hljs-string">'Email is invalid'</span>];
      }
      <span class="hljs-keyword">if</span> (password.isEmpty) {
        errors[<span class="hljs-string">'password'</span>] = [<span class="hljs-string">'Password is required'</span>];
      }
      <span class="hljs-keyword">if</span> (password.length &lt; <span class="hljs-number">6</span>) {
        errors[<span class="hljs-string">'password'</span>] = [<span class="hljs-string">'Password must be at least 6 characters'</span>];
      }

      <span class="hljs-keyword">if</span> (errors.isEmpty) <span class="hljs-keyword">return</span> Right(CreateUserDto.fromJson(json));
      <span class="hljs-keyword">throw</span> BadRequestException(
        message: <span class="hljs-string">'Validation failed'</span>,
        errors: errors,
      );
    } <span class="hljs-keyword">on</span> BadRequestException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> Left(
        ValidationFailure(
          message: e.message,
          errors: e.errors,
          statusCode: e.statusCode,
        ),
      );
    }
  }
}
</code></pre>
<p>Similarly, we will create a new <code>login_user_dto</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:exceptions/exceptions.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'login_user_dto.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'login_user_dto.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoginUserDto</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">LoginUserDto</span> </span>{
  <span class="hljs-keyword">factory</span> LoginUserDto({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> email,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> password,
  }) = _LoginUserDto;

  <span class="hljs-keyword">factory</span> LoginUserDto.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$LoginUserDtoFromJson(json);

  <span class="hljs-keyword">static</span> Either&lt;ValidationFailure, LoginUserDto&gt; validated(
    <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json,
  ) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> errors = &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt;{};
      <span class="hljs-keyword">final</span> email = json[<span class="hljs-string">'email'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String?</span> ?? <span class="hljs-string">''</span>;
      <span class="hljs-keyword">final</span> password = json[<span class="hljs-string">'password'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String?</span> ?? <span class="hljs-string">''</span>;
      <span class="hljs-keyword">if</span> (email.isEmpty) {
        errors[<span class="hljs-string">'email'</span>] = [<span class="hljs-string">'Email is required'</span>];
      }
      <span class="hljs-keyword">if</span> (password.isEmpty) {
        errors[<span class="hljs-string">'password'</span>] = [<span class="hljs-string">'Password is required'</span>];
      }
      <span class="hljs-keyword">if</span> (errors.isEmpty) <span class="hljs-keyword">return</span> Right(LoginUserDto.fromJson(json));
      <span class="hljs-keyword">throw</span> BadRequestException(
        message: <span class="hljs-string">'Validation failed'</span>,
        errors: errors,
      );
    } <span class="hljs-keyword">on</span> BadRequestException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> Left(
        ValidationFailure(
          message: e.message,
          errors: e.errors,
          statusCode: e.statusCode,
        ),
      );
    }
  }
}
</code></pre>
<p>These data transfer objects will be used to send, receive and validate data from the client in the backend.</p>
<blockquote>
<p>💡 Make sure to export <code>create_user_dto</code> and <code>login_user_dto</code> from <code>user.dart</code>, and <code>user.dart</code> from <code>models.dart</code></p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/src/serializers/date_time_converter.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-string">'./create_user_dto/create_user_dto.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'./login_user_dto/login_user_dto.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'user.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'user.g.dart'</span>;
</code></pre>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> models;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/todo/todo.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/user/user.dart'</span>;
</code></pre>
<blockquote>
<p>💡 Make sure to run <code>flutter pub run build_runner build</code> to generate new files.</p>
</blockquote>
<hr />
<h1 id="heading-creating-userdatasource">Creating <code>UserDataSource</code></h1>
<blockquote>
<p>📊 Building the foundation for user management with <code>UserDataSource</code> 🛠️</p>
</blockquote>
<p>In <code>data_source</code> package, we will create a new interface called <code>UserDataSource</code> in <code>data_source/lib/src/user_data_source.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserDataSource</span> </span>{
  Future&lt;User&gt; getUserById(UserId id);

  Future&lt;User&gt; createUser(CreateUserDto user);

  Future&lt;User&gt; getUserByEmail(<span class="hljs-built_in">String</span> email);
}
</code></pre>
<p>We will have methods to query and create users. We will do the implementation in our <code>backend</code>.</p>
<hr />
<h1 id="heading-creating-userrepository">Creating <code>UserRepository</code></h1>
<blockquote>
<p>🔍 Designing our User Management System with UserRepository 🛠️</p>
</blockquote>
<p>We will also create an interface called <code>UserRepository</code> which will return either a failure or valid data making use of <code>UserDataSource</code>.</p>
<p>In <code>repository</code> package, we will create a new interface called <code>UserRepository</code> in <code>repository/lib/src/user_repository.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserRepository</span> </span>{
  Future&lt;Either&lt;Failure, User&gt;&gt; getUserById(UserId id);

  Future&lt;Either&lt;Failure, User&gt;&gt; createUser(CreateUserDto createUserDto);

  Future&lt;Either&lt;Failure, User&gt;&gt; loginUser(LoginUserDto loginUserDto);

  Future&lt;Either&lt;Failure, User&gt;&gt; getUserByEmail(<span class="hljs-built_in">String</span> email);
}
</code></pre>
<p>We will have all the methods we need to create, query and login users. We will do the implementation in our backend.</p>
<blockquote>
<p>💡 Make sure to export <code>UserRepository</code> from <code>repository.dart</code></p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> repository;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/todo_repository.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/user_repository.dart'</span>;
</code></pre>
<hr />
<h1 id="heading-implementing-userdatasource">Implementing <code>UserDataSource</code></h1>
<blockquote>
<p>💻 Bringing <code>UserDataSource</code> to life 🔧</p>
</blockquote>
<p>Now, we will implement <code>UserDataSource</code> in our backend. In <code>backend/lib/user/data_source/user_data_source_impl.dart</code>, we will create a new class called <code>UserDataSourceImpl</code> which will implement <code>UserDataSource</code>.</p>
<p>This is how an empty implementation of <code>UserDataSourceImpl</code> will look like.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserDataSourceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserDataSource</span> </span>{
  UserDataSourceImpl(<span class="hljs-keyword">this</span>._databaseConnection);

  <span class="hljs-keyword">final</span> DatabaseConnection _databaseConnection;
  Future&lt;User&gt; createUser(CreateUserDto user) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;User&gt; getUserByEmail(<span class="hljs-built_in">String</span> email) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;User&gt; getUserById(UserId id) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }
}
</code></pre>
<h2 id="heading-implementing-createuser-method">Implementing <code>createUser</code> method</h2>
<p>Now, we will implement <code>createUser</code> method. We will use <code>DatabaseConnection</code> to connect to our database and create a new user from the dto we received.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;User&gt; createUser(CreateUserDto user) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.connect();
      <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _databaseConnection.db.query(
        <span class="hljs-string">'''
        INSERT INTO users (name, email, password)
        VALUES (@name, @email, @password) RETURNING *
        '''</span>,
        substitutionValues: user.toJson(),
      );
      <span class="hljs-keyword">if</span> (result.affectedRowCount == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> ServerException(<span class="hljs-string">'Failed to create todo'</span>);
      }
      <span class="hljs-keyword">final</span> userMap = result.first.toColumnMap();
      <span class="hljs-keyword">return</span> User.fromJson(userMap);
    } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.close();
    }
  }
</code></pre>
<p>We will validate the DTO in the repository before passing it to the data source. The database will then be queried to generate a new user. We will throw a <code>ServerException</code> if the user is not created, else, we will return the user that was created.</p>
<h2 id="heading-implementing-getuserbyemail-method">Implementing <code>getUserByEmail</code> method</h2>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;User&gt; getUserByEmail(<span class="hljs-built_in">String</span> email) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.connect();
      <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _databaseConnection.db.query(
        <span class="hljs-string">'''
        SELECT id, name, email, password, created_at
        FROM users WHERE email = @email
        '''</span>,
        substitutionValues: {<span class="hljs-string">'email'</span>: email},
      );
      <span class="hljs-keyword">if</span> (result.isEmpty) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> NotFoundException(<span class="hljs-string">'User not found'</span>);
      }
      <span class="hljs-keyword">return</span> User.fromJson(result.first.toColumnMap());
    } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.close();
    }
  }
</code></pre>
<p>Here, we will query the database for the user with the given email. If the user is not found, we will throw a <code>NotFoundException</code>. We will handle this exception in the <code>UserController</code> and return a <code>404</code> response.</p>
<h2 id="heading-implementing-getuserbyid-method">Implementing <code>getUserById</code> method</h2>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
  Future&lt;User&gt; getUserById(UserId id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.connect();
      <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _databaseConnection.db.query(
        <span class="hljs-string">'SELECT id, name, email, created_at FROM users WHERE id = @id'</span>,
        substitutionValues: {<span class="hljs-string">'id'</span>: id},
      );
      <span class="hljs-keyword">if</span> (result.isEmpty) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> NotFoundException(<span class="hljs-string">'User not found'</span>);
      }
      <span class="hljs-keyword">return</span> User.fromJson(result.first.toColumnMap());
    } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.close();
    }
  }
</code></pre>
<p>We will query the database for the user with the given id. If the user is not found, we will throw a <code>NotFoundException</code>. We will handle this exception in the <code>UserController</code> and return a <code>404</code> response.</p>
<hr />
<h1 id="heading-creating-passwordhasherservice">Creating <code>PasswordHasherService</code></h1>
<blockquote>
<p>🔑 Keeping user passwords safe with <code>PasswordHasherService</code> 🔒</p>
</blockquote>
<p>We will create a new service to hash and validate hashed passwords before going on to the <code>UserRepository</code> implementation. The <code>CreateUserDto</code> with the hashed password will be passed to the <code>UserRepository</code>.</p>
<p>In <code>backend/lib/services/password_hasher_service.dart</code>, we will create a new class called <code>PasswordHasherService</code>.</p>
<p>We will use <a target="_blank" href="https://pub.dev/packages/bcrypt"><code>bcrypt</code></a> package to hash and verify the password.</p>
<pre><code class="lang-bash">flutter pub add bcrypt
</code></pre>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:bcrypt/bcrypt.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PasswordHasherService</span> </span>{
  <span class="hljs-keyword">const</span> PasswordHasherService();

  <span class="hljs-built_in">String</span> hashPassword(<span class="hljs-built_in">String</span> password) {
    <span class="hljs-keyword">return</span> BCrypt.hashpw(password, BCrypt.gensalt());
  }

  <span class="hljs-built_in">bool</span> checkPassword(<span class="hljs-built_in">String</span> password, <span class="hljs-built_in">String</span> hashedPassword) {
    <span class="hljs-keyword">return</span> BCrypt.checkpw(password, hashedPassword);
  }
}
</code></pre>
<p>Now, we can pass this service as a dependency to <code>UserRepository</code> when we implement our repository.</p>
<hr />
<h1 id="heading-implementing-userrepository">Implementing <code>UserRepository</code></h1>
<blockquote>
<p>💻 Bringing <code>UserRepository</code> to life 🔧</p>
</blockquote>
<p>Now, we will implement <code>UserRepository</code> in our backend. In <code>backend/lib/user/repository/user_repository_impl.dart</code>, we will create a new class called <code>UserRepositoryImpl</code> which will implement <code>UserRepository</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/services/password_hasher_service.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserRepositoryImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">UserRepository</span> </span>{
  UserRepositoryImpl(<span class="hljs-keyword">this</span>.dataSource, <span class="hljs-keyword">this</span>.passwordHasherService);

  <span class="hljs-keyword">final</span> UserDataSource dataSource;
  <span class="hljs-keyword">final</span> PasswordHasherService passwordHasherService;

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; createUser(CreateUserDto createUserDto) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; getUserByEmail(<span class="hljs-built_in">String</span> email) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; getUserById(UserId id) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; loginUser(LoginUserDto loginUserDto) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }
}
</code></pre>
<h2 id="heading-implementing-getuserbyemail-method-1">Implementing <code>getUserByEmail</code> method</h2>
<p>We will start by implementing <code>getUserByEmail</code>. We will use the data source's <code>dataSource.getUserByEmail</code> method to see if we get a <code>User</code> object. If we get a <code>User</code> object, we shall wrap it in a <code>Right</code> object and return it. If an error occurs, we will return a <code>Left</code> object along with a <code>ServerFailure</code> object.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; getUserByEmail(<span class="hljs-built_in">String</span> email) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> user = <span class="hljs-keyword">await</span> dataSource.getUserByEmail(email);
      <span class="hljs-keyword">return</span> Right(user);
    } <span class="hljs-keyword">catch</span> (e) {
      log(e.toString());
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> Left(
        ServerFailure(
          message: <span class="hljs-string">'User with this email does not exist'</span>,
          statusCode: HttpStatus.notFound,
        ),
      );
    }
  }
</code></pre>
<h2 id="heading-implementing-getuserbyid-method-1">Implementing <code>getUserById</code> method</h2>
<p>Similarly, we will call <code>dataSource.getUserById</code> method and see whether we get a <code>User</code> object. If we get a <code>User</code> object, we shall wrap it in a <code>Right</code> object and return it. If an error occurs, we will return a <code>Left</code> object along with a <code>ServerFailure</code> object.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; getUserById(UserId id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> dataSource.getUserById(id);
      <span class="hljs-keyword">return</span> Right(res);
    } <span class="hljs-keyword">on</span> NotFoundException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(
          message: e.message,
          statusCode: e.statusCode,
        ),
      );
    } <span class="hljs-keyword">on</span> ServerException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(message: e.message),
      );
    }
  }
</code></pre>
<h2 id="heading-implementing-loginuser-method">Implementing <code>loginUser</code> method</h2>
<p>Now, we will implement <code>loginUser</code>. We will check if the user with the given email exists. If the user exists, we will check the password with <code>PasswordHasherService</code>. If they do not match, we will return a failure, else we will return the logged in user.</p>
<p>This method will be used by the <code>UserController</code> to create an access token for the user.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; loginUser(LoginUserDto loginUserDto) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> email = loginUserDto.email;
      <span class="hljs-keyword">final</span> userExists = <span class="hljs-keyword">await</span> getUserByEmail(email);
      <span class="hljs-keyword">if</span> (userExists.isLeft) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> ServerException(<span class="hljs-string">'Invalid email or password'</span>);
      }
      <span class="hljs-keyword">final</span> user = userExists.right;
      <span class="hljs-keyword">final</span> password = loginUserDto.password;
      <span class="hljs-keyword">final</span> isPasswordCorrect =
          passwordHasherService.checkPassword(password, user.password);
      <span class="hljs-keyword">if</span> (!isPasswordCorrect) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> ServerException(<span class="hljs-string">'Invalid email or password'</span>);
      }
      <span class="hljs-keyword">return</span> Right(user);
    } <span class="hljs-keyword">catch</span> (e) {
      log(e.toString());
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> Left(
        ServerFailure(
          message: <span class="hljs-string">'Invalid email or password'</span>,
          statusCode: HttpStatus.unauthorized,
        ),
      );
    }
  }
</code></pre>
<h2 id="heading-implementing-createuser-method-1">Implementing <code>createUser</code> method</h2>
<p>Similarly, we will implement <code>createUser</code>. We will check if the user with the given email exists. If the user exists, we will return an failure, else we will create the user and return it.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, User&gt;&gt; createUser(CreateUserDto createUserDto) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> userExists = <span class="hljs-keyword">await</span> getUserByEmail(createUserDto.email);
      <span class="hljs-keyword">if</span> (userExists.isRight) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> ServerException(<span class="hljs-string">'Email already in use'</span>);
      }
      <span class="hljs-comment">// dto is already validated in the controller</span>
      <span class="hljs-comment">// we will hash the password here</span>
      <span class="hljs-keyword">final</span> hashedPassword = passwordHasherService.hashPassword(
        createUserDto.password,
      );
      <span class="hljs-keyword">final</span> user = <span class="hljs-keyword">await</span> dataSource.createUser(
        createUserDto.copyWith(
          password: hashedPassword,
        ),
      );
      <span class="hljs-keyword">return</span> Right(user);
    } <span class="hljs-keyword">on</span> ServerException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(message: e.message),
      );
    }
  }
</code></pre>
<hr />
<h1 id="heading-implementing-jwtservice">Implementing <code>JwtService</code></h1>
<blockquote>
<p>🔐 Securing User Login with JWT 🔒</p>
</blockquote>
<p>Now, once we have all the necessary implementation of the repository and the data source, we will implement <code>JwtService</code> before implementing our <code>UserController</code>. This service will be used to create a JWT token for the user when they log in or signup.</p>
<p>In <code>backend/lib/user/service/jwt_service.dart</code>, we will create a new class called <code>JwtService</code>. We will use <a target="_blank" href="https://pub.dev/packages/dart_jsonwebtoken"><code>dart_jsonwebtoken</code></a> package to create and verify JWT tokens.</p>
<pre><code class="lang-bash">flutter pub add dart_jsonwebtoken
</code></pre>
<p>Now, we will implement the <code>JwtService</code> class.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_jsonwebtoken/dart_jsonwebtoken.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dotenv/dotenv.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JWTService</span> </span>{
  <span class="hljs-keyword">const</span> JWTService(<span class="hljs-keyword">this</span>._env);

  <span class="hljs-keyword">final</span> DotEnv _env;

  <span class="hljs-built_in">String</span> sign(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; payload) {
    <span class="hljs-keyword">final</span> secret = _env[<span class="hljs-string">'JWT_SECRET'</span>]!;
    <span class="hljs-keyword">final</span> jwt = JWT(payload);
    <span class="hljs-keyword">return</span> jwt.sign(SecretKey(secret));
  }

  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; verify(<span class="hljs-built_in">String</span> token) {
    <span class="hljs-keyword">final</span> secret = _env[<span class="hljs-string">'JWT_SECRET'</span>]!;
    <span class="hljs-keyword">final</span> jwt = JWT.verify(token, SecretKey(secret));
    <span class="hljs-keyword">return</span> jwt.payload <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;;
  }
}
</code></pre>
<blockquote>
<p>💡 NOTE: We are using <code>dotenv</code> package to get the <code>JWT_SECRET</code> from the <code>.env</code> file. 💡 Make sure you have added the <code>JWT_SECRET</code> to the <code>.env</code> file.</p>
</blockquote>
<pre><code class="lang-apache">...
<span class="hljs-attribute">JWT_SECRET</span>=verycomplexsupersecret
</code></pre>
<p>Once this is done, we will implement the <code>UserController</code>.</p>
<hr />
<h1 id="heading-tidy-up-time">Tidy Up Time 🧹</h1>
<blockquote>
<p>Make Room for Improvement 🧠</p>
</blockquote>
<h2 id="heading-remove-duplicates">Remove duplicates</h2>
<p>We have multiple routes that are not implemented and are returning the same response. We will remove these routes and create a new <code>Handler</code> to handle these requests.</p>
<p>We will create a new <code>Handler</code> called <code>notAllowedRequestHandler</code> in <code>backend/lib/request_handlers/not_allowed_request_handler.dart</code> to handle these requests.</p>
<pre><code class="lang-dart">Future&lt;Response&gt; notAllowedRequestHandler(RequestContext context) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">return</span> Response.json(
    body: {<span class="hljs-string">'error'</span>: <span class="hljs-string">'👀 Looks like you are lost 🔦'</span>},
    statusCode: HttpStatus.methodNotAllowed,
  );
}
</code></pre>
<p>Then, we will replace the routes with this handler.</p>
<ul>
<li><code>backend/routes/index.dart</code></li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/request_handlers/not_allowed_request_handler.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

Handler onRequest = notAllowedRequestHandler;
</code></pre>
<ul>
<li><p><code>backend/routes/todos/index.dart</code></p>
</li>
<li><p><code>backend/routes/todos/[id].dart</code></p>
</li>
</ul>
<pre><code class="lang-diff"><span class="hljs-addition">+     return notAllowedRequestHandler(context);</span>
<span class="hljs-deletion">-     return Response.json(</span>
<span class="hljs-deletion">-       body: {'error': '👀 Looks like you are lost 🔦'},</span>
<span class="hljs-deletion">-       statusCode: HttpStatus.methodNotAllowed,</span>
<span class="hljs-deletion">-     );</span>
</code></pre>
<h2 id="heading-refactor-httpcontroller">Refactor <code>HttpController</code></h2>
<p>Right now, if we have to implement <code>HttpController</code>, we will have to override all the methods. We will refactor the <code>HttpController</code> to make it optional to override all the methods.</p>
<p>we will create a new handler called <code>unimplementedHandler</code> in <code>backend/lib/request_handlers/unimplemented_handler.dart</code> to handle the unimplemented methods.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

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

Future&lt;Response&gt; unimplementedHandler([RequestContext? context]) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">return</span> Response.json(
    body: {<span class="hljs-string">'error'</span>: <span class="hljs-string">'👀 Not implemented yet'</span>},
    statusCode: HttpStatus.notImplemented,
  );
}
</code></pre>
<p>Then, we will refactor the <code>HttpController</code> to use this handler. In <code>backend/lib/controller/http_controller.dart</code>, we will change the methods to call and return <code>unimplementedHandler</code>.</p>
<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">HttpController</span> </span>{
  FutureOr&lt;Response&gt; index(Request request) =&gt; unimplementedHandler();

  FutureOr&lt;Response&gt; store(Request request) =&gt; unimplementedHandler();

  FutureOr&lt;Response&gt; <span class="hljs-keyword">show</span>(Request request, <span class="hljs-built_in">String</span> id) =&gt; unimplementedHandler();

  FutureOr&lt;Response&gt; update(Request request, <span class="hljs-built_in">String</span> id) =&gt;
      unimplementedHandler();

  FutureOr&lt;Response&gt; destroy(Request request, <span class="hljs-built_in">String</span> id) =&gt;
      unimplementedHandler();
}
</code></pre>
<hr />
<h1 id="heading-implementing-usercontroller">Implementing <code>UserController</code></h1>
<blockquote>
<p>Adding magic with <code>UserController</code> Implementation 🧙‍♀️</p>
</blockquote>
<p>Now, we will implement <code>UserController</code> in <code>user/controller/user_controller.dart</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/controller/http_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/services/jwt_service.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;

<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">HttpController</span> </span>{
  UserController(<span class="hljs-keyword">this</span>._repo, <span class="hljs-keyword">this</span>._jwtService);

  <span class="hljs-keyword">final</span> UserRepository _repo;
  <span class="hljs-keyword">final</span> JWTService _jwtService;

  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; store(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  FutureOr&lt;Response&gt; login(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }
}
</code></pre>
<p>We will override <code>store</code> method, which will responsible for creating and storing the user, and then another <code>login</code> method, to log in the user.</p>
<p>Also, we will create a new private method called <code>_signAndSendToken</code> which will take the user, and then sign and send the user along with the JWT token.</p>
<h2 id="heading-implement-signandsendtoken-method">Implement <code>_signAndSendToken</code> method</h2>
<p>We will use this method once <code>login</code> and <code>store</code> method are successful and when we want to send a response to the user.</p>
<pre><code class="lang-dart">  Response _signAndSendToken(User user, [<span class="hljs-built_in">int?</span> httpStatus]) {
    <span class="hljs-keyword">final</span> token = _jwtService.sign(user.toJson());
    <span class="hljs-keyword">return</span> Response.json(
      body: {
        <span class="hljs-string">'token'</span>: token,
        <span class="hljs-string">'user'</span>: user.toJson()..remove(<span class="hljs-string">'password'</span>),
      },
      statusCode: httpStatus ?? HttpStatus.ok,
    );
  }
</code></pre>
<h2 id="heading-implement-store-method">Implement <code>store</code> method</h2>
<p>We will parse the JSON body, and validate the body with <code>CreateUserDto</code>. Once validated, we will call <code>repository.createUser</code> which will hash the password and create a new user if not already there.</p>
<p>Once this is done, we will call <code>_signAndSendToken</code> and return with <code>201</code> status code.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; store(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> parsedBody = <span class="hljs-keyword">await</span> parseJson(request);
    <span class="hljs-keyword">if</span> (parsedBody.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: parsedBody.left.message},
        statusCode: parsedBody.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> json = parsedBody.right;
    <span class="hljs-keyword">final</span> createTodoDto = CreateUserDto.validated(json);
    <span class="hljs-keyword">if</span> (createTodoDto.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {
          <span class="hljs-string">'message'</span>: createTodoDto.left.message,
          <span class="hljs-string">'errors'</span>: createTodoDto.left.errors,
        },
        statusCode: createTodoDto.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> _repo.createUser(createTodoDto.right);
    <span class="hljs-keyword">return</span> res.fold(
      (left) =&gt; Response.json(
        body: {<span class="hljs-string">'message'</span>: left.message},
        statusCode: left.statusCode,
      ),
      (right) =&gt; _signAndSendToken(right, HttpStatus.created),
    );
  }
</code></pre>
<h2 id="heading-implement-login">Implement <code>login</code></h2>
<p>Similar to <code>store</code>, we will parse and verify <code>LoginUserDto</code>, and call <code>repository.loginUser</code> which will verify the password and return the user.</p>
<pre><code class="lang-dart">  FutureOr&lt;Response&gt; login(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> parsedBody = <span class="hljs-keyword">await</span> parseJson(request);
    <span class="hljs-keyword">if</span> (parsedBody.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: parsedBody.left.message},
        statusCode: parsedBody.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> json = parsedBody.right;
    <span class="hljs-keyword">final</span> loginUserDto = LoginUserDto.validated(json);
    <span class="hljs-keyword">if</span> (loginUserDto.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {
          <span class="hljs-string">'message'</span>: loginUserDto.left.message,
          <span class="hljs-string">'errors'</span>: loginUserDto.left.errors,
        },
        statusCode: loginUserDto.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> _repo.loginUser(loginUserDto.right);
    <span class="hljs-keyword">return</span> res.fold(
      (left) =&gt; Response.json(
        body: {<span class="hljs-string">'message'</span>: left.message},
        statusCode: left.statusCode,
      ),
      _signAndSendToken,
    );
  }
</code></pre>
<hr />
<h1 id="heading-adding-dependency-injections">Adding Dependency Injections 🧰</h1>
<blockquote>
<p>Making sure our ducks are in a row 🦆</p>
</blockquote>
<p>In our global middleware <code>backend/routes/_middleware.dart</code>, we will register the dependencies.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/services/jwt_service.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/services/password_hasher_service.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/user/controller/user_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/user/data_source/user_data_source_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/user/repositories/user_repository_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dotenv/dotenv.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;

<span class="hljs-keyword">final</span> env = DotEnv()..load();
<span class="hljs-keyword">final</span> _db = DatabaseConnection(env);
<span class="hljs-keyword">final</span> _userDs = UserDataSourceImpl(_db);
<span class="hljs-keyword">const</span> _passwordHasher = PasswordHasherService();
<span class="hljs-keyword">final</span> _userRepo = UserRepositoryImpl(_userDs, _passwordHasher);
<span class="hljs-keyword">final</span> _jwtService = JWTService(env);
<span class="hljs-keyword">final</span> _userController = UserController(_userRepo, _jwtService);

Handler middleware(Handler handler) {
  <span class="hljs-keyword">return</span> handler
      .use(requestLogger())
      .use(provider&lt;DatabaseConnection&gt;((_) =&gt; _db))
      .use(provider&lt;JWTService&gt;((_) =&gt; _jwtService))
      .use(provider&lt;UserDataSource&gt;((_) =&gt; _userDs))
      .use(provider&lt;UserRepository&gt;((_) =&gt; _userRepo))
      .use(provider&lt;UserController&gt;((_) =&gt; _userController))
      .use(provider&lt;PasswordHasherService&gt;((_) =&gt; _passwordHasher));
}
</code></pre>
<hr />
<h1 id="heading-setting-up-routes">Setting up routes</h1>
<blockquote>
<p>Connecting the dots 🔗️ - defining API endpoints</p>
</blockquote>
<p>We will now setup the routes for <code>UserController</code> in <code>backend/routes/user</code> we will create three new files <code>index.dart</code>, <code>login.dart</code>, and <code>signup.dart</code>.</p>
<p>Doing this we will have three different routes:</p>
<ul>
<li><p><code>/user</code> - <code>index.dart</code></p>
</li>
<li><p><code>/user/login</code> - <code>login.dart</code></p>
</li>
<li><p><code>/user/signup</code> - <code>signup.dart</code></p>
</li>
</ul>
<h2 id="heading-indexdart"><code>index.dart</code></h2>
<p>This route does nothing. We will just return <code>unimplementedHandler</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/request_handlers/not_allowed_request_handler.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

Handler onRequest = notAllowedRequestHandler;
</code></pre>
<h2 id="heading-logindart"><code>login.dart</code></h2>
<p>We will get the <code>UserController</code> from the <code>RequestContext</code> and call <code>login</code> method for <code>POST</code> requests.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/request_handlers/not_allowed_request_handler.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/user/controller/user_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

FutureOr&lt;Response&gt; onRequest(RequestContext context) {
  <span class="hljs-keyword">final</span> userController = context.read&lt;UserController&gt;();
  <span class="hljs-keyword">if</span> (context.request.method != HttpMethod.post) {
    <span class="hljs-keyword">return</span> notAllowedRequestHandler(context);
  }
  <span class="hljs-keyword">return</span> userController.login(context.request);
}
</code></pre>
<h2 id="heading-signupdart"><code>signup.dart</code></h2>
<p>Similar to <code>login.dart</code>, we will get the <code>UserController</code> from the <code>RequestContext</code> and call <code>store</code> method for <code>POST</code> requests.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/request_handlers/not_allowed_request_handler.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/user/controller/user_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

FutureOr&lt;Response&gt; onRequest(RequestContext context) {
  <span class="hljs-keyword">final</span> userController = context.read&lt;UserController&gt;();
  <span class="hljs-keyword">if</span> (context.request.method != HttpMethod.post) {
    <span class="hljs-keyword">return</span> notAllowedRequestHandler(context);
  }
  <span class="hljs-keyword">return</span> userController.store(context.request);
}
</code></pre>
<blockquote>
<p>💡 NOTE: Recheck your <code>.env</code> file before testing the routes.</p>
</blockquote>
<hr />
<h1 id="heading-testing-routes">Testing routes</h1>
<blockquote>
<p>Testing Time 🧪 - Verify Your Routes!</p>
</blockquote>
<p>Once we have set up the routes, we can test them using <code>curl</code> or <code>postman</code>.</p>
<h2 id="heading-userslogin"><code>/users/login</code></h2>
<h3 id="heading-when-the-email-and-password-match">When the email and password match</h3>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/users/login'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "email":"saileshbro@gmail.com",
    "password":"6aMj@UBByu%7BzN^C9tMe#Te4b!4cJrXwwFi#HgKrQ&amp;g&amp;ddNN6eHQ94vd5SuJtEc%7^H6L^xews8soG@R7GnW*RvfJVMaKEuBXNtVtbP5!3^qs*n!Z%87q8eRJmKFUHg"
}'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{
  <span class="hljs-attr">"token"</span>: <span class="hljs-string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjQ4ODY5Yjc4LWEwMjgtNGMwNS1hN2QzLTQ5OTQ2Mzk4NDI2NSIsIm5hbWUiOiJTYWlsZXNoIERhaGFsIiwiZW1haWwiOiJzYWlsZXNoQGdtYWlsLmNvbSIsImNyZWF0ZWRfYXQiOiIyMDIzLTAxLTMwVDA5OjU3OjQ5LjExODM2MVoiLCJwYXNzd29yZCI6IiQyYSQxMCRGSDVDc2N1Y0VuLlNwWlZmSWFtRGwuZzRMaDFhd2ltbVRMS05yU2NORW1qT25lSFZHOE4wLiIsImlhdCI6MTY3NTA2MDA0MH0.qvzLWgAEphlYqZztoBf7Bvag6hO1qkp44hUwl78CMVo"</span>,
  <span class="hljs-attr">"user"</span>: {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"48869b78-a028-4c05-a7d3-499463984265"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Sailesh Dahal"</span>,
    <span class="hljs-attr">"email"</span>: <span class="hljs-string">"saileshbro@gmail.com"</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2023-01-30T09:57:49.118361Z"</span>
  }
}
</code></pre>
<h3 id="heading-when-the-email-or-password-does-not-match">When the email or password does not match.</h3>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/users/login'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "email":"sa@gmail.com",
    "name":"Sailesh Dahal",
    "password":"6aMj@UBByu%7BzN^C9tMe#Te4b!4cJrXwwFi#HgKrQ&amp;g&amp;ddNN6eHQ94vd5SuJtEc%7^H6L^xews8soG@R7GnW*RvfJVMaKEuBXNtVtbP5!3^qs*n!Z%87q8eRJmKFUHg"
}'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{ <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Invalid email or password"</span> }
</code></pre>
<h2 id="heading-userssignup"><code>/users/signup</code></h2>
<h3 id="heading-when-the-data-is-not-valid">When the data is not valid</h3>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/users/signup'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "email":"sailes@gmail.com",
    "password":"6aMj@UBByu"
}'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Validation failed"</span>,
  <span class="hljs-attr">"errors"</span>: {
    <span class="hljs-attr">"name"</span>: [<span class="hljs-string">"Name is required"</span>]
  }
}
</code></pre>
<h3 id="heading-when-there-is-no-user-with-the-given-email-it-will-create-a-new-user-and-return-the-same-response-as-userslogin">When there is no user with the given email, it will create a new user and return the same response as <code>/users/login</code>.</h3>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/users/signup'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "email":"sailesh12@gmail.com",
    "name":"Sailesh Dahal",
    "password":"6aMj123"
}'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{
  <span class="hljs-attr">"token"</span>: <span class="hljs-string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImM1ODE2YWQ0LTNkODctNDUzZC1hZmFkLTUwMzljY2YxZjdjNyIsIm5hbWUiOiJTYWlsZXNoIERhaGFsIiwiZW1haWwiOiJzYWlsZXNoMTJAZ21haWwuY29tIiwiY3JlYXRlZF9hdCI6IjIwMjMtMDEtMzBUMDY6Mjg6MzUuMzkyMjM4WiIsInBhc3N3b3JkIjoiJDJhJDEwJHRuTllGM3FHQmlPT1dKeHVacm1MaU91SHhBMU1HQlNMcmoxYS5ndHY2TTNCMHpmRW5Dc1dLIiwiaWF0IjoxNjc1MDYwMTE0fQ.aUpo9HXTFmdmkTsfGoTp0mcK0OJ_fFuwZArqyNFpKvQ"</span>,
  <span class="hljs-attr">"user"</span>: {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"c5816ad4-3d87-453d-afad-5039ccf1f7c7"</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Sailesh Dahal"</span>,
    <span class="hljs-attr">"email"</span>: <span class="hljs-string">"sailesh12@gmail.com"</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2023-01-30T06:28:35.392238Z"</span>
  }
}
</code></pre>
<h3 id="heading-when-there-is-a-user-with-the-given-email-it-will-return-the-following-response">When there is a user with the given email, it will return the following response.</h3>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/users/signup'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "email":"sailesh12@gmail.com",
    "name":"Sailesh Dahal",
    "password":"6aMj123"
}'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Email already in use"</span>
}
</code></pre>
<hr />
<h1 id="heading-protecting-routes">Protecting Routes</h1>
<blockquote>
<p>Locking Down: Secure Your Endpoints 🔒</p>
</blockquote>
<p>Now, we can protect our routes by adding middleware to the routes. We will check for the <code>Authorization</code> header and verify the token. We can intercept all the requests going to <code>/todos/*</code> and check for the token.</p>
<p>We can do this by creating a new authorization middleware. We will create a new file <code>backend/lib/middlewares/authorization_middleware.dart</code>. Before that, let's create a new exception for unauthorized requests.</p>
<h2 id="heading-creating-unauthorizedexception">Creating <code>UnauthorizedException</code></h2>
<p>When the token is not valid, we will throw an <code>UnauthorizedException</code> which will be handled by the <code>ExceptionHandlerMiddleware</code>.</p>
<p>In <code>exceptions</code> package, we will create a new exception called <code>UnauthorizedException</code> in <code>exceptions/lib/src/http_exception/unauthorized_exception.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:exceptions/src/http_exception/http_exception.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UnauthorizedException</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">HttpException</span> </span>{
  <span class="hljs-keyword">const</span> UnauthorizedException({
    <span class="hljs-built_in">String</span> message = <span class="hljs-string">'Unauthorized'</span>,
    <span class="hljs-keyword">this</span>.errors = <span class="hljs-keyword">const</span> {},
  }) : <span class="hljs-keyword">super</span>(message, HttpStatus.unauthorized);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt; errors;
}
</code></pre>
<blockquote>
<p>💡 Make sure to export the exception in <code>http_exception.dart</code> file.</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">export</span> <span class="hljs-string">'./bad_request_exception.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'./not_found_exception.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'./unauthorized_exception.dart'</span>;
</code></pre>
<blockquote>
<p>💡 Make sure to run <code>flutter pub run build_runner build</code> to generate the code.</p>
</blockquote>
<h2 id="heading-creating-authorizationmiddleware">Creating <code>AuthorizationMiddleware</code></h2>
<blockquote>
<p>🔒 Securing Endpoints with AuthorizationMiddleware 🛡️</p>
</blockquote>
<p>Now, we can create the <code>AuthorizationMiddleware</code> in <code>backend/lib/middlewares/authorization_middleware.dart</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/services/jwt_service.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/controller/todo_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/data_source/todo_data_source_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/repositories/todo_repository_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:exceptions/exceptions.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;

Handler authorizationMiddleware(Handler handler) {
  <span class="hljs-keyword">return</span> (context) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> request = context.request;
      <span class="hljs-keyword">final</span> authHeader = request.headers[HttpHeaders.authorizationHeader] ?? <span class="hljs-string">''</span>;
      <span class="hljs-keyword">final</span> token = authHeader.replaceFirst(<span class="hljs-string">'Bearer '</span>, <span class="hljs-string">''</span>);
      <span class="hljs-keyword">if</span> (token.isEmpty) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> UnauthorizedException();
      <span class="hljs-keyword">final</span> jwtService = context.read&lt;JWTService&gt;();
      <span class="hljs-keyword">final</span> decoded = jwtService.verify(token);
      <span class="hljs-keyword">final</span> decodedUser = User.fromJson(decoded);
      <span class="hljs-keyword">final</span> userRepo = context.read&lt;UserRepository&gt;();
      <span class="hljs-keyword">final</span> user = <span class="hljs-keyword">await</span> userRepo.getUserById(decodedUser.id);
      <span class="hljs-keyword">if</span> (user.isLeft) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> UnauthorizedException();
      context = _handleAuthDependencies(context, user.right);
      <span class="hljs-keyword">return</span> handler(context);
    } <span class="hljs-keyword">on</span> UnauthorizedException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: e.message},
        statusCode: e.statusCode,
      );
    }
  };
}

RequestContext _handleAuthDependencies(
  RequestContext context,
  User user,
) {
  <span class="hljs-keyword">late</span> RequestContext updatedContext;
  updatedContext = context.provide&lt;User&gt;(() =&gt; user);
  <span class="hljs-keyword">return</span> updatedContext;
}
</code></pre>
<p>Here, we are checking for the <code>Authorization</code> header and verifying the token. If the token is valid, we are adding the <code>User</code> object to the context, so that we can get the logged-in user later from the provider.</p>
<blockquote>
<p>If the token is invalid, we will return a <code>401</code> response.</p>
</blockquote>
<h2 id="heading-updating-auth-dependencies">Updating auth dependencies</h2>
<p>Now, since we will have to add a <code>user_id</code> whenever, we will create a todo, we will have to pass the logged-in user to the TodoDataSource. We will update the <code>TodoDataSourceImpl</code> to accept the <code>User</code> object from the constructor.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoDataSourceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TodoDataSource</span> </span>{
  <span class="hljs-comment">/// <span class="markdown">{@macro todo<span class="hljs-emphasis">_data_</span>source<span class="hljs-emphasis">_impl}</span></span></span>
  <span class="hljs-keyword">const</span> TodoDataSourceImpl(<span class="hljs-keyword">this</span>._databaseConnection, <span class="hljs-keyword">this</span>.user);
  <span class="hljs-keyword">final</span> DatabaseConnection _databaseConnection;

  <span class="hljs-keyword">final</span> User user;
}
</code></pre>
<p>Also, we will update <code>createTodo</code> and <code>updateTodo</code> methods to add the <code>user_id</code>.</p>
<h3 id="heading-update-createtodo-method">Update <code>createTodo</code> method</h3>
<pre><code class="lang-diff">  @override
  Future&lt;Todo&gt; createTodo(CreateTodoDto todo) async {
    try {
      await _databaseConnection.connect();
      final result = await _databaseConnection.db.query(
        '''
<span class="hljs-deletion">-       INSERT INTO todos (title, description, completed)</span>
<span class="hljs-addition">+       INSERT INTO todos (title, description, completed, user_id)</span>
<span class="hljs-deletion">-       VALUES (@title, @description, @completed) RETURNING *</span>
<span class="hljs-addition">+       VALUES (@title, @description, @completed, @user_id) RETURNING *</span>
        ''',
        substitutionValues: {
          'title': todo.title,
          'description': todo.description,
          'completed': false,
<span class="hljs-addition">+         'user_id': _user.id,</span>
        },
      );
      if (result.affectedRowCount == 0) {
        throw const ServerException('Failed to create todo');
      }
      final todoMap = result.first.toColumnMap();
      return Todo(
        id: todoMap['id'] as int,
<span class="hljs-addition">+       userId: todoMap['user_id'] as String,</span>
        title: todoMap['title'] as String,
        description: todoMap['description'] as String,
        createdAt: todoMap['created_at'] as DateTime,
      );
    } on PostgreSQLException catch (e) {
      throw ServerException(e.message ?? 'Unexpected error');
    } finally {
      await _databaseConnection.close();
    }
  }
</code></pre>
<h3 id="heading-update-updatetodo-method">Update <code>updateTodo</code> method</h3>
<pre><code class="lang-diff">      final result = await _databaseConnection.db.query(
        '''
        UPDATE todos
        SET title = COALESCE(@new_title, title),
            description = COALESCE(@new_description, description),
            completed = COALESCE(@new_completed, completed),
            updated_at = current_timestamp
        WHERE id = @id
<span class="hljs-addition">+       AND user_id = @user_id</span>
        RETURNING *
        ''',
        substitutionValues: {
          'id': id,
<span class="hljs-addition">+         'user_id': _user.id,</span>
          'new_title': todo.title,
          'new_description': todo.description,
          'new_completed': todo.completed,
        },
      );
</code></pre>
<h3 id="heading-update-todo-model-to-add-userid">Update <code>Todo</code> model to add <code>userId</code></h3>
<p>We will also add the missing <code>userId</code> from <code>Todo</code> model.</p>
<pre><code class="lang-diff">  factory Todo({
    required TodoId id,
<span class="hljs-addition">+   required UserId userId,</span>
    required String title,
    @Default('') String description,
    @Default(false) bool completed,
    @DateTimeConverter() required DateTime createdAt,
    @DateTimeConverterNullable() DateTime? updatedAt,
  }) = _Todo;
</code></pre>
<p>Now, once this is done, we will update the <code>_handleAuthDependencies</code> method in <code>authorization_middleware.dart</code> as</p>
<pre><code class="lang-dart">RequestContext _handleAuthDependencies(
  RequestContext context,
  User user,
) {
  <span class="hljs-keyword">final</span> db = context.read&lt;DatabaseConnection&gt;();
  <span class="hljs-keyword">final</span> todoDs = TodoDataSourceImpl(db, user);
  <span class="hljs-keyword">final</span> todoRepo = TodoRepositoryImpl(todoDs);
  <span class="hljs-keyword">final</span> todoController = TodoController(todoRepo);
  <span class="hljs-keyword">late</span> RequestContext updatedContext;
  updatedContext = context.provide&lt;User&gt;(() =&gt; user);
  updatedContext = updatedContext.provide&lt;TodoController&gt;(() =&gt; todoController);
  updatedContext = updatedContext.provide&lt;TodoRepository&gt;(() =&gt; todoRepo);
  updatedContext = updatedContext.provide&lt;TodoDataSource&gt;(() =&gt; todoDs);
  <span class="hljs-keyword">return</span> updatedContext;
}
</code></pre>
<h2 id="heading-using-authorizationmiddleware">Using <code>AuthorizationMiddleware</code></h2>
<blockquote>
<p>Applying <code>AuthorizationMiddleware</code> to routes</p>
</blockquote>
<p>Let's create a new middleware in <code>backend/routes/todos/_middleware.dart</code> and add the following code.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/middlewares/authorization_middleware.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

Handler middleware(Handler handler) =&gt; authorizationMiddleware(handler);
</code></pre>
<p>Since this middleware lies inside <code>routes/todos</code>, all the <code>/todos/*</code> routes will be intercepted by this middleware. This means that all the <code>/todos/*</code> routes will be protected and if the user tries to access these routes without a valid token, it will return a <code>401</code> response.</p>
<hr />
<h1 id="heading-testing-protected-todos-routes">Testing protected <code>/todos/*</code> routes</h1>
<blockquote>
<p>Putting updated routes to the Test 🧪</p>
</blockquote>
<p>If we try to access the <code>/todos/*</code> routes without a valid token, it will return a <code>401</code> response. We can test this by running the following command.</p>
<h2 id="heading-get-todos">GET /todos</h2>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X GET <span class="hljs-string">'http://localhost:8080/todos'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Unauthorized"</span>
}
</code></pre>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X GET <span class="hljs-string">'http://localhost:8080/todos'</span> -H <span class="hljs-string">'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY5ZWFkZTg3LWRhNWUtNDRjZC1iNTRlLTQ3NGE4NWQ4ZGVlNCIsIm5hbWUiOiJTYWlsZXNoIERhaGFsIiwiZW1haWwiOiJzYWlsZXNoYnJvQGdtYWlsLmNvbSIsImNyZWF0ZWRfYXQiOiIyMDIzLTAxLTIzVDIyOjU0OjM4Ljg2NDkxMloiLCJwYXNzd29yZCI6IiQyYSQxMCR6VjBTZlI3cUpHOHlCelJWWThtNkRlMWo0UUtHL2VRdXl6NzduQ1lBL1luZENtb1ZSbzB0RyIsImlhdCI6MTY3NDQ5Mzc3OX0.W36mHXKFrxZDhz0Hrxt0Cmrz3WNVexiAZe2KAjrWMaE'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">76</span>,
    <span class="hljs-attr">"user_id"</span>: <span class="hljs-string">"69eade87-da5e-44cd-b54e-474a85d8dee4"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"this is a title asdf"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Not so great description asdf"</span>,
    <span class="hljs-attr">"completed"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2023-01-23T22:56:31.686023Z"</span>,
    <span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2023-01-30T04:08:06.349549Z"</span>
  }
]
</code></pre>
<h2 id="heading-post-todos">POST /todos</h2>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/todos'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "title":"this is a title asdf",
    "description":"Not so great description asdf"
}'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{
  <span class="hljs-attr">"message"</span>: <span class="hljs-string">"Unauthorized"</span>
}
</code></pre>
<ul>
<li><code>REQUEST</code></li>
</ul>
<pre><code class="lang-bash">curl -s -L -X POST <span class="hljs-string">'http://localhost:8080/todos'</span> -H <span class="hljs-string">'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY5ZWFkZTg3LWRhNWUtNDRjZC1iNTRlLTQ3NGE4NWQ4ZGVlNCIsIm5hbWUiOiJTYWlsZXNoIERhaGFsIiwiZW1haWwiOiJzYWlsZXNoYnJvQGdtYWlsLmNvbSIsImNyZWF0ZWRfYXQiOiIyMDIzLTAxLTIzVDIyOjU0OjM4Ljg2NDkxMloiLCJwYXNzd29yZCI6IiQyYSQxMCR6VjBTZlI3cUpHOHlCelJWWThtNkRlMWo0UUtHL2VRdXl6NzduQ1lBL1luZENtb1ZSbzB0RyIsImlhdCI6MTY3NDQ5Mzc3OX0.W36mHXKFrxZDhz0Hrxt0Cmrz3WNVexiAZe2KAjrWMaE'</span> -H <span class="hljs-string">'Content-Type: application/json'</span> --data-raw <span class="hljs-string">'{
    "title":"this is a title asdf",
    "description":"Not so great description asdf"
}'</span>
</code></pre>
<ul>
<li><code>RESPONSE</code></li>
</ul>
<pre><code class="lang-json">{
  <span class="hljs-attr">"id"</span>: <span class="hljs-number">79</span>,
  <span class="hljs-attr">"user_id"</span>: <span class="hljs-string">"69eade87-da5e-44cd-b54e-474a85d8dee4"</span>,
  <span class="hljs-attr">"title"</span>: <span class="hljs-string">"this is a title asdf"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Not so great description asdf"</span>,
  <span class="hljs-attr">"completed"</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2023-01-30T07:10:10.102358Z"</span>,
  <span class="hljs-attr">"updated_at"</span>: <span class="hljs-literal">null</span>
}
</code></pre>
<p>Similarly, we can test the other routes as well 🥳</p>
<hr />
<p>🎉 Congrats, we've made it to the end of Part 6! 🚀 We've successfully added user authentication with JWT to our <a target="_blank" href="https://dartfrog.vgv.dev/">dart_frog</a> backend.</p>
<p>It's been an incredible adventure, and we've gone a long way since Part 1. Using Dart and Flutter, we created a full-stack to-do application. But our task does not end there. We haven't addressed testing yet in this course, but it's an important aspect of developing any program. Let us know in the comments if you'd want to learn more about testing your code in all of the modules we've written in this series.</p>
<p>As always, you can refer back to the GitHub repo for this tutorial at <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart">https://github.com/saileshbro/full_stack_todo_dart</a> if you need any help. Don't forget to give it a ⭐ and help spread the word about the initiative. Also, if you get stuck or want assistance, please submit an issue or, better yet, contribute a pull request.</p>
<p>Thank you for joining me on this journey; let's create even more wonderful applications together. Have fun coding! 💻</p>
]]></content:encoded></item><item><title><![CDATA[🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 5]]></title><description><![CDATA[In the previous part of this series, we set up the foundation for our full-stack to-do application by creating models, data sources, repositories, exceptions and handling failures. In this part, we will take it to the next level by:

Scaffolding an e...]]></description><link>https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-5</link><guid isPermaLink="true">https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-5</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[full stack]]></category><category><![CDATA[Dart]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Fri, 13 Jan 2023 05:25:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673585810829/ff89d125-8676-4352-b825-81b0f75dced0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous part of this series, we set up the foundation for our full-stack to-do application by creating models, data sources, repositories, exceptions and handling failures. In this part, we will take it to the next level by:</p>
<ul>
<li><p>Scaffolding an empty <a target="_blank" href="https://flutter.dev"><code>flutter</code></a> project</p>
</li>
<li><p>Importing necessary dependencies</p>
</li>
<li><p>Creating a folder structure</p>
</li>
<li><p>Connecting our front end with the backend using <code>TodosHttpClient</code></p>
</li>
<li><p>Setting up dependency injection using <a target="_blank" href="https://pub.dev/packages/injectable"><code>injectable</code></a> and <a target="_blank" href="https://pub.dev/packages/get_it"><code>get_it</code></a></p>
</li>
<li><p>Implementing <code>TodosRemoteDataSource</code> and <code>TodoRepository</code></p>
</li>
<li><p>Handling errors as <code>NetworkFailure</code></p>
</li>
<li><p>Creating <code>NetworkException</code> and <code>DioNetworkException</code></p>
</li>
<li><p>Creating <code>NetworkErrorInterceptor</code></p>
</li>
<li><p>Creating <code>ShowTodosView</code> and <code>ShowTodosViewModel</code> to show the fetched todos</p>
</li>
<li><p>Creating <code>MaintainTodoView</code> and <code>MaintainTodoViewModel</code> to maintain the todos</p>
</li>
<li><p>Adding linting with <a target="_blank" href="https://pub.dev/packages/very_good_analysis"><code>very_good_analysis</code></a> to make sure our code is clean.</p>
</li>
</ul>
<p>We will be diving deeper into the world of flutter making use of the best practices, packages and libraries available for flutter. And by the end of this part, we will have a fully functional front end for our to-do application.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/shorts/7XJHrpCpzUo?feature=share">https://www.youtube.com/shorts/7XJHrpCpzUo?feature=share</a></div>
<p> </p>
<p>Don't forget to check the GitHub repo for this tutorial at <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart">GitHub</a> if you need a little help along the way. Let's get started 🚀</p>
<h1 id="heading-scaffolding-frontend">🚀 Scaffolding Frontend</h1>
<blockquote>
<p>🌟 Frontend is about to shine.</p>
</blockquote>
<p>We will begin by scaffolding an empty Flutter project using the command:</p>
<pre><code class="lang-bash">flutter create --project-name fullstack_todo --org np.com.saileshdahal frontend
</code></pre>
<p>This will create an empty flutter project in <code>frontend</code> directory. We will then do some initial setup, and add some dependencies.</p>
<h2 id="heading-importing-necessary-dependencies">Importing necessary dependencies 📦</h2>
<blockquote>
<p>Time to bring in the big guns! 💪 Let's import those dependencies</p>
</blockquote>
<p>We will import the necessary dependencies in the <code>pubspec.yaml</code> file. The dependencies that we are using are as follows.</p>
<ul>
<li><p><a target="_blank" href="https://pub.dev/packages/dio"><code>dio</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/either_dart"><code>either_dart</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/flutter_hooks"><code>flutter_hooks</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/get_it"><code>get_it</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/injectable"><code>injectable</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/stacked"><code>stacked</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/stacked_services"><code>stacked_services</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/build_runner"><code>build_runner</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/very_good_analysis"><code>very_good_analysis</code></a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/retrofit"><code>retrofit</code></a></p>
</li>
</ul>
<p>These dependencies will allow us to easily handle communication with the backend, handle state management, and implement a clean and maintainable codebase for our application.</p>
<p>To install you can use the following command:</p>
<pre><code class="lang-bash">flutter pub add dio either_dart flutter_hooks get_it injectable stacked stacked_services retrofit

flutter pub add --dev build_runner very_good_analysis stacked_generator injectable_generator retrofit_generator
</code></pre>
<p>We will also include the packages that we have built, once done <code>pubspec.yaml</code> should look something like this.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">data_source:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../data_source</span>
  <span class="hljs-attr">dio:</span> <span class="hljs-string">^4.0.6</span>
  <span class="hljs-attr">either_dart:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">exceptions:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../exceptions</span>
  <span class="hljs-attr">failures:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../failures</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">flutter_hooks:</span> <span class="hljs-string">^0.18.5+1</span>
  <span class="hljs-attr">get_it:</span> <span class="hljs-string">^7.2.0</span>
  <span class="hljs-attr">injectable:</span> <span class="hljs-string">^2.1.0</span>
  <span class="hljs-attr">models:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../models</span>
  <span class="hljs-attr">repository:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../repository</span>
  <span class="hljs-attr">retrofit:</span> <span class="hljs-string">^3.3.1</span>
  <span class="hljs-attr">stacked:</span> <span class="hljs-string">^3.0.1</span>
  <span class="hljs-attr">stacked_services:</span> <span class="hljs-string">^0.9.9</span>
  <span class="hljs-attr">typedefs:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../typedefs</span>

<span class="hljs-attr">dev_dependencies:</span>
  <span class="hljs-attr">build_runner:</span> <span class="hljs-string">^2.3.3</span>
  <span class="hljs-attr">flutter_test:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">injectable_generator:</span> <span class="hljs-string">^2.1.3</span>
  <span class="hljs-attr">integration_test:</span>
    <span class="hljs-attr">sdk:</span> <span class="hljs-string">flutter</span>
  <span class="hljs-attr">mockito:</span> <span class="hljs-string">^5.3.2</span>
  <span class="hljs-attr">retrofit_generator:</span> <span class="hljs-string">^4.2.0</span>
  <span class="hljs-attr">stacked_generator:</span> <span class="hljs-string">^0.8.3</span>
  <span class="hljs-attr">very_good_analysis:</span> <span class="hljs-string">^3.1.0</span>
</code></pre>
<h2 id="heading-folder-structure">Folder Structure 📂</h2>
<p>This is how we will architect our folder structures.</p>
<pre><code class="lang-apache">.
├── <span class="hljs-attribute">constants</span>
├── <span class="hljs-attribute">core</span>
│   ├── <span class="hljs-attribute">app</span>
│   ├── <span class="hljs-attribute">di</span>
│   └── <span class="hljs-attribute">network</span>
│       ├── <span class="hljs-attribute">exceptions</span>
│       └── <span class="hljs-attribute">interceptors</span>
├── <span class="hljs-attribute">data</span>
│   ├── <span class="hljs-attribute">data_source</span>
│   │   ├── <span class="hljs-attribute">todo_http_client</span>
│   │   └── <span class="hljs-attribute">todo_remote_data_source</span>
│   └── <span class="hljs-attribute">repositories</span>
├── <span class="hljs-attribute">data_services</span>
└── <span class="hljs-attribute">presentation</span>
</code></pre>
<ul>
<li><p><code>constants</code>: This folder will include all the hard-coded constants in our project, such as API endpoints or keys.</p>
</li>
<li><p><code>core</code>: This directory will include all the core functionalities of our app, which are common for all the features. This includes routing, dependency injection, and network interceptors.</p>
</li>
<li><p><code>data</code>: This directory will include all the implementations related to data sources and repositories.</p>
</li>
<li><p><code>data_services</code>: This will include the implementation of services that will be shared across different view models.</p>
</li>
<li><p><code>presentation</code>: This will include the views and view models for creating and listing</p>
</li>
</ul>
<p>The <code>data_source</code> and <code>repositories</code> folders contain the implementation of the abstract contracts defined in the <code>data_sources</code> and <code>repository</code> packages, respectively. This allows for the separation of concerns and easier maintenance of the codebase.</p>
<h1 id="heading-connecting-with-the-backend">Connecting with the backend 🔌</h1>
<blockquote>
<p>Let's get this party started 🎉</p>
</blockquote>
<p>Let's start by connecting with the <a target="_blank" href="https://pub.dev/packages/dart_frog">dart_frog</a> backend using <a target="_blank" href="https://pub.dev/packages/dio">dio</a>. For this, we will create a new class <code>TodosHttpClient</code></p>
<h2 id="heading-create-todoshttpclient">Create <code>TodosHttpClient</code></h2>
<p><code>TodosHttpClient</code> will be responsible for calling our backend. We will be using <a target="_blank" href="https://pub.dev/packages/retrofit"><code>retrofit</code></a> to generate the necessary codes to call the backend.</p>
<p>Retrofit allows us to define our API endpoints as interfaces with annotated methods, and it will automatically generate the necessary code to handle the network requests and convert the JSON responses to Dart objects.</p>
<p>We will create a new file in <code>data/data_source/todo_http_client/todos_http_client.dart</code> with the following code.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:retrofit/retrofit.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'todos_http_client.g.dart'</span>;

<span class="hljs-meta">@RestApi</span>()
<span class="hljs-meta">@lazySingleton</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodosHttpClient</span> </span>{
  <span class="hljs-meta">@factoryMethod</span>
  <span class="hljs-keyword">factory</span> TodosHttpClient(Dio _dio) = _TodosHttpClient;

  <span class="hljs-meta">@GET</span>(<span class="hljs-string">'/todos'</span>)
  Future&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt; getAllTodo();
  <span class="hljs-meta">@GET</span>(<span class="hljs-string">'/todos/{id}'</span>)
  Future&lt;Todo&gt; getTodoById(<span class="hljs-meta">@Path</span>(<span class="hljs-string">'id'</span>) TodoId id);
  <span class="hljs-meta">@POST</span>(<span class="hljs-string">'/todos'</span>)
  Future&lt;Todo&gt; createTodo(<span class="hljs-meta">@Body</span>() CreateTodoDto todo);
  <span class="hljs-meta">@PATCH</span>(<span class="hljs-string">'/todos/{id}'</span>)
  Future&lt;Todo&gt; updateTodo(<span class="hljs-meta">@Path</span>(<span class="hljs-string">'id'</span>) TodoId id, <span class="hljs-meta">@Body</span>() UpdateTodoDto todo);
  <span class="hljs-meta">@DELETE</span>(<span class="hljs-string">'/todos/{id}'</span>)
  Future&lt;<span class="hljs-keyword">void</span>&gt; deleteTodoById(<span class="hljs-meta">@Path</span>(<span class="hljs-string">'id'</span>) TodoId id);
}
</code></pre>
<p>After completing the steps, we will execute <a target="_blank" href="https://pub.dev/packages/build_runner"><code>build_runner</code></a> to generate the necessary code. This will create an implementation of <code>TodosHttpClient</code> and we will link the generated <code>_TodosHttpClient</code> to the factory method of this abstract class. The Dio object will be passed as a dependency through the constructor. Since we are using <a target="_blank" href="https://pub.dev/packages/get_it"><code>get_it</code></a> and <a target="_blank" href="https://pub.dev/packages/injectable"><code>injectable</code></a>, it will be necessary to manage the injection of this dependency.</p>
<h2 id="heading-setup-injectable">Setup <code>injectable</code> 🔧</h2>
<p>Injectable is a package for Dart that allows for dependency injection in our application. It provides a simple and easy-to-use API for managing dependencies and injecting them into our classes. By using Injectable, we can easily swap out dependencies for different environments, like testing, and make our code more modular and testable.</p>
<blockquote>
<p>👉 Read more about <a target="_blank" href="https://pub.dev/packages/injectable#registering-third-party-types"><code>injectable</code></a>.</p>
</blockquote>
<p>To handle how we inject the <code>Dio</code> instance, we will create some files to inject third-party modules. In the <code>core</code> directory, we will create a new directory called <code>di</code>, short for Dependency Injection.</p>
<p>Here, create a new file called <code>locator.dart</code>, and add the following code.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/core/di/locator.config.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:get_it/get_it.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;

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

<span class="hljs-meta">@injectableInit</span>
<span class="hljs-keyword">void</span> setupLocator() =&gt; locator.init();
</code></pre>
<p>Now, we will create another file called <code>third_party_modules.dart</code>, this file will have a getter which will resolve the instance of <code>Dio</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;

<span class="hljs-meta">@module</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThirdPartyModules</span> </span>{
  <span class="hljs-meta">@lazySingleton</span>
  Dio <span class="hljs-keyword">get</span> dio =&gt; Dio(BaseOptions(baseUrl: kBaseUrl));
}
</code></pre>
<p>Then, we will create the missing <code>kBaseUrl</code> constant in <code>constants/constants.dart</code> file.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">const</span> <span class="hljs-built_in">String</span> kBaseUrl = <span class="hljs-string">'http://localhost:8080'</span>;
</code></pre>
<blockquote>
<p>Note: The port for you may be different, please check what port your backend will run by running <code>dart_frog dev</code> in the <code>backend</code> folder.</p>
</blockquote>
<p>If you check the generated code in <code>locator.config.dart</code>, it creates the instance of <code>dio</code> and passes it to our <code>TodosHttpClient</code>.</p>
<h2 id="heading-implement-todosremotedatasource">Implement <code>TodosRemoteDataSource</code> 💻</h2>
<p>We will now use <code>TodosHttpClient</code> to implement our <code>TodosDataSource</code>. For this, we will create <code>data_source/todos_remote_data_source.dart</code> file, and we will implement <code>TodosDataSource</code> as follows.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/data/data_source/todos_http_client/todos_http_client.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-meta">@LazySingleton</span>(<span class="hljs-keyword">as</span>: TodoDataSource)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodosRemoteDataSource</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TodoDataSource</span> </span>{
  <span class="hljs-keyword">const</span> TodosRemoteDataSource(<span class="hljs-keyword">this</span>.httpClient);

  <span class="hljs-keyword">final</span> TodosHttpClient httpClient;

  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; createTodo(CreateTodoDto todo) =&gt; httpClient.createTodo(todo);

  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; deleteTodoById(TodoId id) =&gt; httpClient.deleteTodoById(id);

  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt; getAllTodo() =&gt; httpClient.getAllTodo();

  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; getTodoById(TodoId id) =&gt; httpClient.getTodoById(id);

  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; updateTodo({<span class="hljs-keyword">required</span> TodoId id, <span class="hljs-keyword">required</span> UpdateTodoDto todo}) =&gt;
      httpClient.updateTodo(id, todo);
}
</code></pre>
<p>Here, we are passing the <code>TodosHttpClient</code> as a dependency. This will help us to test the implementation by passing a mock implementation of <code>HttpClient</code> to the data source implementation.</p>
<h2 id="heading-handling-errors-as-networkfailure">Handling errors as <code>NetworkFailure</code> 🛑</h2>
<p>We need to handle the errors that we send from the backend as failures, and other possible errors as well. One possible way to do this is, when we implement our <code>TodoRepository</code>, we will catch all the exceptions as <code>NetworkException</code>, and then resolve the exception to the <code>Failure</code> and handle the failures in the UI.</p>
<p>The way we do this is can create an <code>interceptor</code>, that we can plug into our <code>dio</code> instance, and then handle the errors from there.</p>
<p>By creating a new <code>NetworkException</code> class in our <code>exceptions</code> package, we will be able to effectively handle network errors that may occur while communicating with the backend. This is especially important when implementing the <code>TodoRepository</code> class in the front end, as it ensures that any network errors that occur are caught and handled in a consistent and efficient manner.</p>
<p>However, it is important to note that when working with APIs, we will encounter errors that are returned in the form of <code>DioError</code>. To handle these errors, we will use an <code>ErrorInterceptorHandler</code> to continue the Dio request-response cycle, while also rejecting any errors that occur.</p>
<p>To ensure that we are able to catch <code>NetworkException</code> in the repository, we will create a new class called <code>DioNetworkException</code> which extends <code>DioError</code> and implements <code>NetworkException</code>. This allows us to create a new instance of <code>DioNetworkException</code> and reject it from the handler.</p>
<h2 id="heading-create-networkexception">Create <code>NetworkException</code> 🌐</h2>
<p>In our <code>exceptions</code> package, we will create a new exception called <code>NetworkExeption</code>. We are doing this because we don't want any external dependencies on our packages, as much as we can. Let's create a new file <code>exceptions/lib/src/network_exception/network_exception.dart</code> in <code>exceptions</code> package with the following contents.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NetworkException</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Exception</span> </span>{
  NetworkException({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.message,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.statusCode,
    <span class="hljs-keyword">this</span>.errors = <span class="hljs-keyword">const</span> {},
  });

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> message;

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> statusCode;

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt; errors;
}
</code></pre>
<blockquote>
<p>👉 Make sure to export this from <code>exceptions.dart</code> library.</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> exceptions;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/http_exception/http_exception.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/network_exception/network_exception.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/server_exception/server_exception.dart'</span>;
</code></pre>
<p>Once this is done, let's come back to <code>frontend</code> and create a new class called <code>DioNetworkException</code>.</p>
<h2 id="heading-create-dionetworkexception">Create <code>DioNetworkException</code></h2>
<p>Let's create a new file in <code>lib/core/network/exceptions/dio_network_exception.dart</code> in the <code>frontend</code> package.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:exceptions/exceptions.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DioNetworkException</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DioError</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">NetworkException</span> </span>{
  DioNetworkException({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.message,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.statusCode,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.errors,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">super</span>.requestOptions,
  });

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> statusCode;
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> message;
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt; errors;
}
</code></pre>
<p>This exception is created to convert the <code>DioError</code> we get while implementing the error interceptor, to our <code>NetworkException</code> so that we only care about the <code>NetworkException</code> we implemented in our package, and catch it.</p>
<h2 id="heading-create-networkerrorinterceptor">Create <code>NetworkErrorInterceptor</code> 🔒</h2>
<p>We will now implement <code>NetworkErrorInterceptor</code> extending <code>Interceptor</code> from <code>dio</code>. Here we will have to override <code>onError</code> method, which will give us <code>DioError</code> object and a handler.</p>
<blockquote>
<p>👉 Read more <a target="_blank" href="https://pub.dev/packages/dio#interceptors">about interceptors</a> from here.</p>
</blockquote>
<p>Create a new file <code>frontend/lib/core/network/interceptors/dio_error_interceptor.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:dio/dio.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/core/network/exceptions/dio_network_exception.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NetworkErrorInterceptor</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Interceptor</span> </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> onError(DioError err, ErrorInterceptorHandler handler) {
    <span class="hljs-keyword">const</span> genericInternetIssue =
        <span class="hljs-string">'Please check your internet connection and try again'</span>;
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">if</span> (err.response == <span class="hljs-keyword">null</span>) {
        <span class="hljs-keyword">throw</span> DioNetworkException(
          message: genericInternetIssue,
          statusCode: <span class="hljs-number">500</span>,
          requestOptions: err.requestOptions,
          errors: {},
        );
      }
      <span class="hljs-keyword">final</span> errorJson = err.response!.data <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;;
      <span class="hljs-keyword">final</span> failureFromServer = NetworkFailure.fromJson(
        {
          ...errorJson,
          <span class="hljs-string">'status_code'</span>: err.response!.statusCode,
        },
      );
      <span class="hljs-keyword">throw</span> DioNetworkException(
        message: failureFromServer.message,
        statusCode: err.response!.statusCode ?? failureFromServer.statusCode,
        errors: failureFromServer.errors,
        requestOptions: err.requestOptions,
      );
    } <span class="hljs-keyword">on</span> DioNetworkException <span class="hljs-keyword">catch</span> (e) {
      handler.reject(e);
    } <span class="hljs-keyword">catch</span> (e) {
      handler.reject(
        DioNetworkException(
          message: genericInternetIssue,
          statusCode: <span class="hljs-number">500</span>,
          requestOptions: err.requestOptions,
          errors: {},
        ),
      );
    }
  }
}
</code></pre>
<blockquote>
<p>Note: we may need to make a small change to our <code>NetworkFailure</code> class and run <code>build_runner</code></p>
</blockquote>
<pre><code class="lang-diff">  const factory NetworkFailure({
    required String message,
    required int statusCode,
<span class="hljs-deletion">-   @Default([]) List&lt;String&gt; errors,</span>
<span class="hljs-addition">+   @Default({}) Map&lt;String, List&lt;String&gt;&gt; errors,</span>
  }) = _NetworkFailure;
</code></pre>
<p>Now once, we have implemented the <code>NetworkErrorInterceptor</code>, we will attach this interceptor to our dio object.</p>
<p>Open <code>third_party_modules.dart</code> and add the following line.</p>
<pre><code class="lang-diff">abstract class ThirdPartyModules {
  @lazySingleton
  Dio get dio =&gt; Dio(BaseOptions(baseUrl: kBaseUrl));
<span class="hljs-addition">+    ..interceptors.add(NetworkErrorInterceptor());</span>
}
</code></pre>
<h2 id="heading-implement-todorepository">Implement <code>TodoRepository</code> 📚</h2>
<p>Now we will implement the todo repository at <code>lib/data/repositories/todo_repository_impl.dart</code> in the following way.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:exceptions/exceptions.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-meta">@LazySingleton</span>(<span class="hljs-keyword">as</span>: TodoRepository)
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoRepositoryImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TodoRepository</span> </span>{
  <span class="hljs-keyword">const</span> TodoRepositoryImpl(<span class="hljs-keyword">this</span>._todoDataSource);
  <span class="hljs-keyword">final</span> TodoDataSource _todoDataSource;

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; createTodo(CreateTodoDto createTodoDto) =&gt;
      handleError(() =&gt; _todoDataSource.createTodo(createTodoDto));

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, <span class="hljs-keyword">void</span>&gt;&gt; deleteTodo(TodoId id) =&gt;
      handleError(() =&gt; _todoDataSource.deleteTodoById(id));

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; getTodoById(TodoId id) =&gt;
      handleError(() =&gt; _todoDataSource.getTodoById(id));

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, <span class="hljs-built_in">List</span>&lt;Todo&gt;&gt;&gt; getTodos() =&gt;
      handleError(_todoDataSource.getAllTodo);

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; updateTodo({
    <span class="hljs-keyword">required</span> TodoId id,
    <span class="hljs-keyword">required</span> UpdateTodoDto updateTodoDto,
  }) =&gt;
      handleError(
        () =&gt; _todoDataSource.updateTodo(id: id, todo: updateTodoDto),
      );

  Future&lt;Either&lt;Failure, T&gt;&gt; handleError&lt;T&gt;(
    Future&lt;T&gt; <span class="hljs-built_in">Function</span>() callback,
  ) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> callback();
      <span class="hljs-keyword">return</span> Right(res);
    } <span class="hljs-keyword">on</span> NetworkException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> Left(
        NetworkFailure(
          message: e.message,
          statusCode: e.statusCode,
          errors: e.errors,
        ),
      );
    }
  }
}
</code></pre>
<p>Here, we are passing the data source as a dependency and calling methods from the data source for each implementation. In addition to that, we have created a <code>handleError</code> generic method, which takes a caller method, and checks if there is a <code>NetworkException</code>.</p>
<p>The <code>NetworkException</code> will be caught here which was thrown from the interceptor as <code>DioNetworkException</code>.</p>
<p>Now, let's do some UI, and connect our view model to the repository.</p>
<h1 id="heading-creating-showtodosview">Creating <code>ShowTodosView</code> 📋</h1>
<p>Next, we will move on to creating the user interface and retrieving data from our repositories. To do this, we will perform some initial setup. We will be utilizing <a target="_blank" href="https://pub.dev/packages/stacked">stacked</a> to implement a Model-View-ViewModel (MVVM) architecture. The necessary dependencies have already been imported. Let's begin by creating the views and adding them to the routes.</p>
<h2 id="heading-create-showtodosview-and-showtodosviewmodel">Create <code>ShowTodosView</code> and <code>ShowTodosViewModel</code></h2>
<p>In <code>lib/presentation//show_todos</code> we will create two new files called <code>show_todos_view.dart</code> and <code>show_todos_viewmodel.dart</code>.</p>
<p>We will begin by creating an empty implementation of the view and view model.</p>
<p>In <code>show_todos_viewmodel.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked/stacked.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;

<span class="hljs-meta">@injectable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShowTodosViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">BaseViewModel</span> </span>{}
</code></pre>
<p>Similarly in <code>show_todos_view.dart</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/core/di/locator.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/presentation/show_todos/show_todos_viewmodel.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked/stacked.dart'</span>;

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> ViewModelBuilder&lt;ShowTodosViewModel&gt;.nonReactive(
      viewModelBuilder: () =&gt; locator&lt;ShowTodosViewModel&gt;(),
      builder: (
        BuildContext context,
        ShowTodosViewModel model,
        Widget? child,
      ) {
        <span class="hljs-keyword">return</span> Scaffold(
          body: Center(
            child: Text(
              <span class="hljs-string">'ShowTodosView'</span>,
            ),
          ),
        );
      },
    );
  }
}
</code></pre>
<p>We will also take advantage of the routing system provided by <a target="_blank" href="https://pub.dev/packages/stacked"><code>stacked</code></a>. This system uses context-less routing, allowing us to easily navigate within our application from the view model.</p>
<p>We will create a <code>NavigationService</code> that can be injected as a dependency, which will enable us to handle routing in a consistent and efficient manner.</p>
<blockquote>
<p>👉 Read <a target="_blank" href="https://pub.dev/packages/stacked_services#navigation-service">more about <code>NavigationService</code></a> from here.</p>
</blockquote>
<p>Let's create a new <code>core/app/routes.dart</code> file with the following contents.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/presentation/show_todos/show_todos_view.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked/stacked_annotations.dart'</span>;

<span class="hljs-meta">@StackedApp</span>(
  routes: routes,
)
<span class="hljs-keyword">const</span> <span class="hljs-built_in">List</span>&lt;StackedRoute&lt;<span class="hljs-built_in">dynamic</span>&gt;&gt; routes = [
  AdaptiveRoute(page: ShowTodosView, initial: <span class="hljs-keyword">true</span>),
];
</code></pre>
<p>Here, the <code>@StackedApp</code> decorator does the magic. Once this is done, we run <code>build_runner</code> to run code generation. We will get a new file <code>routes.router.dart</code></p>
<blockquote>
<p>👉 Read more <a target="_blank" href="https://pub.dev/packages/stacked#router">about <code>@StackedApp</code></a></p>
</blockquote>
<p>Now, in <code>main.dart</code> file, let's remove all the comments and make these changes.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/core/app/routes.router.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fullstack_todo/core/di/locator.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked_services/stacked_services.dart'</span>;

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

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MaterialApp(
      title: <span class="hljs-string">'Fullstack Todo App'</span>,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      navigatorKey: StackedService.navigatorKey,
      onGenerateRoute: StackedRouter().onGenerateRoute,
    );
  }
}
</code></pre>
<p>Before calling <code>runApp()</code>, we are invoking <code>setupLocator()</code> from <code>locator.dart</code>. This function will handle all the logic related to dependency injection. We are also providing <code>navigatorKey</code> and <code>onGenerateRoute</code> parameters, which come from <code>stacked</code> and <code>routes.router.dart</code>, respectively.</p>
<p>We also need to take an extra step, which is to manage the injection of the <code>NavigationService</code>. For this, we need to add a getter to the <code>third_party_modules.dart</code> file.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@module</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThirdPartyModules</span> </span>{
  <span class="hljs-meta">@lazySingleton</span>
  Dio <span class="hljs-keyword">get</span> dio =&gt; Dio(BaseOptions(baseUrl: kBaseUrl))
    ..interceptors.add(NetworkErrorInterceptor());

  <span class="hljs-meta">@lazySingleton</span>
  NavigationService <span class="hljs-keyword">get</span> navigationService;
}
</code></pre>
<p>Once this is done, if you build the app you should see the first visual output.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673539321212/c86ca5ee-3d5c-4c22-881d-249da562025b.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-implement-showtodosviewmodel">Implement <code>ShowTodosViewModel</code></h3>
<p>This view model will utilize the <code>TodoRepository</code> to retrieve data from the backend.</p>
<p>Similar to <code>initState</code> method used when working with <code>StatefulWidgets</code>, we can create a new method in our ViewModel called <code>init</code> to handle initialization logic.</p>
<p>Within this method, we can call the repository to fetch all the todos. We will also create a <code>refresh</code> method to handle pull-to-refresh functionality.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@injectable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShowTodosViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">BaseViewModel</span> </span>{
  <span class="hljs-keyword">final</span> TodoRepository _todoRepository;

  ShowTodosViewModel(<span class="hljs-keyword">this</span>._todoRepository);

  <span class="hljs-keyword">void</span> handleFailure(Failure failure) {
    setError(failure.message);
    log(failure.message);
  }

  <span class="hljs-keyword">void</span> init() =&gt; runBusyFuture(refresh());

  Future&lt;<span class="hljs-keyword">void</span>&gt; refresh() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> _todoRepository.getTodos();
    response.fold(handleFailure, <span class="hljs-built_in">print</span>);
  }
}
</code></pre>
<p>Here, we have three methods, <code>init</code> <code>refresh</code> and <code>handleFailure</code>.</p>
<ul>
<li><p><code>init</code> This method is ideally run on <code>initState</code> state. This will call the <code>refresh</code> method, but in addition to that, this will mark the ViewModel as <code>busy</code> before calling the method. This will be helpful when we want to show a loading indicator when we first fetch the todos.</p>
</li>
<li><p><code>refresh</code> This method will call the <code>TodoRepository</code> and than handle re response accordingly.</p>
</li>
<li><p><code>handleFailure</code> Ideally, this is the place where we would handle the failure and show the UI accordingly. The <code>setError</code> is storing the failure message in the view model, which will be accessible by <code>modelError</code> getter from <code>BaseViewModel</code>.</p>
</li>
</ul>
<p>Now we are getting the todos from the backend. We will also be marking todos as completed and deleting the todo from this same view model.</p>
<blockquote>
<p>Where should we put the todos once we fetch them?</p>
</blockquote>
<p>We will also have another view called <code>MaintainTodoView</code> which will be responsible for creating and updating the todos. We will come across a problem, where we will have to update the state of the created and updated todo. We can do this by passing the todo down the routes once it is created or updated.</p>
<p>Another way to do this is by creating a data service, which will be shared across different view models. This service will be reactive, meaning if the data changes, it will rebuild all the view models listening to this data. This will help us avoid passing down the problem of the route.</p>
<h3 id="heading-implement-todosdataservice">Implement <code>TodosDataService</code></h3>
<p>We will create a <code>TodosDataService</code> which will be shared across the view models. This will have some helper methods that will be used to maintain the state of all the todos. We will create a new file <code>lib/data_services/todos_data_service.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:injectable/injectable.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:stacked/stacked.dart'</span>;

<span class="hljs-meta">@lazySingleton</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodosDataService</span> <span class="hljs-title">with</span> <span class="hljs-title">ReactiveServiceMixin</span> </span>{
  TodosDataService() {
    listenToReactiveValues([_todos]);
  }
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> ReactiveValue&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt; _todos = ReactiveValue&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt;([]);

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

  <span class="hljs-keyword">void</span> add(Todo todo) {
    <span class="hljs-keyword">final</span> index = _todos.value.indexWhere((element) =&gt; element.id == todo.id);
    <span class="hljs-keyword">if</span> (index == <span class="hljs-number">-1</span>) {
      _todos.value.insert(<span class="hljs-number">0</span>, todo);
    } <span class="hljs-keyword">else</span> {
      _todos.value[index] = todo;
    }
    notifyListeners();
  }

  <span class="hljs-keyword">void</span> addAll(<span class="hljs-built_in">List</span>&lt;Todo&gt; todos) {
    _todos.value
      ..clear()
      ..insertAll(<span class="hljs-number">0</span>, todos);
    notifyListeners();
  }

  <span class="hljs-keyword">void</span> remove(Todo todo) {
    _todos.value.removeWhere((element) =&gt; element.id == todo.id);
    notifyListeners();
  }
}
</code></pre>
<p>Here, we are using <code>ReactiveServiceMixin</code> and <code>ReactiveValue</code> objects. When we call <code>add</code> method, it checks if the todo is present already, if so, it will update the existing one, or else it will add it to the top, so that we can see the latest one at the top.</p>
<p>Similarly, there is <code>addAll</code> which will clear all todos and then add them to the top.</p>
<blockquote>
<p>👉 Read more about <a target="_blank" href="https://pub.dev/packages/stacked#reactivity">reactivity in stacked</a> from here.</p>
</blockquote>
<h3 id="heading-updating-showtodosviewmodel">Updating <code>ShowTodosViewModel</code></h3>
<p>Now we will inject <code>TodosDataService</code> from the constructor and make some changes to the view model to handle reactivity.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@injectable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShowTodosViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ReactiveViewModel</span> </span>{
  ShowTodosViewModel(
    <span class="hljs-keyword">this</span>._todoRepository,
    <span class="hljs-keyword">this</span>._todosDataService,
  );

  <span class="hljs-keyword">final</span> TodoRepository _todoRepository;
  <span class="hljs-keyword">final</span> TodosDataService _todosDataService;
  <span class="hljs-built_in">List</span>&lt;Todo&gt; <span class="hljs-keyword">get</span> todos =&gt; _todosDataService.todos;

  Future&lt;<span class="hljs-keyword">void</span>&gt; init() =&gt; runBusyFuture(refresh());

  <span class="hljs-keyword">void</span> handleFailure(Failure failure) {
    setError(failure.message);
    log(failure.message);
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; refresh() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> _todoRepository.getTodos();
    response.fold(handleFailure, _todosDataService.addAll);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">List</span>&lt;ReactiveServiceMixin&gt; <span class="hljs-keyword">get</span> reactiveServices =&gt; [_todosDataService];
}
</code></pre>
<p>If you noticed, we are extending from <a target="_blank" href="https://pub.dev/packages/stacked#reactiveviewmodel"><code>ReactiveViewModel</code></a> and also overriding <code>reactiveServices</code>. We are listening to the <code>_todosDataService</code> and will be reflecting the UI whenever the data is changed in the reactive service.</p>
<h3 id="heading-update-showtodosview-to-show-fetched-todos">Update <code>ShowTodosView</code> to show fetched todos</h3>
<p>Now we can update the <code>show_todos_view.dart</code> to show the fetched todos.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShowTodosView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> ShowTodosView({<span class="hljs-keyword">super</span>.key});

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> ViewModelBuilder&lt;ShowTodosViewModel&gt;.reactive(
      viewModelBuilder: locator,
      onModelReady: (model) =&gt; model.init(),
      builder: (
        BuildContext context,
        ShowTodosViewModel model,
        Widget? child,
      ) {
        <span class="hljs-keyword">return</span> Scaffold(
          appBar: AppBar(
            title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Todos'</span>),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {},
            child: <span class="hljs-keyword">const</span> Icon(Icons.add),
          ),
          body: Builder(
            builder: (context) {
              <span class="hljs-keyword">if</span> (model.isBusy) {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> Center(child: CircularProgressIndicator());
              }
              <span class="hljs-keyword">if</span> (model.hasError) {
                <span class="hljs-keyword">return</span> Center(child: Text(model.modelError.toString()));
              }
              <span class="hljs-keyword">if</span> (model.todos.isEmpty) {
                <span class="hljs-keyword">return</span> Center(
                  child: Text(
                    <span class="hljs-string">'No todos found!'</span>,
                    style: Theme.of(context).textTheme.titleLarge,
                  ),
                );
              }
              <span class="hljs-keyword">return</span> RefreshIndicator(
                onRefresh: model.refresh,
                child: ListView.builder(
                  physics: <span class="hljs-keyword">const</span> AlwaysScrollableScrollPhysics(),
                  itemCount: model.todos.length,
                  itemBuilder: (context, index) {
                    <span class="hljs-keyword">final</span> todo = model.todos[index];
                    <span class="hljs-keyword">return</span> ListTile(
                      title: Text(todo.title),
                      subtitle: Text(todo.description),
                      leading: Checkbox(
                        value: todo.completed,
                        onChanged: (val) {},
                      ),
                      trailing: Row(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          IconButton(
                            icon: <span class="hljs-keyword">const</span> Icon(
                              Icons.edit,
                              color: Colors.blue,
                            ),
                            onPressed: () {},
                          ),
                          IconButton(
                            icon: <span class="hljs-keyword">const</span> Icon(
                              Icons.delete,
                              color: Colors.red,
                            ),
                            onPressed: () {},
                          ),
                        ],
                      ),
                    );
                  },
                ),
              );
            },
          ),
        );
      },
    );
  }
}
</code></pre>
<p>This view will get all the lists from the view model with <code>init</code> method.</p>
<blockquote>
<p>Notice the <code>onModelReady</code> in the <code>ViewModelBuilder</code> where we are calling <code>init</code> method from the view model.</p>
</blockquote>
<p>We are using the <code>Builder</code> to check the different states of the view model and show the UI accordingly.</p>
<p>We show a <code>CircularProgressIndicator</code> when the view model is <code>isBusy</code>. This value is set by <code>runBusyFuture</code> method in the view model.</p>
<p>Also if the model has an error, we can check <code>hasError</code> and show the error message accordingly. We will also show the todos with a <code>ListView.builder</code>, when there are todos, and an empty message as well.</p>
<p>Now let's build the app, and check out the progress.</p>
<blockquote>
<p>Tip: If you are using macOS, make sure to allow <a target="_blank" href="https://stackoverflow.com/a/61201109/9611399">network access in the entitlements file</a>.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673539368493/53546965-2fb3-42cf-859c-718611e45739.png" alt class="image--center mx-auto" /></p>
<p>And, if you create one using postman, you should see something like this.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtube.com/shorts/hcz457pUcZ0?feature=share">https://youtube.com/shorts/hcz457pUcZ0?feature=share</a></div>
<p> </p>
<h3 id="heading-create-todolisttile">Create <code>TodoListTile</code></h3>
<p>Once this is done, let's do some refactoring. We can create a <code>TodoListTile</code> widget as a separate stateless widget in <code>show_todos/widgets/todo_list_tile.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoListTile</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ViewModelWidget</span>&lt;<span class="hljs-title">ShowTodosViewModel</span>&gt; </span>{
  <span class="hljs-keyword">const</span> TodoListTile({
    <span class="hljs-keyword">super</span>.key,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.todo,
  });

  <span class="hljs-keyword">final</span> Todo todo;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context, ShowTodosViewModel viewModel) {
    <span class="hljs-keyword">return</span> ListTile(
      title: Text(todo.title),
      subtitle: Text(todo.description),
      leading: Checkbox(
        value: todo.completed,
        onChanged: (val) =&gt; viewModel.markCompleted(todo),
      ),
      trailing: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          IconButton(
            icon: <span class="hljs-keyword">const</span> Icon(
              Icons.edit,
              color: Colors.blue,
            ),
            onPressed: () =&gt; viewModel.handleTodo(todo),
          ),
          IconButton(
            icon: <span class="hljs-keyword">const</span> Icon(
              Icons.delete,
              color: Colors.red,
            ),
            onPressed: () =&gt; viewModel.deleteTodo(todo),
          ),
        ],
      ),
    );
  }
}
</code></pre>
<p>Here, we have missing view model methods, which we will implement later. Also, we are not using a <code>StatelessWidget</code> here, but it's a <code>ViewModelWidget</code>. This is a wrapper around <code>StatelessWidget</code> which will also provide us with the view model. This works with <code>Provider</code> under the hood.</p>
<p>Let's use this <code>TodoListTile</code> widget in <code>ShowTodosView</code>. We can update the <code>ListView.builder</code> as follows.</p>
<pre><code class="lang-dart">ListView.builder(
  physics: <span class="hljs-keyword">const</span> AlwaysScrollableScrollPhysics(),
  itemCount: model.todos.length,
  itemBuilder: (context, index) =&gt;
      TodoListTile(todo: model.todos[index]),
),
</code></pre>
<h3 id="heading-implement-delete-and-markcompleted-methods">Implement <code>delete</code> and <code>markCompleted</code> methods.</h3>
<p>We will now implement <code>deleteTodo</code> and <code>markCompleted</code> methods as follows.</p>
<pre><code class="lang-dart">  Future&lt;<span class="hljs-keyword">void</span>&gt; deleteTodo(Todo todo) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> _todoRepository.deleteTodo(todo.id);
    response.fold(
      handleFailure,
      (_) =&gt; _todosDataService.remove(todo),
    );
  }
</code></pre>
<p>Here, we are calling the <code>deleteTodo</code> method from the repository, and if it's successful, we will remove the todo from the data service, that in turn will remove it from the UI.</p>
<pre><code class="lang-dart">  Future&lt;<span class="hljs-keyword">void</span>&gt; markCompleted(Todo todo) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (busy(<span class="hljs-string">'updating'</span>)) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">final</span> completed = !todo.completed;
    _todosDataService.add(todo.copyWith(completed: completed));
    <span class="hljs-keyword">final</span> updateDto = UpdateTodoDto(
      completed: !todo.completed,
    );
    <span class="hljs-keyword">final</span> update = <span class="hljs-keyword">await</span> runBusyFuture(
      _todoRepository.updateTodo(id: todo.id, updateTodoDto: updateDto),
      busyObject: <span class="hljs-string">'updating'</span>,
    );
    update.fold(
      (failure) {
        _todosDataService.add(todo.copyWith(completed: !completed));
        handleFailure(failure);
      },
      (_) {},
    );
  }
</code></pre>
<p>Here, in <code>markCompleted</code> method, we are first negating the state, and then adding it to the data service, and then calling the API. This is to show the update in an instantaneous manner. If the update fails, we will revert the checkbox state.</p>
<p>We will add an empty implementation for the <code>handleTodo</code> method as follows.</p>
<pre><code class="lang-dart">  Future&lt;<span class="hljs-keyword">void</span>&gt;? handleTodo([Todo? todo]) {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
  }
</code></pre>
<p>This method will take an optional Todo object, and we will pass it to <code>MaintainTodoView</code>, which will create or update the todo based on the value provided.</p>
<p>Once these methods are completed, we will have these functionalities.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/shorts/sY-5NN8GC8I?feature=share">https://www.youtube.com/shorts/sY-5NN8GC8I?feature=share</a></div>
<p> </p>
<h1 id="heading-creating-maintaintodoview">Creating <code>MaintainTodoView</code>📝</h1>
<p>This view will be able to create and update the todos. We will create the view and the view model. Also, we will create a route for this, and implement <code>handleTodo</code> method in <code>ShowTodosViewModel</code>.</p>
<p>We will create <code>maintain_todo_view.dart</code> and <code>maintain_todo_viewmomdel.dart</code> in <code>presentstion/main_todo</code> directory.</p>
<p>We will create the view model as follows.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@injectable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MaintainTodoViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ReactiveViewModel</span> </span>{
  MaintainTodoViewModel(<span class="hljs-keyword">this</span>._todosDataService);
  <span class="hljs-keyword">final</span> TodosDataService _todosDataService;

  Todo? _todo;
  <span class="hljs-keyword">void</span> init(Todo? todo) {
    <span class="hljs-keyword">if</span> (todo == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span>;
    _todo = todo;
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">List</span>&lt;ReactiveServiceMixin&gt; <span class="hljs-keyword">get</span> reactiveServices =&gt; [_todosDataService];
}
</code></pre>
<p>Similarly, we will create an empty view, as follows</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MaintainTodoView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> MaintainTodoView(<span class="hljs-keyword">this</span>.todo, {<span class="hljs-keyword">super</span>.key});
  <span class="hljs-keyword">final</span> Todo? todo;
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> ViewModelBuilder&lt;MaintainTodoViewModel&gt;.nonReactive(
      onModelReady: (model) =&gt; model.init(todo),
      viewModelBuilder: () =&gt; locator&lt;MaintainTodoViewModel&gt;(),
      builder: (
        BuildContext context,
        MaintainTodoViewModel model,
        Widget? child,
      ) {
        <span class="hljs-keyword">return</span> Scaffold(
          body: Center(
            child: Text(
              <span class="hljs-string">'MaintainTodoView'</span>,
            ),
          ),
        );
      },
    );
  }
}
</code></pre>
<p>Now, we can add this view to the route, and handle routing from <code>ShowTodosViewModel</code>. In <code>lib/core/app/routes.dart</code> add the following route entry:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">const</span> <span class="hljs-built_in">List</span>&lt;StackedRoute&lt;<span class="hljs-built_in">dynamic</span>&gt;&gt; routes = [
  AdaptiveRoute(page: ShowTodosView, initial: <span class="hljs-keyword">true</span>),
  AdaptiveRoute(page: MaintainTodoView),
];
</code></pre>
<p>Once you run the <code>build_runner</code>, open the <code>ShowTodosViewModel</code>, and make the following change.</p>
<p>We will have to register <code>NavigationService</code> in our third-party modules as follows.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@module</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThirdPartyModules</span> </span>{
  <span class="hljs-meta">@lazySingleton</span>
  Dio <span class="hljs-keyword">get</span> dio =&gt; Dio(BaseOptions(baseUrl: kBaseUrl));

  <span class="hljs-meta">@lazySingleton</span>
  NavigationService <span class="hljs-keyword">get</span> navigationService;
}
</code></pre>
<p>Now, we will inject the <code>NavigationService</code> from the <code>ShowTodosViewModel</code> constructor and implement the <code>handleTodo</code> as follows.</p>
<pre><code class="lang-diff">  ShowTodosViewModel(
    this._todoRepository,
    this._todosDataService,
<span class="hljs-addition">+   this._navigationService,</span>
  );

  final TodoRepository _todoRepository;
  final TodosDataService _todosDataService;
<span class="hljs-addition">+ final NavigationService _navigationService;</span>

  Future&lt;void&gt;? handleTodo([Todo? todo]) {
<span class="hljs-addition">+   return _navigationService.navigateTo&lt;void&gt;(</span>
<span class="hljs-addition">+     Routes.maintainTodoView,</span>
<span class="hljs-addition">+     arguments: MaintainTodoViewArguments(todo: todo),</span>
<span class="hljs-addition">+   );</span>
  }
</code></pre>
<blockquote>
<p>Make sure to run <code>build_runner</code>.</p>
</blockquote>
<p>Now, you should be able to navigate to <code>MaintainTodoView</code>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/shorts/Ua27jp70j2s?feature=share">https://www.youtube.com/shorts/Ua27jp70j2s?feature=share</a></div>
<p> </p>
<h2 id="heading-implementing-maintaintodoviewmodel">Implementing <code>MaintainTodoViewModel</code></h2>
<p>We will create three state variables for handling <code>title</code>, <code>description</code>, and <code>completed</code> value. We will also expose the getters for these variables. Also, we will create <code>onChanged</code> methods for these variables.</p>
<p>We will also create an <code>init</code> method, which will take an optional <code>Todo</code> object and populate these state variables. Here if we pass this optional todo, we will be updating that todo.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@injectable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MaintainTodoViewModel</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">BaseViewModel</span> </span>{
  MaintainTodoViewModel(
    <span class="hljs-keyword">this</span>._todosDataService,
    <span class="hljs-keyword">this</span>._repository,
    <span class="hljs-keyword">this</span>._navigationService,
  );

  <span class="hljs-keyword">final</span> TodosDataService _todosDataService;
  <span class="hljs-keyword">final</span> TodoRepository _repository;
  <span class="hljs-keyword">final</span> NavigationService _navigationService;
  <span class="hljs-built_in">String</span> _title = <span class="hljs-string">''</span>;
  <span class="hljs-built_in">String</span> <span class="hljs-keyword">get</span> title =&gt; _title;

  <span class="hljs-built_in">String</span> _description = <span class="hljs-string">''</span>;
  <span class="hljs-built_in">String</span> <span class="hljs-keyword">get</span> description =&gt; _description;

  <span class="hljs-built_in">bool</span> _completed = <span class="hljs-keyword">false</span>;
  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">get</span> completed =&gt; _completed;

  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">get</span> isValidated {
    <span class="hljs-keyword">final</span> empty = _title.isEmpty || _description.isEmpty;
    <span class="hljs-keyword">if</span> (empty) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    <span class="hljs-keyword">final</span> errors = error(<span class="hljs-string">'title'</span>) != <span class="hljs-keyword">null</span> || error(<span class="hljs-string">'description'</span>) != <span class="hljs-keyword">null</span>;
    <span class="hljs-keyword">if</span> (errors) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
  }

  <span class="hljs-keyword">void</span> onTitleChanged(<span class="hljs-built_in">String</span> value) {
    _title = value;
    <span class="hljs-keyword">if</span> (value.isEmpty) {
      setErrorForObject(<span class="hljs-string">'title'</span>, <span class="hljs-string">'Title is required'</span>);
    } <span class="hljs-keyword">else</span> {
      setErrorForObject(<span class="hljs-string">'title'</span>, <span class="hljs-keyword">null</span>);
    }
    notifyListeners();
  }

  <span class="hljs-keyword">void</span> onDescriptionChanged(<span class="hljs-built_in">String</span> value) {
    _description = value;
    <span class="hljs-keyword">if</span> (value.isEmpty) {
      setErrorForObject(<span class="hljs-string">'description'</span>, <span class="hljs-string">'Description is required'</span>);
    } <span class="hljs-keyword">else</span> {
      setErrorForObject(<span class="hljs-string">'description'</span>, <span class="hljs-keyword">null</span>);
    }
    notifyListeners();
  }

  Todo? _todo;
  <span class="hljs-keyword">void</span> init(Todo? todo) {
    <span class="hljs-keyword">if</span> (todo == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span>;
    _title = todo.title;
    _description = todo.description;
    _todo = todo;
    _completed = todo.completed;
  }

  <span class="hljs-keyword">void</span> onCompletedChanged({<span class="hljs-built_in">bool?</span> value}) {
    _completed = value ?? <span class="hljs-keyword">false</span>;
    notifyListeners();
  }

  <span class="hljs-keyword">void</span> handleTodo() {}
}
</code></pre>
<p>In change handlers, we are also validating the values, and setting the errors for each state on change, and then we have a <code>isValidated</code> getter which would return if the values are validated or not.</p>
<h2 id="heading-implementing-maintaintodoview">Implementing <code>MaintainTodoView</code></h2>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MaintainTodoView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">HookWidget</span> </span>{
  <span class="hljs-keyword">const</span> MaintainTodoView(<span class="hljs-keyword">this</span>.todo, {<span class="hljs-keyword">super</span>.key});
  <span class="hljs-keyword">final</span> Todo? todo;

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">final</span> titleController = useTextEditingController(text: todo?.title);
    <span class="hljs-keyword">final</span> descriptionController =
        useTextEditingController(text: todo?.description);
    <span class="hljs-keyword">final</span> titleFocusNode = useFocusNode();
    <span class="hljs-keyword">final</span> descriptionFocusNode = useFocusNode();
    <span class="hljs-keyword">final</span> checkBoxFocusNode = useFocusNode();
    <span class="hljs-keyword">final</span> buttonFocusNode = useFocusNode();
    <span class="hljs-keyword">return</span> ViewModelBuilder&lt;MaintainTodoViewModel&gt;.nonReactive(
      onModelReady: (model) =&gt; model.init(todo),
      viewModelBuilder: locator,
      builder: (
        BuildContext context,
        MaintainTodoViewModel model,
        Widget? child,
      ) {
        <span class="hljs-keyword">return</span> Scaffold(
          appBar: AppBar(
            title: Text(<span class="hljs-string">'<span class="hljs-subst">${todo == <span class="hljs-keyword">null</span> ? <span class="hljs-string">'Create'</span> : <span class="hljs-string">'Edit'</span>}</span> Todo'</span>),
          ),
          body: Padding(
            padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16</span>),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                TextFormField(
                  controller: titleController,
                  focusNode: titleFocusNode,
                  decoration: <span class="hljs-keyword">const</span> InputDecoration(
                    hintText: <span class="hljs-string">'Title'</span>,
                    border: OutlineInputBorder(),
                  ),
                  onChanged: model.onTitleChanged,
                  onEditingComplete: descriptionFocusNode.requestFocus,
                ),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">14</span>),
                TextFormField(
                  controller: descriptionController,
                  focusNode: descriptionFocusNode,
                  decoration: <span class="hljs-keyword">const</span> InputDecoration(
                    hintText: <span class="hljs-string">'Description'</span>,
                    border: OutlineInputBorder(),
                  ),
                  onChanged: model.onDescriptionChanged,
                  onEditingComplete: checkBoxFocusNode.requestFocus,
                ),
                <span class="hljs-keyword">if</span> (todo != <span class="hljs-keyword">null</span>) <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">14</span>),
                <span class="hljs-keyword">if</span> (todo != <span class="hljs-keyword">null</span>)
                  SelectorViewModelBuilder&lt;MaintainTodoViewModel, <span class="hljs-built_in">bool</span>&gt;(
                    selector: (model) =&gt; model.completed,
                    builder: (context, val, _) {
                      <span class="hljs-keyword">return</span> CheckboxListTile(
                        focusNode: checkBoxFocusNode,
                        contentPadding: EdgeInsets.zero,
                        title: <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Mark Completed'</span>),
                        value: val,
                        onChanged: (v) =&gt; model.onCompletedChanged(value: v),
                      );
                    },
                  ),
                <span class="hljs-keyword">const</span> Spacer(),
                SelectorViewModelBuilder&lt;MaintainTodoViewModel, <span class="hljs-built_in">bool</span>&gt;(
                  selector: (model) =&gt; model.isValidated,
                  builder: (context, validated, child) {
                    <span class="hljs-keyword">if</span> (validated) <span class="hljs-keyword">return</span> child!;
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> SizedBox.shrink();
                  },
                  child: SizedBox(
                    height: <span class="hljs-number">50</span>,
                    child: ElevatedButton(
                      focusNode: buttonFocusNode,
                      onPressed: model.handleTodo,
                      child:
                          SelectorViewModelBuilder&lt;MaintainTodoViewModel, <span class="hljs-built_in">bool</span>&gt;(
                        selector: (model) =&gt; model.isBusy,
                        builder: (context, isBusy, child) {
                          <span class="hljs-keyword">if</span> (isBusy) <span class="hljs-keyword">return</span> child!;
                          <span class="hljs-keyword">return</span> Row(
                            mainAxisSize: MainAxisSize.min,
                            children: <span class="hljs-keyword">const</span> [
                              Icon(
                                Icons.save,
                                size: <span class="hljs-number">30</span>,
                              ),
                              SizedBox(width: <span class="hljs-number">10</span>),
                              Text(
                                <span class="hljs-string">'Save'</span>,
                                style: TextStyle(
                                  fontSize: <span class="hljs-number">20</span>,
                                ),
                              ),
                            ],
                          );
                        },
                        child: <span class="hljs-keyword">const</span> CircularProgressIndicator(
                          valueColor:
                              AlwaysStoppedAnimation&lt;Color&gt;(Colors.white),
                        ),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        );
      },
    );
  }
}
</code></pre>
<p>This form includes crucial text fields for the <code>title</code> and <code>description</code> of the to-do task. The optional check box is displayed only when an existing to-do task is provided for updating. If a to-do task is provided, the form will be populated with the previous values and the check box will be visible for updating.</p>
<p>To handle the form's input, we utilize the <code>flutter_hooks</code> library for managing <code>TextEditingControllers</code> and <code>FocusNodes</code>. The <code>ElevatedButton</code> takes care of the create/update action.</p>
<p>We are also using <code>SelectorViewModelBuilder</code> with a <code>selector</code> to rebuild only when the value returned from the selector is changed, resulting in partial rebuilds. For example, we are only showing the <code>ElevatedButton</code> when the form is validated.</p>
<blockquote>
<p>👉 Read <a target="_blank" href="https://pub.dev/packages/stacked#selectorviewmodelbuilder">more about <code>SelectorViewModelBuilder</code></a></p>
</blockquote>
<p>With this, we have a functional form UI something like this.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/shorts/SOUH1t2RdbY?feature=share">https://www.youtube.com/shorts/SOUH1t2RdbY?feature=share</a></div>
<p> </p>
<p>In the final part, let's implement <code>handleTodo</code> method in <code>MaintainTodoViewmodel</code> like this.</p>
<pre><code class="lang-dart">  Future&lt;<span class="hljs-keyword">void</span>&gt; handleTodo() {
    <span class="hljs-keyword">if</span> (!isValidated) <span class="hljs-keyword">return</span> Future.value();
    <span class="hljs-keyword">if</span> (_todo == <span class="hljs-keyword">null</span>) {
      <span class="hljs-keyword">return</span> _createTodo();
    }
    <span class="hljs-keyword">return</span> _updateTodo();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _createTodo() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> dto = CreateTodoDto(title: title, description: description);
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> runBusyFuture(_repository.createTodo(dto));
    <span class="hljs-keyword">return</span> response.fold(
      (failure) {
        setError(failure.message);
      },
      (todo) {
        _todosDataService.add(todo);
        _navigationService.back&lt;<span class="hljs-keyword">void</span>&gt;();
      },
    );
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _updateTodo() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (_todo == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">final</span> dto = UpdateTodoDto(
      title: title,
      description: description,
      completed: completed,
    );
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> runBusyFuture(
      _repository.updateTodo(id: _todo!.id, updateTodoDto: dto),
    );
    <span class="hljs-keyword">return</span> response.fold(
      (failure) {
        setError(failure.message);
      },
      (todo) {
        _todosDataService.add(todo);
        _navigationService.back&lt;<span class="hljs-keyword">void</span>&gt;();
      },
    );
  }
</code></pre>
<p>The <code>handleTodo</code> method is responsible for creating or updating a to-do task based on the optional <code>Todo</code> provided in the <code>init</code> method.</p>
<p>It calls the private methods <code>_createTodo</code> and <code>_updateTodo</code> internally. <code>_createTodo</code> creates a data transfer object, passes it to the repository, and navigates back upon successful creation.</p>
<p>Similarly, <code>_updateTodo</code> updates the provided to-do task and navigates back upon successful update.</p>
<p>Finally, we have a fully functional app.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/shorts/oy4_PN1cbyI?feature=share">https://www.youtube.com/shorts/oy4_PN1cbyI?feature=share</a></div>
<p> </p>
<hr />
<h1 id="heading-bonus">Bonus 🎉</h1>
<p>To ensure our code follows best practices and is consistent throughout our project, let's add some linting with <a target="_blank" href="https://pub.dev/packages/very_good_analysis">very_good_analysis</a> in <code>analysis_options.yaml</code>.</p>
<p>Linting is an important step in the development process as it helps catch potential errors and enforces a set of coding standards. This results in more readable and maintainable code, making it easier for both you and other developers to understand and work with the codebase.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">include:</span> <span class="hljs-string">package:very_good_analysis/analysis_options.yaml</span>
<span class="hljs-attr">analyzer:</span>
  <span class="hljs-attr">exclude:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'**/*.router.dart'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'**/*.locator.dart'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'**/*.config.dart'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'**/*.g.dart'</span>
<span class="hljs-attr">linter:</span>
  <span class="hljs-attr">rules:</span>
    <span class="hljs-attr">public_member_api_docs:</span> <span class="hljs-literal">false</span>
</code></pre>
<hr />
<p>🎉 Congrats, we've made it to the end of Part 5! 🚀 We've successfully built the front end of our full-stack to-do application using Flutter. From importing necessary dependencies to connecting with the backend, we've covered it all. We've even implemented features such as creating and updating todos, handling errors and creating a reactive data service.</p>
<p>It's been an amazing journey, and we've come a long way since Part 1. We've built a complete full-stack to-do application using Dart and Flutter. But our work doesn't stop here. In this series, we haven't covered testing yet, but it's a crucial part of building any application. If you're interested in learning more about testing your code in all the modules we've built in this series, let us know in the comments.</p>
<p>As always, you can refer back to the GitHub repo for this tutorial at <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart">https://github.com/saileshbro/full_stack_todo_dart</a> if you need any help. Don't forget to give it a ⭐ and spread the love for the project. Also, if you're stuck or need help, feel free to open an issue or better yet, send a pull request.</p>
<p>Thank you for joining me in this adventure, let's make more amazing apps together. Happy coding! 💻</p>
]]></content:encoded></item><item><title><![CDATA[🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 4]]></title><description><![CDATA[In the previous part, we set up models, data sources, repositories, exceptions and failures for the full-stack to-do application. We also made some changes to our packages. In this part, we will:

Connect to a Postgres database

Complete the backend ...]]></description><link>https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-4</link><guid isPermaLink="true">https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-4</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Mon, 02 Jan 2023 06:09:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672575039013/d1d63842-e6b8-49c0-b40e-9d6aae5224ca.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous part, we set up models, data sources, repositories, exceptions and failures for the full-stack to-do application. We also made some changes to our packages. In this part, we will:</p>
<ul>
<li><p>Connect to a <a target="_blank" href="https://www.postgresql.org/">Postgres</a> database</p>
</li>
<li><p>Complete the backend routes</p>
</li>
<li><p>Add a new controller to handle HTTP requests</p>
</li>
<li><p>Add necessary failures and exceptions</p>
</li>
<li><p>Fully implement CRUD operations for the to-do application</p>
</li>
</ul>
<h1 id="heading-implementing-the-backend">🚀 Implementing the Backend</h1>
<p>It's time to tackle the backend of our to-do app! 💪 Let's get coding! 💻</p>
<h2 id="heading-importing-necessary-dependencies">Importing necessary dependencies 📦</h2>
<blockquote>
<p>Time to bring in the big guns! 💪 Let's import those dependencies</p>
</blockquote>
<p>We will import all the necessary dependencies in the <code>pubspec.yaml</code> file.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">dart_frog:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">data_source:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../data_source</span>
  <span class="hljs-attr">dotenv:</span> <span class="hljs-string">^4.0.1</span>
  <span class="hljs-attr">either_dart:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">exceptions:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../exceptions</span>
  <span class="hljs-attr">failures:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../failures</span>
  <span class="hljs-attr">http:</span> <span class="hljs-string">^0.13.5</span>
  <span class="hljs-attr">models:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../models</span>
  <span class="hljs-attr">postgres:</span> <span class="hljs-string">^2.5.2</span>
  <span class="hljs-attr">repository:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../repository</span>
  <span class="hljs-attr">typedefs:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../typedefs</span>
</code></pre>
<h2 id="heading-setting-up-the-postgres-database">🛠️ Setting up the Postgres database</h2>
<blockquote>
<p>💾 Let's create a database</p>
</blockquote>
<p>We will be using the <a target="_blank" href="https://www.postgresql.org/">PostgreSQL</a> database for this tutorial. To use the <a target="_blank" href="https://www.postgresql.org/">PostgreSQL</a> database for this tutorial, we can set up a test database on <a target="_blank" href="http://elephantsql.com">elephantsql.com</a>. Simply sign up on the website and click on the option to create a new instance. You should see something similar to this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672567276279/aed8bde3-4e4f-4be1-8870-249f1ccacf4f.png" alt="Elephant SQL New DB" class="image--center mx-auto" /></p>
<p>Once you add a name, you will be prompted to choose a name for your instance and a region that is nearest to you. I have selected the <code>AP-East-1</code> region, but you can choose any region that you prefer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672567316874/4a5d9897-4fad-44a7-8aba-97aac2734568.png" alt="Elephant SQL Select Region" class="image--center mx-auto" /></p>
<p>Once you have chosen a name and selected a region, click the <code>Create Instance</code> button. This will redirect you to a dashboard where you can click on the instance name to access the credentials for the database you have just created. The credentials should look similar to this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672567334246/aba373d5-7eb6-417b-872e-70e3c7a53b70.png" alt="Elephant SQL Creds" class="image--center mx-auto" /></p>
<h2 id="heading-connecting-to-the-database">Connecting to the Database 🔌</h2>
<blockquote>
<p>💻 Setting up our database connection like a boss</p>
</blockquote>
<h3 id="heading-setting-up-environment">Setting up environment 🌿</h3>
<p>Now that we have created a database, we can connect to it from our application. To do this, we will create a new file <code>.env</code> at the root of the <code>backend</code> directory. This file will contain the credentials for the database that we have just created. The <code>.env</code> file should look similar to this.</p>
<p>Once this is done, we will use the <a target="_blank" href="https://pub.dev/packages/dotenv">dotenv</a> package to load the environment variables from the <code>.env</code> file. We will also use the <a target="_blank" href="https://pub.dev/packages/postgres">postgres</a> package to connect to the database. You can run the following command in the backend directory to add the necessary dependencies.</p>
<pre><code class="lang-bash">dart pub add dotenv postgres
</code></pre>
<p>This is how <code>.env</code> file should look. Make sure to use your database credentials</p>
<pre><code class="lang-apache"><span class="hljs-attribute">DB_HOST</span>=tiny.db.elephantsql.com
<span class="hljs-attribute">DB_PORT</span>=<span class="hljs-number">5432</span>
<span class="hljs-attribute">DB_DATABASE</span>=asztgqfq
<span class="hljs-attribute">DB_USERNAME</span>=asztgqfq
<span class="hljs-attribute">DB_PASSWORD</span>=PcIXbvXQLwpEON<span class="hljs-number">61</span>GVPzqs<span class="hljs-number">0</span>zHyzHyHZc
</code></pre>
<h3 id="heading-creating-database-connection">Creating database connection 🔗</h3>
<p>Now, create a <code>backend/lib/db/database_connection.dart</code> file and add the following code.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:developer'</span>;

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseConnection</span> </span>{
  DatabaseConnection(<span class="hljs-keyword">this</span>._dotEnv) {
    _host = _dotEnv[<span class="hljs-string">'DB_HOST'</span>] ?? <span class="hljs-string">'localhost'</span>;
    _port = <span class="hljs-built_in">int</span>.tryParse(_dotEnv[<span class="hljs-string">'DB_PORT'</span>] ?? <span class="hljs-string">''</span>) ?? <span class="hljs-number">5432</span>;
    _database = _dotEnv[<span class="hljs-string">'DB_DATABASE'</span>] ?? <span class="hljs-string">'test'</span>;
    _username = _dotEnv[<span class="hljs-string">'DB_USERNAME'</span>] ?? <span class="hljs-string">'test'</span>;
    _password = _dotEnv[<span class="hljs-string">'DB_PASSWORD'</span>] ?? <span class="hljs-string">'test'</span>;
  }

  <span class="hljs-keyword">final</span> DotEnv _dotEnv;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> _host;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> _port;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> _database;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> _username;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> _password;
  PostgreSQLConnection? _connection;

  PostgreSQLConnection <span class="hljs-keyword">get</span> db =&gt;
      _connection ??= <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Database connection not initialized'</span>);

  Future&lt;<span class="hljs-keyword">void</span>&gt; connect() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      _connection = PostgreSQLConnection(
        _host,
        _port,
        _database,
        username: _username,
        password: _password,
      );
      log(<span class="hljs-string">'Database connection successful'</span>);
      <span class="hljs-keyword">return</span> _connection!.open();
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">'Database connection failed: <span class="hljs-subst">$e</span>'</span>);
    }
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; close() =&gt; _connection!.close();
}
</code></pre>
<p>Whenever we want to query, we will open a connection to the database and close it once we are done. This will ensure that we are not keeping the connection open for too long.</p>
<h3 id="heading-injecting-databaseconnection-through-provider">💉Injecting <code>DatabaseConnection</code> through <code>provider</code></h3>
<p>Create a new file <code>routes/_middleware.dart</code> and add the following code.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dotenv/dotenv.dart'</span>;

<span class="hljs-keyword">final</span> env = DotEnv()..load();
<span class="hljs-keyword">final</span> _db = DatabaseConnection(env);

Handler middleware(Handler handler) {
  <span class="hljs-keyword">return</span> handler.use(provider&lt;DatabaseConnection&gt;((_) =&gt; _db));
}
</code></pre>
<p>This middleware is used to provide the <code>DatabaseConnection</code> instance to other parts of the application through a <code>provider</code>.</p>
<h3 id="heading-fetching-databaseconnection-from-provider">Fetching <code>DatabaseConnection</code> from <code>provider</code> 🔍</h3>
<p>Now in <code>routes/index.dart</code> you can get this <code>DatabaseConnection</code> from <code>RequestContext</code> and use it to query the database.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

Future&lt;Response&gt; onRequest(RequestContext context) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> connection = context.read&lt;DatabaseConnection&gt;();
  <span class="hljs-keyword">await</span> connection.connect();
  <span class="hljs-keyword">final</span> response =
      <span class="hljs-keyword">await</span> connection.db.query(<span class="hljs-string">'select * from information_schema.tables'</span>);
  <span class="hljs-keyword">await</span> connection.close();
  <span class="hljs-keyword">return</span> Response.json(body: response.map((e) =&gt; e.toColumnMap()).toList());
}
</code></pre>
<p>If you run <code>dart_frog dev</code>, then you should be able to open <code>http://localhost:8080</code> and see the following output.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672567384241/52a4d34e-c9b0-4237-bf5c-c399941321f9.png" alt="Database Connection Successful" class="image--center mx-auto" /></p>
<p>We have successfully connected to the database.</p>
<h3 id="heading-create-database-table">Create Database Table 📝</h3>
<blockquote>
<p>🗃️ Let's build our database table</p>
</blockquote>
<p>Before implementing the <code>TodoDataSource</code>, we will need to create the table in the database. To do this, open the <a target="_blank" href="elephantsql.com">elephantsql.com</a> dashboard and click on the <code>BROWSER</code> tab.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672567429967/334dd3bf-3d31-49fd-abc2-d2235d7865b4.png" alt="Elephant SQL Query Execution" class="image--center mx-auto" /></p>
<p>Then, execute the following query:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> todos(
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">SERIAL</span> PRIMARY <span class="hljs-keyword">KEY</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    title <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">255</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    description <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    completed <span class="hljs-built_in">BOOL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-literal">FALSE</span>,
    created_at <span class="hljs-built_in">timestamp</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">current_timestamp</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    updated_at <span class="hljs-built_in">timestamp</span> <span class="hljs-literal">null</span>
);
</code></pre>
<p>This <a target="_blank" href="https://www.tutorialspoint.com/postgresql/postgresql_create_table.htm">PostgreSQL query</a> creates a new table called todos with the following columns:</p>
<ul>
<li><p><code>id</code>: an integer column that is the table's primary key and is generated automatically by the database (using the <a target="_blank" href="https://www.educba.com/postgresql-serial/"><code>SERIAL</code></a> type). The <a target="_blank" href="https://www.tutorialspoint.com/postgresql/postgresql_constraints.htm"><code>NOT NULL</code></a> constraint ensures that this column cannot contain a <code>NULL</code> value.</p>
</li>
<li><p><code>title</code>: a string column with a maximum length of 255 characters. The <a target="_blank" href="https://www.tutorialspoint.com/postgresql/postgresql_constraints.htm"><code>NOT NULL</code></a> constraint ensures that this column cannot contain a <code>NULL</code> value.</p>
</li>
<li><p><code>description</code>: a text column. The <code>NOT NULL</code> constraint ensures that this column cannot contain a <code>NULL</code> value.</p>
</li>
<li><p><code>completed</code>: a boolean column with a default value of <code>FALSE</code>.</p>
</li>
<li><p><code>created_at</code>: a <a target="_blank" href="https://www.educba.com/postgresql-timestamp/"><code>timestamp</code></a> column with a default value of the current timestamp. The <code>NOT NULL</code> constraint ensures that this column cannot contain a <code>NULL</code> value.</p>
</li>
<li><p><code>updated_at</code>: a <a target="_blank" href="https://www.educba.com/postgresql-timestamp/"><code>timestamp</code></a> column that can contain a <code>NULL</code> value.</p>
</li>
</ul>
<p>The <code>todos</code> table will be used to store the to-do items in our application. Each row in the table represents a single to-do item, and the table's columns store the data for that to-do item.</p>
<p>Once you run the query, you should see a toast message at the top.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672567447946/08dd1b1d-1c5b-4f4d-bd8b-7562dc7ffc3f.png" alt="Elephant SQL Query Success" class="image--center mx-auto" /></p>
<h2 id="heading-implementing-tododatasource">Implementing <code>TodoDataSource</code> 💪</h2>
<blockquote>
<p>Cooking up some <code>TodoDataSource</code> magic 🔮</p>
</blockquote>
<p>We will implement the todo data source in <code>backend/lib/todo/data_source/todo_data_source_impl.dart</code>. This file will contain the implementation of the <code>TodoDataSource</code> interface. We will pass a <code>DatabaseConnection</code> as a dependency to this class.</p>
<p>Create <code>TodoDataSourceImpl</code> and implement <code>TodoDataSource</code> interface, and override necessary methods. The empty implementation should look like this.</p>
<h3 id="heading-empty-tododatasource-implementation">Empty <code>TodoDataSource</code> implementation</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoDataSourceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TodoDataSource</span> </span>{
  <span class="hljs-keyword">const</span> TodoDataSourceImpl(<span class="hljs-keyword">this</span>._databaseConnection);
  <span class="hljs-keyword">final</span> DatabaseConnection _databaseConnection;

  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; createTodo(CreateTodoDto todo) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; deleteTodoById(TodoId id) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt; getAllTodo() {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; getTodoById(TodoId id) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; updateTodo({<span class="hljs-keyword">required</span> TodoId id, <span class="hljs-keyword">required</span> UpdateTodoDto todo}) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }
}
</code></pre>
<h3 id="heading-injecting-tododatasource-through-provider">💉Injecting <code>TodoDataSource</code> through <code>provider</code></h3>
<blockquote>
<p>Let's add this to our global middleware in <code>routes/_middleware.dart</code> file</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/data_source/todo_data_source_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dotenv/dotenv.dart'</span>;
<span class="hljs-keyword">final</span> env = DotEnv()..load();
<span class="hljs-keyword">final</span> _db = DatabaseConnection(env);
<span class="hljs-keyword">final</span> _ds = TodoDataSourceImpl(_db);

Handler middleware(Handler handler) {
  <span class="hljs-keyword">return</span> handler
      .use(requestLogger())
      .use(provider&lt;DatabaseConnection&gt;((_) =&gt; _db))
      .use(provider&lt;TodoDataSource&gt;((_) =&gt; _ds));
}
</code></pre>
<h3 id="heading-createtodo-implementation"><code>createTodo</code> implementation</h3>
<p>Now we will implement the <code>createTodo</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; createTodo(CreateTodoDto todo) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.connect();
      <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _databaseConnection.db.query(
        <span class="hljs-string">'''
        INSERT INTO todos (title, description, completed, created_at)
        VALUES (@title, @description, @completed, @created_at) RETURNING *
        '''</span>,
        substitutionValues: {
          <span class="hljs-string">'title'</span>: todo.title,
          <span class="hljs-string">'description'</span>: todo.description,
          <span class="hljs-string">'completed'</span>: <span class="hljs-keyword">false</span>,
          <span class="hljs-string">'created_at'</span>: <span class="hljs-built_in">DateTime</span>.now(),
        },
      );
      <span class="hljs-keyword">if</span> (result.affectedRowCount == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> ServerException(<span class="hljs-string">'Failed to create todo'</span>);
      }
      <span class="hljs-keyword">final</span> todoMap = result.first.toColumnMap();
      <span class="hljs-keyword">return</span> Todo(
        id: todoMap[<span class="hljs-string">'id'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">int</span>,
        title: todoMap[<span class="hljs-string">'title'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String</span>,
        description: todoMap[<span class="hljs-string">'description'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">String</span>,
        createdAt: todoMap[<span class="hljs-string">'created_at'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">DateTime</span>,
      );
    } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.close();
    }
  }
</code></pre>
<p>First, the method establishes a connection to the database using the <code>_databaseConnection</code> object. Then, it uses the query method on the <code>db</code> object to execute an <code>INSERT</code> statement The <code>substitutionValues</code> parameter is used to bind the values from the <code>CreateTodoDto</code> .</p>
<p>If the <code>INSERT</code> statement is successful, the method retrieves the inserted row from the database using the <code>RETURNING *</code> clause and converts it to a map using the <code>toColumnMap</code> method. The method then uses this map to create and return a new <code>Todo</code> object.</p>
<h3 id="heading-getalltodo-implementation"><code>getAllTodo</code> implementation</h3>
<p>Now we will implement the <code>getAllTodo</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt; getAllTodo() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.connect();
      <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _databaseConnection.db.query(
        <span class="hljs-string">'SELECT * FROM todos'</span>,
      );
      <span class="hljs-keyword">final</span> data =
          result.map((e) =&gt; e.toColumnMap()).map(Todo.fromJson).toList();
      <span class="hljs-keyword">return</span> data;
    } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.close();
    }
  }
</code></pre>
<p>This <code>getAllTodo</code> method is used to retrieve a list of all the to-do items stored in the database. This executes a <code>SELECT</code> query to retrieve all rows from the <code>todos</code> table, maps each row to a <code>Todo</code> object using the <code>Todo.fromJson</code> function, and returns the list of <code>Todo</code> objects.</p>
<h3 id="heading-gettodobyid-implementation"><code>getTodoById</code> implementation 🔍</h3>
<p>Now we will implement the <code>getTodoById</code> method.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
Future&lt;Todo&gt; getTodoById(TodoId id) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">await</span> _databaseConnection.connect();
    <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _databaseConnection.db.query(
      <span class="hljs-string">'SELECT * FROM todos WHERE id = @id'</span>,
      substitutionValues: {<span class="hljs-string">'id'</span>: id},
    );
    <span class="hljs-keyword">if</span> (result.isEmpty) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> NotFoundException(<span class="hljs-string">'Todo not found'</span>);
    }
    <span class="hljs-keyword">return</span> Todo.fromJson(result.first.toColumnMap());
  } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
  } <span class="hljs-keyword">finally</span> {
    <span class="hljs-keyword">await</span> _databaseConnection.close();
  }
}
</code></pre>
<p>We execute a <code>SELECT</code> query that selects from <code>todos</code> table where the id equals the provided id. If the query returns an empty result set, we throw a <code>NotFoundException</code>, indicating that the requested to-do item could not be found, else it returns the mapped todo object.</p>
<h3 id="heading-updatetodo-implementation"><code>updateTodo</code> implementation 🔧</h3>
<p>Here is the implementation of the <code>updateTodo</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Todo&gt; updateTodo({
    <span class="hljs-keyword">required</span> TodoId id,
    <span class="hljs-keyword">required</span> UpdateTodoDto todo,
  }) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.connect();
      <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> _databaseConnection.db.query(
        <span class="hljs-string">'''
        UPDATE todos
        SET title = COALESCE(@new_title, title),
            description = COALESCE(@new_description, description),
            completed = COALESCE(@new_completed, completed),
            updated_at = current_timestamp
        WHERE id = @id
        RETURNING *
        '''</span>,
        substitutionValues: {
          <span class="hljs-string">'id'</span>: id,
          <span class="hljs-string">'new_title'</span>: todo.title,
          <span class="hljs-string">'new_description'</span>: todo.description,
          <span class="hljs-string">'new_completed'</span>: todo.completed,
        },
      );
      <span class="hljs-keyword">if</span> (result.isEmpty) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> NotFoundException(<span class="hljs-string">'Todo not found'</span>);
      }
      <span class="hljs-keyword">return</span> Todo.fromJson(result.first.toColumnMap());
    } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.close();
    }
  }
</code></pre>
<p>It executes an <code>UPDATE</code> query on the todos table. If no value is provided for a column, then the <code>COALESCE</code> function is used to keep the existing value in the database unchanged. The <code>updated_at</code> column is set to the <code>current_timestamp</code>. If the result set is empty, it means that no row with the given id was found, so a <code>NotFoundException</code> is thrown.</p>
<h3 id="heading-deletetodobyid-implementation"><code>deleteTodoById</code> implementation 🗑️</h3>
<p>Now, we will implement the <code>deleteTodoById</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; deleteTodoById(TodoId id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.connect();
      <span class="hljs-keyword">await</span> _databaseConnection.db.query(
        <span class="hljs-string">'''
        DELETE FROM todos
        WHERE id = @id
        '''</span>,
        substitutionValues: {<span class="hljs-string">'id'</span>: id},
      );
    } <span class="hljs-keyword">on</span> PostgreSQLException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">throw</span> ServerException(e.message ?? <span class="hljs-string">'Unexpected error'</span>);
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> _databaseConnection.close();
    }
  }
</code></pre>
<p>If the delete statement is successful, the method does not return anything.</p>
<h3 id="heading-postgressqlexception-handling"><code>PostgresSQLException</code> handling 🚨</h3>
<p>In all of the methods above, if there is an exception while querying, like <code>PostgresSQLException</code>, it is caught and a <code>ServerException</code> is thrown with a more general error message. Finally, the database connection is closed before the method finishes executing.</p>
<h2 id="heading-implementing-todorepository">Implementing <code>TodoRepository</code> 💪</h2>
<blockquote>
<p>💪 Time to make that <code>TodoRepository</code> do some work!</p>
</blockquote>
<p>We will implement the todo repository in <code>backend/lib/todo/repositories/todo_repository_impl.dart</code>. This file will contain the implementation of the <code>TodoRepository</code> interface. We will pass a <code>TodoDataSource</code> as a dependency to this class.</p>
<h3 id="heading-empty-todorepository-implementation">Empty <code>TodoRepository</code> implementation</h3>
<p>Create <code>TodoRepositoryImpl</code> and implement <code>TodoRepository</code> interface, and override necessary methods. The empty implementation should look like this.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/src/typedefs.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoRepositoryImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TodoRepository</span> </span>{
  TodoRepositoryImpl(<span class="hljs-keyword">this</span>.dataSource);

  <span class="hljs-keyword">final</span> TodoDataSource dataSource;

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; createTodo(CreateTodoDto createTodoDto) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, <span class="hljs-keyword">void</span>&gt;&gt; deleteTodo(TodoId id) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; getTodoById(TodoId id) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, <span class="hljs-built_in">List</span>&lt;Todo&gt;&gt;&gt; getTodos() {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; updateTodo({
    <span class="hljs-keyword">required</span> TodoId id,
    <span class="hljs-keyword">required</span> UpdateTodoDto updateTodoDto,
  }) {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }
}
</code></pre>
<h3 id="heading-injecting-todorepository-through-provider">💉Injecting <code>TodoRepository</code> through <code>provider</code></h3>
<p>Before implementing the methods of <code>TodoRepository</code>, we will add it to our global middleware so that we can access it from our routes.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/data_source/todo_data_source_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/repositories/todo_repository_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dotenv/dotenv.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;
<span class="hljs-keyword">final</span> env = DotEnv()..load();
<span class="hljs-keyword">final</span> _db = DatabaseConnection(env);
<span class="hljs-keyword">final</span> _ds = TodoDataSourceImpl(_db);
<span class="hljs-keyword">final</span> _repo = TodoRepositoryImpl(_ds);

Handler middleware(Handler handler) {
  <span class="hljs-keyword">return</span> handler
      .use(requestLogger())
      .use(provider&lt;DatabaseConnection&gt;((_) =&gt; _db))
      .use(provider&lt;TodoDataSource&gt;((_) =&gt; _ds))
      .use(provider&lt;TodoRepository&gt;((_) =&gt; _repo));
}
</code></pre>
<h3 id="heading-createtodo-implementation-1"><code>createTodo</code> implementation</h3>
<p>Now we will implement the <code>createTodo</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; createTodo(CreateTodoDto createTodoDto) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> todo = <span class="hljs-keyword">await</span> dataSource.createTodo(createTodoDto);
      <span class="hljs-keyword">return</span> Right(todo);
    } <span class="hljs-keyword">on</span> ServerException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(message: e.message),
      );
    }
  }
</code></pre>
<p>In this code, we are implementing the <code>createTodo</code> method which is part of the <code>TodoRepository</code> interface using <code>dataSource.createTodo</code> method. The <code>dataSource.createTodo</code> method is responsible for inserting the todo into the database. If the insertion is successful, it returns the todo object.</p>
<h3 id="heading-gettodobyid-implementation-1"><code>getTodoById</code> implementation</h3>
<p>Now we will implement the <code>getTodoById</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; getTodoById(TodoId id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> dataSource.getTodoById(id);
      <span class="hljs-keyword">return</span> Right(res);
    } <span class="hljs-keyword">on</span> NotFoundException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(
          message: e.message,
          statusCode: e.statusCode,
        ),
      );
    } <span class="hljs-keyword">on</span> ServerException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(message: e.message),
      );
    }
  }
</code></pre>
<p>The method calls the <code>dataSource.getTodoById</code> method, which is responsible for querying the database and returning the todo object. If the todo is found, it returns the value. A <code>NotFoundException</code> is thrown when the todo with the given id is not found in the database.</p>
<h3 id="heading-gettodos-implementation"><code>getTodos</code> implementation</h3>
<p>Here, we will implement the <code>getTodos</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, <span class="hljs-built_in">List</span>&lt;Todo&gt;&gt;&gt; getTodos() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">return</span> Right(<span class="hljs-keyword">await</span> dataSource.getAllTodo());
    } <span class="hljs-keyword">on</span> ServerException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(message: e.message),
      );
    }
  }
</code></pre>
<p>We call the <code>dataSource.getAllTodo</code> method. If the method execution is successful, we return the list of todo items.</p>
<h3 id="heading-updatetodo-implementation-1"><code>updateTodo</code> implementation 🔧</h3>
<p>Now we will implement the <code>updateTodo</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, Todo&gt;&gt; updateTodo({
    <span class="hljs-keyword">required</span> TodoId id,
    <span class="hljs-keyword">required</span> UpdateTodoDto updateTodoDto,
  }) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">return</span> Right(
        <span class="hljs-keyword">await</span> dataSource.updateTodo(
          id: id,
          todo: updateTodoDto,
        ),
      );
    } <span class="hljs-keyword">on</span> NotFoundException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(
          message: e.message,
          statusCode: e.statusCode,
        ),
      );
    } <span class="hljs-keyword">on</span> ServerException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(message: e.message),
      );
    }
  }
</code></pre>
<p>The method updates the todo using the <code>dataSource.updateTodo</code> method. If the update is successful, it returns the updated todo. If the update fails and a <code>NotFoundException</code> is thrown, it logs the error message and returns a <code>ServerFailure</code> object with the appropriate status code.</p>
<h3 id="heading-deletetodo-implementation"><code>deleteTodo</code> implementation</h3>
<p>Finally, we will implement the <code>deleteTodo</code> method.</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  Future&lt;Either&lt;Failure, <span class="hljs-keyword">void</span>&gt;&gt; deleteTodo(TodoId id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> exists = <span class="hljs-keyword">await</span> getTodoById(id);
      <span class="hljs-keyword">if</span> (exists.isLeft) <span class="hljs-keyword">return</span> exists;
      <span class="hljs-keyword">final</span> todo = <span class="hljs-keyword">await</span> dataSource.deleteTodoById(id);
      <span class="hljs-keyword">return</span> Right(todo);
    } <span class="hljs-keyword">on</span> ServerException <span class="hljs-keyword">catch</span> (e) {
      log(e.message);
      <span class="hljs-keyword">return</span> Left(
        ServerFailure(message: e.message),
      );
    }
  }
</code></pre>
<p>We check if a todo exists by calling the <code>this.getTodoById</code> method. If it does not exist, we return a <code>Failure</code>. If the todo does exist, we delete it by calling the <code>dataSource.deleteTodoById</code>.</p>
<h3 id="heading-handling-exception">Handling <code>Exception</code> 🛑</h3>
<p>The <code>dataSource</code> methods throw exceptions like <code>ServerException</code> and <code>NotFoundException</code>. We catch these exceptions and log the error message. We then return a <code>Left</code> object containing a <code>ServerFailure</code> object. The <code>ServerFailure</code> object is a custom failure type that we can use to indicate that a server error occurred.</p>
<h2 id="heading-building-the-todocontroller">Building the <code>TodoController</code> 🔨</h2>
<blockquote>
<p>Bringing it all together with our fancy new controller 🎉</p>
</blockquote>
<p>The controller will be responsible for handling HTTP requests and sending back the appropriate response. We will implement five methods in the controller:</p>
<ul>
<li><p><strong>index</strong> <code>GET /resource</code> 🔍</p>
</li>
<li><p><strong>show</strong> <code>GET /resource/{id}</code> 📖</p>
</li>
<li><p><strong>store</strong> <code>POST /resource</code> 📤</p>
</li>
<li><p><strong>update</strong> <code>PUT/PATCH /resource/{id}</code> 🔗</p>
</li>
<li><p><strong>delete</strong> <code>DELETE /resource/{id}</code> 🗑️</p>
</li>
</ul>
<p>These methods will correspond to the standard HTTP methods for retrieving, creating, updating, and deleting data. By using these methods, we can keep our code clean and organized. This approach is inspired by the <a target="_blank" href="https://laravel.com/docs/9.x/controllers">Laravel framework's API controller</a> methods.</p>
<h3 id="heading-abstract-httpcontroller">Abstract <code>HttpController</code> 🔮</h3>
<p>We will create a new abstract class in the <code>backend/lib/controller/http_controller.dart</code> called <code>HttpController</code> and which will have five methods.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;

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

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HttpController</span> </span>{
  FutureOr&lt;Response&gt; index(Request request);

  FutureOr&lt;Response&gt; store(Request request);

  FutureOr&lt;Response&gt; <span class="hljs-keyword">show</span>(Request request, <span class="hljs-built_in">String</span> id);

  FutureOr&lt;Response&gt; update(Request request, <span class="hljs-built_in">String</span> id);

  FutureOr&lt;Response&gt; destroy(Request request, <span class="hljs-built_in">String</span> id);
}
</code></pre>
<h3 id="heading-empty-todocontroller-implementation">Empty <code>TodoController</code> implementation</h3>
<p>Now for the implementation of this contract, we will create a new class <code>TodoController</code> in the <code>backend/lib/controller/todo_controller.dart</code> file. We will implement each method in the <code>TodoController</code> class.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:async'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/controller/http_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:exceptions/exceptions.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">HttpController</span> </span>{
  TodoController(<span class="hljs-keyword">this</span>._repo);

  <span class="hljs-keyword">final</span> TodoRepository _repo;
  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; index(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; <span class="hljs-keyword">show</span>(Request request, <span class="hljs-built_in">String</span> id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; destroy(Request request, <span class="hljs-built_in">String</span> id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; store(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; update(Request request, <span class="hljs-built_in">String</span> id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }

  Future&lt;Either&lt;Failure, <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;&gt;&gt; parseJson(
    Request request,
  ) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">throw</span> UnimplementedError();
  }
}
</code></pre>
<h3 id="heading-parse-request-body">Parse request body 🔬</h3>
<p>Before we implement the methods, we will create a new helper method in <code>HttpController</code> which will be responsible to parse the request body.</p>
<p>If the request body is not a valid JSON, it will return a <code>Left</code> object containing a <code>BadRequestFailure</code> object. If the request body is a valid JSON, it will return a <code>Right</code> object containing the parsed JSON.</p>
<p>Add the following method to the <code>HttpController</code> class.</p>
<pre><code class="lang-dart">  Future&lt;Either&lt;Failure, <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;&gt;&gt; parseJson(
    Request request,
  ) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> body = <span class="hljs-keyword">await</span> request.body();
      <span class="hljs-keyword">if</span> (body.isEmpty) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> BadRequestException(message: <span class="hljs-string">'Invalid body'</span>);
      }
      <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json;
      <span class="hljs-keyword">try</span> {
        json = jsonDecode(body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;;
        <span class="hljs-keyword">return</span> Right(json);
      } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> BadRequestException(message: <span class="hljs-string">'Invalid body'</span>);
      }
    } <span class="hljs-keyword">on</span> BadRequestException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> Left(
        ValidationFailure(
          message: e.message,
          errors: {},
        ),
      );
    }
  }
</code></pre>
<h3 id="heading-injecting-todocontroller-through-provider">💉Injecting <code>TodoController</code> through <code>provider</code></h3>
<blockquote>
<p>Let's add this to our global middleware <code>routes/_middleware.dart</code> file.</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/db/database_connection.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/controller/todo_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/data_source/todo_data_source_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/repositories/todo_repository_impl.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:data_source/data_source.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dotenv/dotenv.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:repository/repository.dart'</span>;

<span class="hljs-keyword">final</span> env = DotEnv()..load();
<span class="hljs-keyword">final</span> _db = DatabaseConnection(env);
<span class="hljs-keyword">final</span> _ds = TodoDataSourceImpl(_db);
<span class="hljs-keyword">final</span> _repo = TodoRepositoryImpl(_ds);
<span class="hljs-keyword">final</span> _todoController = TodoController(_repo);

Handler middleware(Handler handler) {
  <span class="hljs-keyword">return</span> handler
      .use(requestLogger())
      .use(provider&lt;DatabaseConnection&gt;((_) =&gt; _db))
      .use(provider&lt;TodoDataSource&gt;((_) =&gt; _ds))
      .use(provider&lt;TodoRepository&gt;((_) =&gt; _repo))
      .use(provider&lt;TodoController&gt;((_) =&gt; _todoController));
}
</code></pre>
<blockquote>
<p>Note: We are using <code>requestLogger</code> middleware to log the request and response.</p>
</blockquote>
<h2 id="heading-implementing-todocontroller">Implementing <code>TodoController</code> 🚀</h2>
<p>We will implement the <code>TodoController</code> methods as follows:</p>
<h3 id="heading-index-implementation"><code>index</code> implementation</h3>
<p>The <code>index</code> method will be responsible for retrieving all todo items from the database. We will implement it as follows:</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; index(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> _repo.getTodos();
    <span class="hljs-keyword">return</span> res.fold(
      (left) =&gt; Response.json(
        body: {<span class="hljs-string">'message'</span>: left.message},
        statusCode: left.statusCode,
      ),
      (right) =&gt; Response.json(
        body: right.map((e) =&gt; e.toJson()).toList(),
      ),
    );
  }
</code></pre>
<p>We will call the <code>getTodos</code> method and map the response. If the failure case is returned, we will return a response with an error status code and the error message. Else, we will return a <code>200</code> status code and the list of todos.</p>
<h3 id="heading-show-implementation"><code>show</code> implementation</h3>
<p><code>show</code> method will be responsible for retrieving a single to-do item from the database. We will implement it as follows:</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; <span class="hljs-keyword">show</span>(Request request, <span class="hljs-built_in">String</span> id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> todoId = mapTodoId(id);
    <span class="hljs-keyword">if</span> (todoId.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: todoId.left.message},
        statusCode: todoId.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> _repo.getTodoById(todoId.right);
    <span class="hljs-keyword">return</span> res.fold(
      (left) =&gt; Response.json(
        body: {<span class="hljs-string">'message'</span>: left.message},
        statusCode: left.statusCode,
      ),
      (right) =&gt; Response.json(
        body: right.toJson(),
      ),
    );
  }
</code></pre>
<p>We will first call <code>mapTodoId</code> method to validate the <code>id</code> parameter. If it returns a failure, we will return a failure response with the status code. Then we will get the todo item from the repository and return the todo item if it is found. Else, we will return a failure response with the status code.</p>
<h3 id="heading-store-implementation"><code>store</code> implementation</h3>
<p><code>store</code> method is as follows</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; store(Request request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> parsedBody = <span class="hljs-keyword">await</span> parseJson(request);
    <span class="hljs-keyword">if</span> (parsedBody.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: parsedBody.left.message},
        statusCode: parsedBody.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> json = parsedBody.right;
    <span class="hljs-keyword">final</span> createTodoDto = CreateTodoDto.validated(json);
    <span class="hljs-keyword">if</span> (createTodoDto.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {
          <span class="hljs-string">'message'</span>: createTodoDto.left.message,
          <span class="hljs-string">'errors'</span>: createTodoDto.left.errors,
        },
        statusCode: createTodoDto.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> _repo.createTodo(createTodoDto.right);
    <span class="hljs-keyword">return</span> res.fold(
      (left) =&gt; Response.json(
        body: {<span class="hljs-string">'message'</span>: left.message},
        statusCode: left.statusCode,
      ),
      (right) =&gt; Response.json(
        body: right.toJson(),
        statusCode: HttpStatus.created,
      ),
    );
  }
</code></pre>
<p>If <code>parseJson</code> resolves to a failure, we will return a response with an error status code and message.</p>
<p>We will pass the JSON object to the <code>CreateTodoDto.validated</code> method. If this returns a failure, we will return a response with an error status code and message, here it will be <code>ValidationFailure</code>.</p>
<p>We will pass the DTO to <code>createTodo</code> method of the <code>TodoRepository</code>. If creating fails we will return a response with an error status code and message.</p>
<p>If everything goes right, we will return a response with a <code>201</code> status code and the to-do item.</p>
<h3 id="heading-destroy-implementation"><code>destroy</code> implementation</h3>
<p><code>destroy</code> method is as follows</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; destroy(Request request, <span class="hljs-built_in">String</span> id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> todoId = mapTodoId(id);
    <span class="hljs-keyword">if</span> (todoId.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: todoId.left.message},
        statusCode: todoId.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> _repo.deleteTodo(todoId.right);
    <span class="hljs-keyword">return</span> res.fold(
      (left) =&gt; Response.json(
        body: {<span class="hljs-string">'message'</span>: left.message},
        statusCode: left.statusCode,
      ),
      (right) =&gt; Response.json(body: {<span class="hljs-string">'message'</span>: <span class="hljs-string">'OK'</span>}),
    );
  }
</code></pre>
<p>We will first call <code>mapTodoId</code> method to validate the <code>id</code> parameter. If it returns a failure, we will return a failure response with the status code. Then we will get the delete todo item from the repository and return OK with 200 status code if. If there is a failure, we will return a failure response with the status code.</p>
<h3 id="heading-update-implementation"><code>update</code> implementation</h3>
<p><code>update</code> method will be responsible for updating a single to-do item from the database. We will implement it as follows:</p>
<pre><code class="lang-dart">  <span class="hljs-meta">@override</span>
  FutureOr&lt;Response&gt; update(Request request, <span class="hljs-built_in">String</span> id) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> parsedBody = <span class="hljs-keyword">await</span> parseJson(request);
    <span class="hljs-keyword">final</span> todoId = mapTodoId(id);
    <span class="hljs-keyword">if</span> (todoId.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: todoId.left.message},
        statusCode: todoId.left.statusCode,
      );
    }
    <span class="hljs-keyword">if</span> (parsedBody.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'message'</span>: parsedBody.left.message},
        statusCode: parsedBody.left.statusCode,
      );
    }

    <span class="hljs-keyword">final</span> json = parsedBody.right;
    <span class="hljs-keyword">final</span> updateTodoDto = UpdateTodoDto.validated(json);
    <span class="hljs-keyword">if</span> (updateTodoDto.isLeft) {
      <span class="hljs-keyword">return</span> Response.json(
        body: {
          <span class="hljs-string">'message'</span>: updateTodoDto.left.message,
          <span class="hljs-string">'errors'</span>: updateTodoDto.left.errors,
        },
        statusCode: updateTodoDto.left.statusCode,
      );
    }
    <span class="hljs-keyword">final</span> res = <span class="hljs-keyword">await</span> _repo.updateTodo(
      id: todoId.right,
      updateTodoDto: updateTodoDto.right,
    );
    <span class="hljs-keyword">return</span> res.fold(
      (left) =&gt; Response.json(
        body: {<span class="hljs-string">'message'</span>: left.message},
        statusCode: left.statusCode,
      ),
      (right) =&gt; Response.json(
        body: right.toJson(),
      ),
    );
  }
</code></pre>
<p>This method is similar to the <code>store</code> method. We will first validate the <code>id</code> parameter and then the JSON body. If both are valid, we will call the <code>updateTodo</code> method of the <code>TodoRepository</code> and then resolve the failure or the updated value.</p>
<h2 id="heading-implementing-routes">Implementing Routes 🛣️</h2>
<p>We will now implement the routes. These are the routes that we will have</p>
<ul>
<li><p><strong>GET</strong> <code>/todos</code> - Get all todos</p>
</li>
<li><p><strong>GET</strong> <code>/todos/:id</code> - Get a single todo</p>
</li>
<li><p><strong>POST</strong> <code>/todos</code> - Create a todo</p>
</li>
<li><p><strong>PUT</strong> <code>/todos/:id</code> - Update a todo</p>
</li>
<li><p><strong>PATCH</strong> <code>/todos/:id</code> - Update a todo</p>
</li>
<li><p><strong>DELETE</strong> <code>/todos/:id</code> - Delete a todo</p>
</li>
</ul>
<p><code>dart_frog</code> has a file system routing. For example, let's say we need to create <code>todos/1</code> route. We will create a file <code>routes/todos/[id].dart</code> and it will be mapped to <code>todos/1</code> route.</p>
<h3 id="heading-implementing-todos-route">Implementing <code>todos/</code> route</h3>
<p>To implement all the HTTP methods related to <code>todos/</code> route, we will create a new file <code>routes/todos/index.dart</code>.</p>
<p>We will create a file <code>routes/todos/index.dart</code> and implement the routes as follows:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/controller/todo_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

Future&lt;Response&gt; onRequest(RequestContext context) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> controller = context.read&lt;TodoController&gt;();
  <span class="hljs-keyword">switch</span> (context.request.method) {
    <span class="hljs-keyword">case</span> HttpMethod.<span class="hljs-keyword">get</span>:
      <span class="hljs-keyword">return</span> controller.index(context.request);
    <span class="hljs-keyword">case</span> HttpMethod.post:
      <span class="hljs-keyword">return</span> controller.store(context.request);
    <span class="hljs-keyword">case</span> HttpMethod.put:
    <span class="hljs-keyword">case</span> HttpMethod.patch:
    <span class="hljs-keyword">case</span> HttpMethod.delete:
    <span class="hljs-keyword">case</span> HttpMethod.head:
    <span class="hljs-keyword">case</span> HttpMethod.options:
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'error'</span>: <span class="hljs-string">'👀 Looks like you are lost 🔦'</span>},
        statusCode: HttpStatus.methodNotAllowed,
      );
  }
}
</code></pre>
<p>Here, we are getting the <code>TodoController</code> from the <code>context</code> and mapping the respective HTTP method to the controller method. We are also handling cases when the HTTP method is not allowed.</p>
<h3 id="heading-implementing-todosid-route">Implementing <code>todos/:id</code> route</h3>
<p>Similarly, we will create a file <code>routes/todos/[id].dart</code> and implement the routes as follows:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:backend/todo/controller/todo_controller.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:dart_frog/dart_frog.dart'</span>;

Future&lt;Response&gt; onRequest(RequestContext context, <span class="hljs-built_in">String</span> id) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">final</span> todoController = context.read&lt;TodoController&gt;();
  <span class="hljs-keyword">switch</span> (context.request.method) {
    <span class="hljs-keyword">case</span> HttpMethod.<span class="hljs-keyword">get</span>:
      <span class="hljs-keyword">return</span> todoController.<span class="hljs-keyword">show</span>(context.request, id);
    <span class="hljs-keyword">case</span> HttpMethod.put:
    <span class="hljs-keyword">case</span> HttpMethod.patch:
      <span class="hljs-keyword">return</span> todoController.update(context.request, id);
    <span class="hljs-keyword">case</span> HttpMethod.delete:
      <span class="hljs-keyword">return</span> todoController.destroy(context.request, id);
    <span class="hljs-keyword">case</span> HttpMethod.head:
    <span class="hljs-keyword">case</span> HttpMethod.options:
    <span class="hljs-keyword">case</span> HttpMethod.post:
      <span class="hljs-keyword">return</span> Response.json(
        body: {<span class="hljs-string">'error'</span>: <span class="hljs-string">'👀 Looks like you are lost 🔦'</span>},
        statusCode: HttpStatus.methodNotAllowed,
      );
  }
}
</code></pre>
<p>Here, we will get the <code>id</code> parameter from the route as a second parameter in <code>onRequest</code> method as a string. This can be anything. This explains the use of <code>mapTodoId</code> function in <code>typedefs</code> package.</p>
<p>We will pass the <code>id</code> parameter to the controller methods. We will also handle the case when the HTTP method is not allowed.</p>
<h3 id="heading-implementing-route">Implementing <code>/</code> route</h3>
<p>Finally, we will update <code>routes/index.dart</code> file to return <code>methodNotAllowed</code> response. This is our <code>/</code> route, which will be handled as follows:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

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

Future&lt;Response&gt; onRequest(RequestContext context) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">return</span> Response.json(
    body: {<span class="hljs-string">'error'</span>: <span class="hljs-string">'👀 Looks like you are lost 🔦'</span>},
    statusCode: HttpStatus.methodNotAllowed,
  );
}
</code></pre>
<h1 id="heading-testing-backend">🧪 Testing backend</h1>
<blockquote>
<p>Time to put our backend to the test! 🔍</p>
</blockquote>
<p>We will run some e2e tests, to verify the backend works fine. create a file <code>backend/e2e/routes_test.dart</code> and implement the tests as follows:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:test/test.dart'</span>;

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-keyword">late</span> Todo createdTodo;
  tearDownAll(() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos'</span>));
    <span class="hljs-keyword">final</span> todos = (jsonDecode(response.body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>)
        .map((e) =&gt; Todo.fromJson(e <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;))
        .toList();
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> todo <span class="hljs-keyword">in</span> todos) {
      <span class="hljs-keyword">await</span> http.delete(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos/<span class="hljs-subst">${todo.id}</span>'</span>));
    }
  });
  group(<span class="hljs-string">'E2E -'</span>, () {
    test(<span class="hljs-string">'GET /todos returns empty list of todos'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos'</span>));
      expect(response.statusCode, HttpStatus.ok);
      expect(response.body, equals(<span class="hljs-string">'[]'</span>));
    });

    test(<span class="hljs-string">'POST /todos to create a new todo'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(
        <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos'</span>),
        headers: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },
        body: jsonEncode(_createTodoDto.toJson()),
      );
      expect(response.statusCode, HttpStatus.created);
      createdTodo =
          Todo.fromJson(jsonDecode(response.body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;);
      expect(createdTodo.title, equals(_createTodoDto.title));
      expect(createdTodo.description, equals(_createTodoDto.description));
    });

    test(<span class="hljs-string">'GET /todos returns list of todos with one todo'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos'</span>));
      expect(response.statusCode, HttpStatus.ok);
      <span class="hljs-keyword">final</span> todos = (jsonDecode(response.body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>)
          .map((e) =&gt; Todo.fromJson(e <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;))
          .toList();
      expect(todos.length, equals(<span class="hljs-number">1</span>));
      expect(todos.first, equals(createdTodo));
    });

    test(<span class="hljs-string">'GET /todos/:id returns the created todo'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(
        <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos/<span class="hljs-subst">${createdTodo.id}</span>'</span>),
      );
      expect(response.statusCode, HttpStatus.ok);
      <span class="hljs-keyword">final</span> todo =
          Todo.fromJson(jsonDecode(response.body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;);
      expect(todo, equals(createdTodo));
    });

    test(<span class="hljs-string">'PUT /todos/:id to update the created todo'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> updateTodoDto = UpdateTodoDto(
        title: <span class="hljs-string">'updated title'</span>,
        description: <span class="hljs-string">'updated description'</span>,
      );
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.put(
        <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos/<span class="hljs-subst">${createdTodo.id}</span>'</span>),
        headers: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },
        body: jsonEncode(updateTodoDto.toJson()),
      );
      expect(response.statusCode, HttpStatus.ok);
      <span class="hljs-keyword">final</span> todo =
          Todo.fromJson(jsonDecode(response.body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;);
      expect(todo.title, equals(updateTodoDto.title));
      expect(todo.description, equals(updateTodoDto.description));
    });

    test(<span class="hljs-string">'PATCH /todos/:id to update the created todo'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> updateTodoDto = UpdateTodoDto(
        title: <span class="hljs-string">'UPDATED TITLE'</span>,
        description: <span class="hljs-string">'UPDATED DESCRIPTION'</span>,
      );
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.patch(
        <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos/<span class="hljs-subst">${createdTodo.id}</span>'</span>),
        headers: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },
        body: jsonEncode(updateTodoDto.toJson()),
      );
      expect(response.statusCode, HttpStatus.ok);
      <span class="hljs-keyword">final</span> todo =
          Todo.fromJson(jsonDecode(response.body) <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt;);
      expect(todo.title, equals(updateTodoDto.title));
      expect(todo.description, equals(updateTodoDto.description));
    });

    test(<span class="hljs-string">'DELETE /todos/:id to delete the created todo'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.delete(
        <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos/<span class="hljs-subst">${createdTodo.id}</span>'</span>),
      );
      expect(response.statusCode, HttpStatus.ok);
      expect(response.body, jsonEncode({<span class="hljs-string">'message'</span>: <span class="hljs-string">'OK'</span>}));
    });
    test(<span class="hljs-string">'GET /todos returns empty list of todos'</span>, () <span class="hljs-keyword">async</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(<span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'http://localhost:8080/todos'</span>));
      expect(response.statusCode, HttpStatus.ok);
      expect(response.body, equals(<span class="hljs-string">'[]'</span>));
    });
  });
}

<span class="hljs-keyword">final</span> _createTodoDto = CreateTodoDto(
  title: <span class="hljs-string">'title'</span>,
  description: <span class="hljs-string">'description'</span>,
);
</code></pre>
<p>To run the tests, first, start the backend server by running the following command:</p>
<pre><code class="lang-bash">dart_frog dev
</code></pre>
<p>And then on a new terminal run the tests:</p>
<pre><code class="lang-bash">dart <span class="hljs-built_in">test</span> e2e/routes_test.dart
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672567494374/49437eba-3aac-4eda-af60-fe4bd0850487.png" alt="Tests Passing fine" class="image--center mx-auto" /></p>
<hr />
<p>Wow, we've made it to the end of part 4! 🎉 It's been a wild ride, but we've finally completed the backend of our full-stack to-do application. We connected to a Postgres database, completed all our backend routes, and fully implemented CRUD operations. We even tested our backend to make sure everything is running smoothly.</p>
<p>But we're not done yet! In the final part of this tutorial, we'll be building the front end of our to-do app using Flutter. It's going to be a blast! 💻</p>
<p>Don't forget, you can always refer back to the GitHub repo for this tutorial at <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart">https://github.com/saileshbro/full_stack_todo_dart</a> if you need a little help along the way.</p>
<p>Until next time, happy coding! 😄</p>
]]></content:encoded></item><item><title><![CDATA[🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 3]]></title><description><![CDATA[In the previous part, we set up models, data sources, repositories, and failures for the full-stack to-do application. In this part, we will:

Make changes to the failure classes

Create new failures

RequestFailure

ValidationFailure

ServerFailure
...]]></description><link>https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-3</link><guid isPermaLink="true">https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-3</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[dart_frog]]></category><category><![CDATA[full stack]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Sun, 01 Jan 2023 12:25:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672566752751/21d62a4d-4814-4e9c-9c37-aab0e9a87357.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous part, we set up models, data sources, repositories, and failures for the full-stack to-do application. In this part, we will:</p>
<ul>
<li><p>Make changes to the failure classes</p>
</li>
<li><p>Create new failures</p>
<ul>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/failures/lib/src/request_failure/request_failure.dart"><code>RequestFailure</code></a></p>
</li>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/failures/lib/src/validation_failure/validation_failure.dart"><code>ValidationFailure</code></a></p>
</li>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/failures/lib/src/server_failure/server_failure.dart"><code>ServerFailure</code></a></p>
</li>
</ul>
</li>
<li><p>Create new Exceptions</p>
<ul>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/exceptions/lib/src/server_exception/server_exception.dart"><code>ServerException</code></a></p>
</li>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/exceptions/lib/src/http_exception/http_exception.dart"><code>HttpException</code></a></p>
</li>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/exceptions/lib/src/http_exception/not_found_exception.dart"><code>NotFoundException</code></a></p>
</li>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/exceptions/lib/src/http_exception/bad_request_exception.dart"><code>BadRequestException</code></a></p>
</li>
</ul>
</li>
<li><p>Add validation to DTOs</p>
</li>
<li><p><a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/models/lib/src/serializers/date_time_converter.dart">Add JSON converters</a></p>
</li>
</ul>
<h1 id="heading-creating-and-updating-packages">Creating and updating packages 📦</h1>
<p>In this section, we will create new packages using <a target="_blank" href="https://pub.dev/packages/very_good_cli">very_good_cli</a> and update existing ones to efficiently manage the packages in our full-stack to-do application.</p>
<h2 id="heading-working-with-failure-classes">Working with Failure classes</h2>
<blockquote>
<p>It's time to shake things up in the failure department! 💥 Let's get to work on updating our failure classes.</p>
</blockquote>
<h2 id="heading-create-buildyaml">Create <code>build.yaml</code></h2>
<p>We will now create a new file called <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/failures/build.yaml"><code>build.yaml</code></a> in the <code>failures</code> directory and add the following code. This will change the behaviour of the <a target="_blank" href="https://pub.dev/packages/json_serializable"><code>json_serializable</code></a> so that it generates JSON keys in <code>snake_case</code>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">targets:</span>
  <span class="hljs-string">$default:</span>
    <span class="hljs-attr">builders:</span>
      <span class="hljs-attr">json_serializable:</span>
        <span class="hljs-attr">options:</span>
          <span class="hljs-attr">any_map:</span> <span class="hljs-literal">false</span>
          <span class="hljs-attr">checked:</span> <span class="hljs-literal">false</span>
          <span class="hljs-attr">create_factory:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">disallow_unrecognized_keys:</span> <span class="hljs-literal">false</span>
          <span class="hljs-attr">explicit_to_json:</span> <span class="hljs-literal">true</span>
          <span class="hljs-attr">field_rename:</span> <span class="hljs-string">snake</span>
          <span class="hljs-attr">generic_argument_factories:</span> <span class="hljs-literal">false</span>
          <span class="hljs-attr">ignore_unannotated:</span> <span class="hljs-literal">false</span>
          <span class="hljs-attr">include_if_null:</span> <span class="hljs-literal">true</span>
</code></pre>
<h3 id="heading-update-failure">Update <code>Failure</code></h3>
<p>Let's add a new getter to the <code>Failure</code> abstract class to get the status code of the failure. This will allow us to map the failure to the appropriate <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status">HTTP status code</a> in the controller.</p>
<pre><code class="lang-diff">abstract class Failure {
  String get message;
<span class="hljs-addition">+ int get statusCode;</span>
}
</code></pre>
<h3 id="heading-update-networkfailure">Update <code>NetworkFailure</code></h3>
<p>We will update our failure package and add more failures to it. To do this, we will begin by renaming the <code>code</code> field in our <code>NetworkFailure</code> class to <code>statusCode</code>. This will make the field more meaningful and easier for our readers to understand.</p>
<pre><code class="lang-diff">class NetworkFailure extends Failure with _$NetworkFailure {
  /// {@macro network_failure}
  const factory NetworkFailure({
    required String message,
<span class="hljs-deletion">-   required int code,</span>
<span class="hljs-addition">+   required int statusCode,</span>
    @Default([]) List&lt;String&gt; errors,
  }) = _NetworkFailure;
</code></pre>
<h2 id="heading-create-new-failures">Create new failures</h2>
<blockquote>
<p>Time to add some more failure friends to our app! 🙌</p>
</blockquote>
<p>We'll be creating <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/failures/lib/src/request_failure/request_failure.dart"><code>RequestFailure</code></a>, <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/failures/lib/src/server_failure/server_failure.dart"><code>ServerFailure</code></a>, and <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/blob/780b4662d4a03c17aaef281b100c8a686d8317bb/failures/lib/src/validation_failure/validation_failure.dart"><code>ValidationFailure</code></a> to help us map out any potential errors that may occur.</p>
<h3 id="heading-requestfailure"><code>RequestFailure</code></h3>
<p>To create a <code>RequestFailure</code> class, we will create a new file in the <code>src</code> directory called <code>request_failure/request_failure.dart</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

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

<span class="hljs-keyword">part</span> <span class="hljs-string">'request_failure.freezed.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RequestFailure</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Failure</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">RequestFailure</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> RequestFailure({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> message,
    <span class="hljs-meta">@Default</span>(HttpStatus.badRequest) <span class="hljs-built_in">int</span> statusCode,
  }) = _RequestFailure;
}
</code></pre>
<p>We will create two new classes in the <code>src</code> directory, <code>ServerFailure</code> and <code>ValidationFailure</code>. The <code>ServerFailure</code> class will be used to represent errors that occur on the server side of our application, and the <code>ValidationFailure</code> class will be used to represent validation errors in our application.</p>
<h3 id="heading-serverfailure"><code>ServerFailure</code></h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

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

<span class="hljs-keyword">part</span> <span class="hljs-string">'server_failure.freezed.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServerFailure</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Failure</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">ServerFailure</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> ServerFailure({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> message,
    <span class="hljs-meta">@Default</span>(HttpStatus.internalServerError) <span class="hljs-built_in">int</span> statusCode,
  }) = _ServerFailure;
}
</code></pre>
<h3 id="heading-validationfailure"><code>ValidationFailure</code></h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

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

<span class="hljs-keyword">part</span> <span class="hljs-string">'validation_failure.freezed.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidationFailure</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Failure</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">ValidationFailure</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> ValidationFailure({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> message,
    <span class="hljs-meta">@Default</span>(HttpStatus.badRequest) <span class="hljs-built_in">int</span> statusCode,
    <span class="hljs-meta">@Default</span>({}) <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt; errors,
  }) = _ValidationFailure;
}
</code></pre>
<blockquote>
<p>Don't forget to export and run <a target="_blank" href="https://pub.dev/packages/build_runner"><code>build_runner</code></a> 😎</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> failures;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/failure.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/network_failure/network_failure.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/request_failure/request_failure.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/server_failure/server_failure.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/validation_failure/validation_failure.dart'</span>;
</code></pre>
<blockquote>
<p>💡 Note: You can run the <code>build_runner</code> command by running <code>flutter pub run build_runner build</code> in the terminal.</p>
</blockquote>
<h2 id="heading-working-with-packages">Working with packages</h2>
<blockquote>
<p>Get ready for some package updating fun! 📦</p>
</blockquote>
<h3 id="heading-update-typedefs-package">Update <code>typedefs</code> package</h3>
<p>We will be creating a function called <code>mapTodoId</code> in the <code>typedefs</code> package. This function will be responsible for converting a string id to a <code>TodoId</code> object. This is necessary because we need a way to convert user input into a format our application can understand and use. If the id is not valid, the <code>mapTodoId</code> function will return a <code>RequestFailure</code> object. This will allow us to handle any errors or invalid input gracefully, ensuring that our application is robust and can handle any potential issues</p>
<pre><code class="lang-dart">Either&lt;Failure, TodoId&gt; mapTodoId(<span class="hljs-built_in">String</span> id) {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">final</span> todoId = <span class="hljs-built_in">int</span>.tryParse(id);
    <span class="hljs-keyword">if</span> (todoId == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">const</span> BadRequestException(message: <span class="hljs-string">'Invalid id'</span>);
    <span class="hljs-keyword">return</span> Right(todoId);
  } <span class="hljs-keyword">on</span> BadRequestException <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-keyword">return</span> Left(
      RequestFailure(
        message: e.message,
        statusCode: e.statusCode,
      ),
    );
  }
}
</code></pre>
<p>The <code>mapTodoId</code> method will return either a <code>TodoId</code> object or a <code>RequestFailure</code> object. Make sure to add the dependencies <a target="_blank" href="https://pub.dev/packages/either_dart"><code>either_dart</code></a> and <code>failures</code> to the <code>pubspec.yaml</code> file of the <code>typedefs</code> package.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">either_dart:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">exceptions:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../exceptions</span>
  <span class="hljs-attr">failures:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../failures</span>
</code></pre>
<h3 id="heading-create-a-new-exceptions-package">Create a new <code>exceptions</code> package</h3>
<p>To handle internal exceptions and map them to the appropriate failures, we will create a new package called <code>exceptions</code> and throw our custom exceptions. For example, if we encounter a <a target="_blank" href="https://pub.dev/documentation/postgres/latest/postgres/PostgreSQLException-class.html"><code>PostgreSQLException</code></a> while inserting a new to-do, we will throw a <code>ServerException</code> and map it to the <code>ServerFailure</code> class. To create the <code>exceptions</code> package, run the following command in the terminal:</p>
<pre><code class="lang-bash">very_good create -t dart_pkg exceptions
</code></pre>
<p>This package will include different types of exceptions such as <code>ServerException</code>, <code>HttpException</code>, and <code>NotFoundException</code>. The <code>ServerException</code> will be thrown in the case of an internal server error, while the <code>HttpException</code> is an abstract class that will be extended by other exceptions like <code>NotFoundException</code> and <code>BadRequestException</code>. We can use these custom exceptions to handle internal errors and map them to the appropriate failure classes, such as <code>ServerFailure</code> or <code>RequestFailure</code>. We will start by creating a new file inside <code>src/server_exception/server_exception.dart</code> and add the following code:</p>
<h4 id="heading-serverexception"><code>ServerException</code></h4>
<p>To create <code>ServerException</code> we will create a new file <code>src/server_exception/server_exception.dart</code> and add the following code:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServerException</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Exception</span> </span>{
  <span class="hljs-keyword">const</span> ServerException(<span class="hljs-keyword">this</span>.message);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> message;
  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">String</span> toString() =&gt; <span class="hljs-string">'ServerException: <span class="hljs-subst">$message</span>'</span>;
}
</code></pre>
<h4 id="heading-httpexpection"><code>HttpExpection</code></h4>
<p>To create <code>HttpException</code> we will create a new file <code>src/http_exception/http_exception.dart</code> and add the following code:</p>
<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">HttpException</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Exception</span> </span>{
  <span class="hljs-keyword">const</span> HttpException(<span class="hljs-keyword">this</span>.message, <span class="hljs-keyword">this</span>.statusCode);
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> message;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> statusCode;

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">String</span> toString() =&gt; <span class="hljs-string">'<span class="hljs-subst">$runtimeType</span>: <span class="hljs-subst">$message</span>'</span>;
}
</code></pre>
<h4 id="heading-notfoundexception"><code>NotFoundException</code></h4>
<p>Next, we will create <code>NotFoundException</code>, which will be thrown when a resource is not found. To do this, create a new file called <code>src/http_exception/not_found_exception.dart</code> and add the following code:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotFoundException</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">HttpException</span> </span>{
  <span class="hljs-keyword">const</span> NotFoundException(<span class="hljs-built_in">String</span> message) : <span class="hljs-keyword">super</span>(message, HttpStatus.notFound);
}
</code></pre>
<h4 id="heading-badrequestexception"><code>BadRequestException</code></h4>
<p>Similarly, we will create a new file inside <code>src/http_exception/bad_request_exception.dart</code> and add the following code:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BadRequestException</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">HttpException</span> </span>{
  <span class="hljs-keyword">const</span> BadRequestException({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> message,
    <span class="hljs-keyword">this</span>.errors = <span class="hljs-keyword">const</span> {},
  }) : <span class="hljs-keyword">super</span>(message, HttpStatus.badRequest);

  <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt; errors;
}
</code></pre>
<p>Make sure to import the <code>HttpException</code>. Once you are done with <code>HttpException</code>, add an export statement in <code>http_exception.dart</code> file.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">export</span> <span class="hljs-string">'./bad_request_exception.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'./not_found_exception.dart'</span>;
</code></pre>
<p>And finally, export the <code>HttpException</code> from <code>exceptions/lib/exceptions.dart</code> file.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> exceptions;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/http_exception/http_exception.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/server_exception/server_exception.dart'</span>;
</code></pre>
<h3 id="heading-update-models-package">Update <code>models</code> package</h3>
<h4 id="heading-lets-handle-the-validation">Let's handle the validation</h4>
<blockquote>
<p>Ready to add some sass to your data validation? Let's get those DTOs in shape! 💪</p>
</blockquote>
<p>To add validation to our <code>CreateTodoDto</code> class, we will create a new static method called validated in <code>models/lib/src/create_todo_dto/create_todo_dto.dart</code>. This method will return either a <code>ValidationFailure</code> object or a <code>CreateTodoDto</code> object. We will use this method to ensure that our to-do creation requests contain all necessary information before they are processed. The validation rules are:</p>
<ul>
<li><p>the <code>title</code> should not be empty</p>
</li>
<li><p>the <code>description</code> should not be empty</p>
</li>
</ul>
<p>Before we can add the validated method to the <code>CreateTodoDto</code> class, we need to make sure that the necessary packages are added to the <code>pubspec.yaml</code> file.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">either_dart:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">exceptions:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../exceptions</span>
  <span class="hljs-attr">failures:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../failures</span>
  <span class="hljs-attr">freezed_annotation:</span> <span class="hljs-string">^2.2.0</span>
  <span class="hljs-attr">json_annotation:</span> <span class="hljs-string">^4.7.0</span>
  <span class="hljs-attr">typedefs:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../typedefs</span>
</code></pre>
<p>Now we will create a <code>validated</code> method inside <code>CreateTodoDto</code></p>
<pre><code class="lang-dart">  <span class="hljs-keyword">static</span> Either&lt;ValidationFailure, CreateTodoDto&gt; validated(
    <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json,
  ) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> errors = &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt;{};
      <span class="hljs-keyword">if</span> (json[<span class="hljs-string">'title'</span>] == <span class="hljs-keyword">null</span>) {
        errors[<span class="hljs-string">'title'</span>] = [<span class="hljs-string">'Title is required'</span>];
      }
      <span class="hljs-keyword">if</span> (json[<span class="hljs-string">'description'</span>] == <span class="hljs-keyword">null</span>) {
        errors[<span class="hljs-string">'description'</span>] = [<span class="hljs-string">'Description is required'</span>];
      }
      <span class="hljs-keyword">if</span> (errors.isEmpty) <span class="hljs-keyword">return</span> Right(CreateTodoDto.fromJson(json));
      <span class="hljs-keyword">throw</span> BadRequestException(
        message: <span class="hljs-string">'Validation failed'</span>,
        errors: errors,
      );
    } <span class="hljs-keyword">on</span> BadRequestException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> Left(
        ValidationFailure(
          message: e.message,
          errors: e.errors,
          statusCode: e.statusCode,
        ),
      );
    }
  }
</code></pre>
<p>Similarly, we will create a new static method called <code>validated</code> to validate the <code>UpdateTodoDto</code>. We will ensure that at least one field is present.</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">static</span> Either&lt;ValidationFailure, UpdateTodoDto&gt; validated(
    <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json,
  ) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> errors = &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt;&gt;{};
      <span class="hljs-keyword">if</span> (json[<span class="hljs-string">'title'</span>] == <span class="hljs-keyword">null</span> || json[<span class="hljs-string">'title'</span>] == <span class="hljs-string">''</span>) {
        errors[<span class="hljs-string">'title'</span>] = [<span class="hljs-string">'At least one field must be provided'</span>];
      }
      <span class="hljs-keyword">if</span> (json[<span class="hljs-string">'description'</span>] == <span class="hljs-keyword">null</span> || json[<span class="hljs-string">'description'</span>] == <span class="hljs-string">''</span>) {
        errors[<span class="hljs-string">'description'</span>] = [<span class="hljs-string">'At least one field must be provided'</span>];
      }
      <span class="hljs-keyword">if</span> (json[<span class="hljs-string">'completed'</span>] == <span class="hljs-keyword">null</span>) {
        errors[<span class="hljs-string">'completed'</span>] = [<span class="hljs-string">'At least one field must be provided'</span>];
      }
      <span class="hljs-keyword">if</span> (errors.length &lt; <span class="hljs-number">3</span>) <span class="hljs-keyword">return</span> Right(UpdateTodoDto.fromJson(json));
      <span class="hljs-keyword">throw</span> BadRequestException(
        message: <span class="hljs-string">'Validation failed'</span>,
        errors: errors,
      );
    } <span class="hljs-keyword">on</span> BadRequestException <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> Left(
        ValidationFailure(
          message: e.message,
          statusCode: e.statusCode,
          errors: e.errors,
        ),
      );
    }
  }
</code></pre>
<h4 id="heading-custom-json-converters">Custom JSON converters</h4>
<p>To serialize and deserialize our <code>DateTime</code> objects, we will create a new file called <code>models/lib/src/serializers/date_time_serializer.dart</code>. In this file, we will add the necessary code to handle the serialization and deserialization of <code>DateTime</code> objects.</p>
<p>These classes will implement the <a target="_blank" href="https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html"><code>JsonConverter</code></a> interface and provide the necessary logic to convert <code>DateTime</code> objects to and from JSON. The <code>DateTimeConverterNullable</code> class will handle cases where the <code>DateTime</code> object may be <code>null</code>, while the <code>DateTimeConverter</code> class will handle non-null <code>DateTime</code> objects. With these classes in place, we will be able to correctly handle the formatting of <code>DateTime</code> objects when retrieving data from the database.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DateTimeConverterNullable</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JsonConverter</span>&lt;<span class="hljs-title">DateTime</span>?, <span class="hljs-title">dynamic</span>&gt; </span>{
  <span class="hljs-keyword">const</span> DateTimeConverterNullable();

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">DateTime?</span> fromJson(<span class="hljs-built_in">dynamic</span> json) {
    <span class="hljs-keyword">if</span> (json == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> DateTimeConverter().fromJson(json);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">String?</span> toJson(<span class="hljs-built_in">DateTime?</span> object) {
    <span class="hljs-keyword">if</span> (object == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> DateTimeConverter().toJson(object);
  }
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DateTimeConverter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">JsonConverter</span>&lt;<span class="hljs-title">DateTime</span>, <span class="hljs-title">dynamic</span>&gt; </span>{
  <span class="hljs-keyword">const</span> DateTimeConverter();

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">DateTime</span> fromJson(<span class="hljs-built_in">dynamic</span> json) {
    <span class="hljs-keyword">if</span> (json <span class="hljs-keyword">is</span> <span class="hljs-built_in">DateTime</span>) <span class="hljs-keyword">return</span> json;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">DateTime</span>.parse(json <span class="hljs-keyword">as</span> <span class="hljs-built_in">String</span>);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">String</span> toJson(<span class="hljs-built_in">DateTime</span> object) {
    <span class="hljs-keyword">return</span> object.toIso8601String();
  }
}
</code></pre>
<p>Now we can use this converter in our <code>Todo</code> model.</p>
<pre><code class="lang-diff">  factory Todo({
    required TodoId id,
    required String title,
    @Default('') String description,
    @Default(false) bool? completed,
<span class="hljs-deletion">-   required DateTime createdAt,</span>
<span class="hljs-addition">+   @DateTimeConverter() required DateTime createdAt,</span>
<span class="hljs-deletion">-   DateTime? updatedAt,</span>
<span class="hljs-addition">+   @DateTimeConverterNullable() DateTime? updatedAt,</span>
  }) = _Todo;
</code></pre>
<p>Once you have finished updating the <code>Todo</code> model to use the converters.</p>
<blockquote>
<p>Don't forget to run <code>build_runner</code> 😎</p>
</blockquote>
<p>Woo hoo! We made it to the end of part 3 🎉</p>
<p>In this part, we gave our failure classes a little update and created some new ones. We also added some shiny new exceptions and made sure our DTOs were properly validated. We're almost there, friends! Just a few more steps until we can fully implement the CRUD operations for our awesome to-do app.</p>
<p>In the final part, we'll finally be able to connect to a Postgres database and complete all our backend routes. It's going to be a coding party 🎉 Are you ready to join the fun? I know I am! 🤩</p>
<p>And if you ever need a reference, just head on over to the GitHub repo for this tutorial at <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart">https://github.com/saileshbro/full_stack_todo_dart</a>.</p>
<p>Let's get ready to code some magic in part 4! 😄</p>
]]></content:encoded></item><item><title><![CDATA[🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 2]]></title><description><![CDATA[In the first part of this article, we set up melos as a tool for managing monorepo projects and installed dart_frog as a web framework for building server-side apps with Dart. We also created a new Dart Frog project, which included adding the dart_fr...]]></description><link>https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-2</link><guid isPermaLink="true">https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-2</guid><category><![CDATA[dart_frog]]></category><category><![CDATA[very_good_cli]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Thu, 22 Dec 2022 04:41:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671606659449/56kszfpkg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the first part of this article, we set up <a target="_blank" href="https://docs.page/invertase/melos">melos</a> as a tool for managing monorepo projects and installed <a target="_blank" href="https://pub.dev/packages/dart_frog">dart_frog</a> as a web framework for building server-side apps with Dart. We also created a new Dart Frog project, which included adding the <code>dart_frog</code> dependency on the project's <code>pubspec.yaml</code> file and creating the necessary files and directories.</p>
<p>We will create models, data sources, repositories, and failure classes as a separate Dart package in part 2.</p>
<p>To do this, we will be using <a target="_blank" href="https://cli.vgv.dev/">very_good_cli</a>.</p>
<h1 id="heading-setting-up-models-repositories-and-data-services">Setting up Models, Repositories, and Data Services 💻</h1>
<h2 id="heading-installing-verygoodclihttpsclivgvdev">Installing <a target="_blank" href="https://cli.vgv.dev/">very_good_cli</a></h2>
<blockquote>
<p><strong>very_good_cli</strong>, a Command-Line Interface that provides helpful commands and templates for generating various types of projects, including Flutter apps, Flame games, Flutter packages, Dart packages, federated plugins, and Dart CLI projects.</p>
</blockquote>
<p><strong>Very Good CLI</strong> makes it easy to create and set up these types of projects with a single command, so let's get started!</p>
<p>To install <strong>Very Good CLI</strong>, open a terminal and enter the following command:</p>
<pre><code class="lang-bash">dart pub global activate very_good_cli
</code></pre>
<p>To confirm that Very Good CLI has been installed correctly, you can enter the following command in a terminal <code>very_good --version</code> 🚀</p>
<h2 id="heading-creating-typedefs">Creating <code>typedefs</code></h2>
<p>We will have our <strong>typedefs</strong>, which will be shared between the backend and frontend of our project. To create a new Dart package for these typedefs, we can use the following command:</p>
<pre><code class="lang-bash">very_good create -t dart_pkg typedefs
</code></pre>
<p>This will generate a new dart package <code>typedefs</code>. Here <code>-t dart_pkg</code> means we are creating a new dart package.</p>
<p>Once the <code>typedefs</code> package has been created and the necessary files and directories have been generated, you can navigate to <code>typedefs/lib/src/typedefs.dart</code> to create a new typedef.</p>
<p>In this file, you can create a new <strong>typedef</strong> by adding the following code:</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Primary key type for a Todo.</span></span>
<span class="hljs-keyword">typedef</span> TodoId = <span class="hljs-built_in">int</span>;
</code></pre>
<p>This will create a new typedef called <code>TodoId</code>, an alias for the type <code>int</code>. This typedef can then be used throughout the backend and frontend of your project to represent an identifier for a to-do item. Once you are done, make sure to export the file from <code>lib/typedefs.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> typedefs;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/typedefs.dart'</span>;
</code></pre>
<p>We will add additional shared <strong>typedefs</strong> to this file as needed. <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/tree/main/typedefs"><strong>GitHub Link</strong></a></p>
<h2 id="heading-creating-models">Creating <code>models</code></h2>
<p>Similarly, we will have a separate package to house shared data models.</p>
<ol>
<li><p><code>CreateTodoDto</code> This will be used to send and receive payload for creating a new todo.</p>
</li>
<li><p><code>UpdateTodoDto</code> This will be used to send and receive payload for updating an existing todo.</p>
</li>
<li><p><code>Todo</code> This is the representation of the <code>Todo</code> entity.</p>
</li>
</ol>
<p>To create a separate package for models you can use the <code>very_good</code> CLI:</p>
<pre><code class="lang-bash">very_good create -t dart_pkg models
</code></pre>
<p>Once the package has been created, we will install <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a> for <strong>JSON serialization</strong> and <strong>value equality</strong>, as this library provides helpful tools for these tasks. We will use <a target="_blank" href="https://pub.dev/packages/json_serializable">json_serializable</a> for JSON serialization. To install <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a> and <a target="_blank" href="https://pub.dev/packages/freezed_annotation">freezed_annotation</a>, open your terminal inside the <code>models</code> package and use the command:</p>
<pre><code class="lang-bash">dart pub add freezed json_serializable build_runner -d
dart pub add freezed_annotation json_annotation
</code></pre>
<p>Inside <code>models/lib</code> we will create files in the given order. We have created a parent folder for each model to house the generated codes so that it will look cleaner.</p>
<pre><code class="lang-bash">.
├── models.dart
└── src
    ├── create_todo_dto
    │   └── create_todo_dto.dart
    ├── todo
    │   └── todo.dart
    └── update_todo_dto
        └── update_todo_dto.dart
</code></pre>
<p>Now, in <code>todo.dart</code> we will use <a target="_blank" href="https://pub.dev/packages/freezed">freezed</a> package to create a data class.</p>
<blockquote>
<p><a target="_blank" href="https://pub.dev/packages/freezed">freezed</a> package is a code generation tool that allows you to create immutable classes with concise, boilerplate-free syntax. It uses the <code>@freezed</code> annotation to generate a number of useful methods and operators for your class, such as <code>==</code>, <code>hashCode</code>, and <code>copyWith</code>.</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

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

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Todo</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">Todo</span> </span>{
  <span class="hljs-keyword">factory</span> Todo({
    <span class="hljs-keyword">required</span> TodoId id,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> title,
    <span class="hljs-meta">@Default</span>(<span class="hljs-string">''</span>) <span class="hljs-built_in">String</span> description,
    <span class="hljs-meta">@Default</span>(<span class="hljs-keyword">false</span>) <span class="hljs-built_in">bool?</span> completed,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">DateTime</span> createdAt,
    <span class="hljs-built_in">DateTime?</span> updatedAt,
  }) = _Todo;

  <span class="hljs-keyword">factory</span> Todo.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt; _$TodoFromJson(json);
}
</code></pre>
<p>Since <code>TodoId</code> is in a separate package, we will have to add it in the <code>models/pubspec.yaml</code> file as a dependency. After adding the <code>pubspec.yaml</code> should look like this.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">models</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">Very</span> <span class="hljs-string">Good</span> <span class="hljs-string">Project</span> <span class="hljs-string">created</span> <span class="hljs-string">by</span> <span class="hljs-string">Very</span> <span class="hljs-string">Good</span> <span class="hljs-string">CLI.</span>
<span class="hljs-attr">version:</span> <span class="hljs-number">0.1</span><span class="hljs-number">.0</span><span class="hljs-string">+1</span>
<span class="hljs-attr">publish_to:</span> <span class="hljs-string">none</span>

<span class="hljs-attr">environment:</span>
  <span class="hljs-attr">sdk:</span> <span class="hljs-string">"&gt;=2.18.0 &lt;3.0.0"</span>

<span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">freezed_annotation:</span> <span class="hljs-string">^2.2.0</span>
  <span class="hljs-attr">json_annotation:</span> <span class="hljs-string">^4.7.0</span>
  <span class="hljs-attr">typedefs:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../typedefs</span>

<span class="hljs-attr">dev_dependencies:</span>
  <span class="hljs-attr">build_runner:</span> <span class="hljs-string">^2.3.3</span>
  <span class="hljs-attr">freezed:</span> <span class="hljs-string">^2.3.2</span>
  <span class="hljs-attr">json_serializable:</span> <span class="hljs-string">^6.5.4</span>
  <span class="hljs-attr">lints:</span> <span class="hljs-string">^2.0.0</span>
  <span class="hljs-attr">mocktail:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">test:</span> <span class="hljs-string">^1.19.2</span>
  <span class="hljs-attr">very_good_analysis:</span> <span class="hljs-string">^3.1.0</span>
</code></pre>
<p>Similarly, we will create <code>create_todo_dto.dart</code> for <code>CreateDotoDto</code> class.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'create_todo_dto.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'create_todo_dto.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreateTodoDto</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">CreateTodoDto</span> </span>{
  <span class="hljs-keyword">factory</span> CreateTodoDto({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> title,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> description,
  }) = _CreateTodoDto;

  <span class="hljs-keyword">factory</span> CreateTodoDto.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$CreateTodoDtoFromJson(json);
}
</code></pre>
<p>Similarly, we will create <code>update_todo_dto.dart</code> for <code>UpdateTodoDto</code> class.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'update_todo_dto.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'update_todo_dto.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UpdateTodoDto</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">UpdateTodoDto</span> </span>{
  <span class="hljs-keyword">factory</span> UpdateTodoDto({
    <span class="hljs-built_in">String?</span> title,
    <span class="hljs-built_in">String?</span> description,
    <span class="hljs-built_in">bool?</span> completed,
  }) = _UpdateTodoDto;

  <span class="hljs-keyword">factory</span> UpdateTodoDto.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$UpdateTodoDtoFromJson(json);
}
</code></pre>
<p>Once this is done, we run <a target="_blank" href="https://pub.dev/packages/build_runner">build_runner</a> and generate the necessary code. To generate the missing code, you can run the following command:</p>
<pre><code class="lang-bash">dart pub run build_runner build --delete-conflicting-outputs
</code></pre>
<p>This will generate <code>*.g.dart</code> and <code>*.freezed.dart</code> files. Once this is done, you will have files inside <code>models/lib</code> in the given order.</p>
<pre><code class="lang-plaintext">.
├── models.dart
└── src
    ├── create_todo_dto
    │   ├── create_todo_dto.dart
    │   ├── create_todo_dto.freezed.dart
    │   └── create_todo_dto.g.dart
    ├── todo
    │   ├── todo.dart
    │   ├── todo.freezed.dart
    │   └── todo.g.dart
    └── update_todo_dto
        ├── update_todo_dto.dart
        ├── update_todo_dto.freezed.dart
        └── update_todo_dto.g.dart
</code></pre>
<p>Once you are done creating the models, be sure to export them from <code>lib/models.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> models;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/create_todo_dto/create_todo_dto.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/todo/todo.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/update_todo_dto/update_todo_dto.dart'</span>;
</code></pre>
<p>You can find the <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/tree/main/models">GitHub link for models here</a>.</p>
<h2 id="heading-creating-failures">Creating <code>failures</code></h2>
<p>In the next step, we will create a separate package to handle failures in our application. This package will contain all the failures that may occur, such as <code>NetworkFailure</code> or <code>ServerFailure</code>. Whenever we need to handle an error, we will encapsulate the value or error in a union type and return it from a repository. For example, when we make an API call to retrieve data, we may receive either the requested data or an error. By encapsulating this in an <code>Either</code> type, we can effectively handle and manage failures in our app.</p>
<blockquote>
<p>This is inspired by <a target="_blank" href="https://resocoder.com/flutter-clean-architecture-tdd/">ReSo Coder's Clean Architecture</a> tutorial.</p>
</blockquote>
<p>To create the failures package:</p>
<pre><code class="lang-bash">very_good create -t dart_pkg failures
</code></pre>
<p>This will create a new package in <code>failures</code> directory. We will use <code>freezed</code> and <code>json_serializable</code> libraries for data serialization here as well.</p>
<pre><code class="lang-bash">dart pub add freezed json_serializable build_runner -d
dart pub add freezed_annotation json_annotation
</code></pre>
<p>To begin, we will create a <code>Failure</code> class inside the <code>lib/src/failure.dart</code> directory. This class will be the base for all failure types and will handle and manage our app's failures.</p>
<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">Failure</span> </span>{
  <span class="hljs-built_in">String</span> <span class="hljs-keyword">get</span> message;
}
</code></pre>
<p>We can then create specific failure classes, such as <code>NetworkFailure</code>, to return to the caller whenever there is a network exception. For instance, if we have a <code>TodosRemoteDataSource</code> that makes a call to a backend and encounters a HTTP exception, the data source can throw a <code>NetworkException</code> which will be caught by a repository <code>TodoRepository</code>. The repository will then encapsulate the error in a union type and return either the requested data or the failure to the caller. This allows us to effectively handle and manage failures in our app.</p>
<p>To create a <code>NetworkFailure</code> class, we will create a new file in the <code>src</code> directory called <code>network_failure/network_failure.dart</code>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:freezed_annotation/freezed_annotation.dart'</span>;

<span class="hljs-keyword">part</span> <span class="hljs-string">'network_failure.freezed.dart'</span>;
<span class="hljs-keyword">part</span> <span class="hljs-string">'network_failure.g.dart'</span>;

<span class="hljs-meta">@freezed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NetworkFailure</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Failure</span> <span class="hljs-title">with</span> <span class="hljs-title">_</span>$<span class="hljs-title">NetworkFailure</span> </span>{
  <span class="hljs-keyword">const</span> <span class="hljs-keyword">factory</span> NetworkFailure({
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">String</span> message,
    <span class="hljs-keyword">required</span> <span class="hljs-built_in">int</span> code,
    <span class="hljs-meta">@Default</span>([]) <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">String</span>&gt; errors,
  }) = _NetworkFailure;

  <span class="hljs-keyword">factory</span> NetworkFailure.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) =&gt;
      _$NetworkFailureFromJson(json);
}
</code></pre>
<p>Once you have finished creating the <code>NetworkFailure</code> class, you can run the <code>build_runner</code> command to generate the necessary files for your project.</p>
<p>Make sure to export the failure class from <code>failures/lib/failures.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> failures;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/failure.dart'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-string">'src/network_failure/network_failure.dart'</span>;
</code></pre>
<p>You can view <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/tree/main/failures">the code from here.</a></p>
<h2 id="heading-creating-datasource">Creating <code>data_source</code></h2>
<p>We will create a new package with <code>very_good_cli</code> for our data source.</p>
<pre><code class="lang-bash">very_good create -t dart_pkg data_source
</code></pre>
<p>Once the package is created, we can create an abstract data source contract.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoDataSource</span> </span>{
  Future&lt;<span class="hljs-built_in">List</span>&lt;Todo&gt;&gt; getAllTodo();

  Future&lt;Todo&gt; getTodoById(TodoId id);

  Future&lt;Todo&gt; createTodo(CreateTodoDto todo);

  Future&lt;Todo&gt; updateTodo({
    <span class="hljs-keyword">required</span> TodoId id,
    <span class="hljs-keyword">required</span> UpdateTodoDto todo,
  });

  Future&lt;<span class="hljs-keyword">void</span>&gt; deleteTodoById(TodoId id);
}
</code></pre>
<p>You can import the missing packages in <code>pubspec.yaml</code>. After importing, it should look like this.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">data_source</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">Very</span> <span class="hljs-string">Good</span> <span class="hljs-string">Project</span> <span class="hljs-string">created</span> <span class="hljs-string">by</span> <span class="hljs-string">Very</span> <span class="hljs-string">Good</span> <span class="hljs-string">CLI.</span>
<span class="hljs-attr">version:</span> <span class="hljs-number">0.1</span><span class="hljs-number">.0</span><span class="hljs-string">+1</span>
<span class="hljs-attr">publish_to:</span> <span class="hljs-string">none</span>

<span class="hljs-attr">environment:</span>
  <span class="hljs-attr">sdk:</span> <span class="hljs-string">"&gt;=2.18.0 &lt;3.0.0"</span>
<span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">models:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../models</span>
  <span class="hljs-attr">typedefs:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../typedefs</span>

<span class="hljs-attr">dev_dependencies:</span>
  <span class="hljs-attr">mocktail:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">test:</span> <span class="hljs-string">^1.19.2</span>
  <span class="hljs-attr">very_good_analysis:</span> <span class="hljs-string">^3.1.0</span>
</code></pre>
<p>The <code>TodoDataSource</code> class will be used by both the frontend and backend of our app to access and manage data related to to-dos. This class will <strong>throw known exceptions</strong> that can be handled in the <code>TodoRepository</code>. To manage these exceptions, we will create a separate <code>exceptions</code> package where we can <strong>register all the exceptions</strong> used in our app. The <code>TodoRepository</code> will then be <strong>responsible for handling these exceptions</strong>, allowing us to effectively manage and handle errors in our application.</p>
<p>Once you are done adding the data source, make sure to export it in <code>data_sources/data_sources.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> data_source;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/todo_data_source.dart'</span>;
</code></pre>
<p>You can view the code <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/tree/main/data_source">for the data_source from GitHub.</a></p>
<h2 id="heading-creating-repository">Creating <code>repository</code></h2>
<p>We will make use of the <code>TodoDataSource</code> class to access and manage data related to to-dos. We will also catch any exceptions thrown by the <code>TodoDataSource</code> and serialize them as failures. <code>TodoRepository</code> will then return the failure or the requested data to the caller.</p>
<p>To create the <code>TodoRepository</code> class, we will first navigate to the root directory of our project and create a new package using the <code>very_good</code> CLI:</p>
<pre><code class="lang-bash">very_good create -t dart_pkg repository
</code></pre>
<p>Once we are done, we will add all the dependencies in <code>pubspec.yaml</code> and run <code>dart pub get</code> to get all the packages.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">either_dart:</span> <span class="hljs-string">^0.3.0</span>
  <span class="hljs-attr">models:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../models</span>
  <span class="hljs-attr">failures:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../failures</span>
  <span class="hljs-attr">typedefs:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../typedefs</span>
  <span class="hljs-attr">data_source:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">../data_source</span>
</code></pre>
<p>To create an abstract <code>TodoRepository</code> class, we will navigate to our project's <code>lib/src</code> directory and create a new file called <code>todo_repository.dart</code>. Inside this file, we will make the <code>TodoRepository</code> class and add the necessary abstract methods that will be used to manage and access data related to to-dos.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:either_dart/either.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:failures/failures.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:models/models.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:typedefs/typedefs.dart'</span>;

<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoRepository</span> </span>{
  Future&lt;Either&lt;Failure, <span class="hljs-built_in">List</span>&lt;Todo&gt;&gt;&gt; getTodos();

  Future&lt;Either&lt;Failure, Todo&gt;&gt; getTodoById(TodoId id);

  Future&lt;Either&lt;Failure, Todo&gt;&gt; createTodo(CreateTodoDto createTodoDto);

  Future&lt;Either&lt;Failure, Todo&gt;&gt; updateTodo({
    <span class="hljs-keyword">required</span> TodoId id,
    <span class="hljs-keyword">required</span> UpdateTodoDto updateTodoDto,
  });

  Future&lt;Either&lt;Failure, <span class="hljs-keyword">void</span>&gt;&gt; deleteTodo(TodoId id);
}
</code></pre>
<p>We are using <a target="_blank" href="https://pub.dev/packages/either_dart">either_dart</a> to encapsulate the failure and data in the same place.</p>
<blockquote>
<p><a target="_blank" href="https://pub.dev/packages/either_dart">either_dart</a> is the error handling and railway-oriented programming library is a Dart library that supports async "map" and async "then" functions for working with asynchronous computations and handling errors with <code>Future&lt;Either&gt;</code> values.</p>
</blockquote>
<p>After you are done, make sure to export it in <code>src/repository.dart</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">library</span> repository;

<span class="hljs-keyword">export</span> <span class="hljs-string">'src/todo_repository.dart'</span>;
</code></pre>
<p>You can find the <a target="_blank" href="https://github.com/saileshbro/full_stack_todo_dart/tree/main/repository">code for this on GitHub</a>.</p>
<p>Damn! that was a lot of code and a lot of writing 😮‍💨</p>
<p>🎉 In Part 3, we're going to get started on the implementation of our repository! 💻 We'll begin by setting up the backend and establishing database connections. 🔌 It's going to be a lot of fun and a great opportunity to get hands-on with the code. So stay tuned! 😎</p>
]]></content:encoded></item><item><title><![CDATA[🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 1]]></title><description><![CDATA[💡 Introduction
This tutorial will create a full-stack to-do application using Dart and Flutter in a monorepo setup.

A monorepo is a version control repository containing multiple projects with common code.

This allows for easier management and col...]]></description><link>https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-1</link><guid isPermaLink="true">https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-1</guid><category><![CDATA[dart-backend]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[full stack]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Sailesh Dahal]]></dc:creator><pubDate>Mon, 19 Dec 2022 15:45:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671464617290/rsiUzoJYs.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">💡 Introduction</h1>
<p>This tutorial will create a full-stack to-do application using Dart and Flutter in a monorepo setup.</p>
<blockquote>
<p>A monorepo is a version control repository containing multiple projects with common code.</p>
</blockquote>
<p>This allows for easier management and collaboration on projects within a single repository. We will share a common code between the frontend and backend of our to-do application, like interfaces, data models e.t.c.</p>
<p>The application's backend will be built using <a target="_blank" href="https://pub.dev/packages/dart_frog">dart_frog</a>, while the front end will be developed using Flutter.</p>
<p>This tutorial teaches you how to:</p>
<ul>
<li><p>🧰 Set up a monorepo</p>
</li>
<li><p>💻 Create a full-stack Dart Flutter application</p>
</li>
<li><p>🔗 Manage and share common code between front-end and back end</p>
</li>
<li><p>📝 Build a to-do application with CRUD functionality</p>
</li>
<li><p>🏁 Complete the tutorial with a functional to-do application</p>
</li>
</ul>
<h1 id="heading-lets-go">Let's Go! 🚀</h1>
<p>Before we can build our full-stack to-do application, we must set up the necessary tools and dependencies. First, we will install <a target="_blank" href="https://pub.dev/packages/melos">melos</a>.</p>
<h2 id="heading-setup-meloshttpsdocspageinvertasemelos">Setup <a target="_blank" href="https://docs.page/invertase/melos">melos</a> 🛠️</h2>
<blockquote>
<p><a target="_blank" href="https://docs.page/invertase/melos">Melos</a> is a library that will be used to set up and manage monorepo projects.</p>
</blockquote>
<p>To install <a target="_blank" href="https://docs.page/invertase/melos">melos</a>, open a terminal and run the following command:</p>
<pre><code class="lang-bash">dart pub global activate melos
</code></pre>
<p>This command will install Melos globally, allowing us to use it in any project.</p>
<p>Create a <code>melos.yaml</code> file and add the below content to set up our full-stack to-do application.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">full_stack_todo_dart</span>
<span class="hljs-attr">packages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">/**/pubspec.yaml</span>
</code></pre>
<p>The <code>packages</code> field defines the packages that are part of the monorepo. In this case, we are using the <code>/**/pubspec.yaml</code> pattern, which tells Melos to search for all <code>pubspec.yaml</code> files in the project and consider them as packages.</p>
<h2 id="heading-hopping-into-backend-with-dartfroghttpspubdevpackagesdartfrog">Hopping into backend with <a target="_blank" href="https://pub.dev/packages/dart_frog">dart_frog</a> 🐸</h2>
<p>To handle the server-side logic of our full-stack to-do application, we will use a library called <a target="_blank" href="https://pub.dev/packages/dart_frog">dart_frog</a>.</p>
<blockquote>
<p><a target="_blank" href="https://pub.dev/packages/dart_frog">dart_frog</a> is a lightweight web framework for Dart that makes it easy to build server-side applications.</p>
</blockquote>
<p>To install <a target="_blank" href="https://pub.dev/packages/dart_frog">dart_frog</a>, open a terminal and run the following command:</p>
<pre><code class="lang-bash">dart pub global activate dart_frog_cli
</code></pre>
<p>To create a new dart_frog project, open a terminal and navigate to the directory where you want to create the project. Then, run the following command:</p>
<pre><code class="lang-bash">dart_frog create backend
</code></pre>
<p>The command "dart_frog" creates a new project with necessary files and directories and adds the dependency "dart_frog" to the project's "pubspec.yaml" file.</p>
<p>Once you have created a new dart_frog project and set up the backend of your full-stack to-do application, you can add a Melos script to run the server. To do this, open the <code>melos.yaml</code> file in the root directory of your project and add the following content:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">scripts:</span>
  <span class="hljs-attr">backend:dev:</span>
    <span class="hljs-attr">run:</span> <span class="hljs-string">melos</span> <span class="hljs-string">exec</span> <span class="hljs-string">-c</span> <span class="hljs-number">1</span> <span class="hljs-string">--fail-fast</span> <span class="hljs-string">--</span> <span class="hljs-string">"dart_frog dev"</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">Starts</span> <span class="hljs-string">the</span> <span class="hljs-string">dev</span> <span class="hljs-string">server</span> <span class="hljs-string">for</span> <span class="hljs-string">the</span> <span class="hljs-string">backend</span>
    <span class="hljs-attr">select-package:</span>
      <span class="hljs-attr">flutter:</span> <span class="hljs-literal">false</span>
</code></pre>
<p>Once these steps have been completed, you can run the <code>backend:dev</code> script by opening a terminal and navigating to the root directory of your project. Then, run the following command <code>melos run backend:dev</code></p>
<p>This will start the dev server for the backend of your to-do application. You can then start building the server-side logic of your application using dart_frog.</p>
<pre><code class="lang-plaintext">✓ Running on http://localhost:8080 (0.1s)
</code></pre>
<p>If you see an output similar to the one above after running the <code>melos run backend:dev</code> command means that the dev server for the backend of your full-stack to-do application has been successfully started.</p>
<p>If you open <code>http://localhost:8080</code>, you should be greeted with <code>Welcome to Dart Frog!</code></p>
<blockquote>
<p>Stay tuned for part-2 ...</p>
</blockquote>
]]></content:encoded></item></channel></rss>