<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Md. Mobin</title>
    <description>The latest articles on DEV Community by Md. Mobin (@djsmk123).</description>
    <link>https://dev.to/djsmk123</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F869436%2Fbdef6092-f77a-4639-9fe9-877e6d1adf26.jpeg</url>
      <title>DEV Community: Md. Mobin</title>
      <link>https://dev.to/djsmk123</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/djsmk123"/>
    <language>en</language>
    <item>
      <title>Native iOS App Updater in Flutter: How Zomato, Swiggy, and Others Do It</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Sun, 29 Sep 2024 10:53:47 +0000</pubDate>
      <link>https://dev.to/djsmk123/native-ios-app-updater-in-flutter-how-zomato-swiggy-and-others-do-it-g5p</link>
      <guid>https://dev.to/djsmk123/native-ios-app-updater-in-flutter-how-zomato-swiggy-and-others-do-it-g5p</guid>
      <description>&lt;p&gt;After a long break, we're back—because let's face it, as developers, the work never really stops. Today, we're talking about something we all deal with but don't always talk about: app updates. You know, those little notifications that users often ignore, but we desperately want them to notice.&lt;/p&gt;

&lt;p&gt;You've been there—you build an awesome app, push new features, fix bugs... and then just hope users update. But what if they didn’t have to go to the App Store or Play Store to do it manually?&lt;/p&gt;

&lt;p&gt;Some of the biggest apps, like Zomato and Swiggy, manage to keep things updated without users having to do anything. It’s not magic, though—it’s all about native app updaters, and if you’re using Flutter for iOS, it’s easier than you might think.&lt;/p&gt;

&lt;p&gt;Let’s dive into how it works and how you can do it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;App Updates on Android&lt;/li&gt;
&lt;li&gt;Challenges with App Updates on iOS&lt;/li&gt;
&lt;li&gt;Creating a Native App Updater for iOS&lt;/li&gt;
&lt;li&gt;Demo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Updating apps is a bit like flossing—something everyone knows should be done, but most users put off until it’s absolutely necessary. For developers, this can be frustrating. New features are rolled out, bugs are fixed, and the app runs smoother than ever, yet a large chunk of users are still on outdated versions.&lt;/p&gt;

&lt;p&gt;In the world of Flutter development, Android makes automated updates a breeze. But iOS? That’s a different story. iOS tends to make things a bit more complicated, requiring extra effort to ensure users get the latest version of the app.&lt;/p&gt;

&lt;p&gt;Fortunately, apps like Zomato, Swiggy and other apps have cracked the code, delivering seamless updates without users having to manually visit the App Store. And the good news? Native iOS app updaters in Flutter make it possible to do the same. Ready to see how? Let’s get started!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  App Updates on Android
&lt;/h2&gt;

&lt;p&gt;Before diving into the coding and technical aspects, let’s discuss how to update a Flutter app—or even force an update—without making users leave the app.&lt;/p&gt;

&lt;p&gt;Fortunately, Google provides the &lt;a href="https://developer.android.com/guide/playcore/in-app-updates" rel="noopener noreferrer"&gt;In-App Updates&lt;/a&gt; toolkit. This handy tool opens a native interface for downloading app updates right within the app itself. It handles everything from force updates to optional updates, making the process seamless for users. Plus, it takes care of managing the state after an update, ensuring a smooth transition to the latest version without any hiccups.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  How to Integrate In-App Updates into Flutter
&lt;/h3&gt;

&lt;p&gt;To integrate in-app updates in Flutter, you can use the &lt;a href="https://pub.dev/packages/in_app_update" rel="noopener noreferrer"&gt;&lt;code&gt;in_app_update&lt;/code&gt;&lt;/a&gt; package for handling Android updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add the package to your &lt;code&gt;pubspec.yaml&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;in_app_update:&amp;lt;latest_version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Or run the following command:&lt;/strong&gt;&lt;br&gt;
  &lt;code&gt;flutter pub add in_app_update&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check for updates:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;checkForUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;InAppUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkForUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_updateInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle error&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download the Updated App:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;downloadUpdatedApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_updateInfo&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;updateAvailability&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;UpdateAvailability&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateAvailable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;InAppUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;performImmediateUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;showSnack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AppUpdateResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inAppUpdateFailed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For more details, check out the &lt;a href="https://pub.dev/packages/in_app_update/example" rel="noopener noreferrer"&gt;example&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Challenges with App Updates on iOS
&lt;/h2&gt;

&lt;p&gt;As we know, iOS has strict policies and guidelines regarding user experience, making it challenging to implement force updates for apps. While Google provides the &lt;code&gt;in_app_updates&lt;/code&gt; toolkit to handle updates seamlessly, iOS doesn’t offer a similar solution.&lt;/p&gt;

&lt;p&gt;If you search online for ways to force an iOS application update, you’ll likely find methods that involve displaying a dialog box redirecting users to the App Store. While this approach can work, it’s not always effective. Once users leave the application, there’s a significant chance they won’t return to update and reopen the app.&lt;/p&gt;

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

&lt;p&gt;To determine whether an update is available, there are a couple of options:&lt;/p&gt;
&lt;h3&gt;
  
  
  Option 1: Using the iTunes Lookup API
&lt;/h3&gt;

&lt;p&gt;The iTunes Lookup API can be a useful tool for retrieving the latest version from the App Store. However, it comes with several limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Consistency&lt;/strong&gt;: The information returned may not always be up-to-date, leading to discrepancies between the API and the App Store.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited Metadata&lt;/strong&gt;: The API provides only essential app details, lacking comprehensive information such as detailed changelogs or version history.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Country-Specific Restrictions&lt;/strong&gt;: Responses can vary by country, which may lead to inconsistencies in the app's availability or metadata.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Empty Responses&lt;/strong&gt;: For some app IDs, the API may return empty data arrays (&lt;code&gt;[]&lt;/code&gt;), indicating that no information could be found, which can be frustrating for users trying to retrieve app details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;: The API can return ambiguous error messages, making it challenging to troubleshoot issues effectively.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Option 2: Best Solution
&lt;/h3&gt;

&lt;p&gt;A more effective solution is to use server-side configuration, either via an API or remote configuration, to compare the app's local version with the latest version available. This approach provides greater control over the update process compared to relying on the iTunes API.&lt;/p&gt;

&lt;p&gt;Other options, such as web scraping from the App Store URL, exist but are not efficient for this purpose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdncsidj9cgco8ez8dly.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdncsidj9cgco8ez8dly.jpg" alt="meme2 "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating a Native App Updater for iOS
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Using Platform Channels
&lt;/h3&gt;

&lt;p&gt;For creating native app updates, Flutter uses platform channels. If you are not familiar with &lt;strong&gt;platform channels&lt;/strong&gt;, think of them as a bridge between natively written code (in &lt;strong&gt;Swift&lt;/strong&gt;, &lt;strong&gt;Java&lt;/strong&gt;, &lt;strong&gt;Kotlin&lt;/strong&gt;, or &lt;strong&gt;JavaScript&lt;/strong&gt;) and the respective platforms (&lt;strong&gt;iOS&lt;/strong&gt;, &lt;strong&gt;Android&lt;/strong&gt;, &lt;strong&gt;Web&lt;/strong&gt;, &lt;strong&gt;Windows&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;For more information about &lt;a href="https://docs.flutter.dev/platform-integration/platform-channels" rel="noopener noreferrer"&gt;&lt;code&gt;platform channels&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  StoreKit
&lt;/h3&gt;

&lt;p&gt;To ensure a smooth in-app update experience for iOS, the &lt;code&gt;StoreKit&lt;/code&gt; framework provided by Apple will be used. For a quick explanation, let's have a little chat with &lt;strong&gt;ChatGPT&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ChatGPT (Not Pro Version):&lt;/strong&gt; Hello, I am ChatGPT. How can I help you?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; Explain StoreKit iOS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT (Not Pro Version):&lt;/strong&gt; Giving your personal data to Sam Altman, thanks. Fetching...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT (Not Pro Version):&lt;/strong&gt; StoreKit is a framework provided by Apple that allows you to integrate in-app purchases (IAPs) and subscriptions into your iOS, macOS, watchOS, and tvOS applications. It provides the tools needed to manage products, handle transactions, and maintain user receipts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thinking this is enough to implement this, but if you want to explore more in detail, check out &lt;a href="https://developer.apple.com/documentation/storekit" rel="noopener noreferrer"&gt;&lt;code&gt;StoreKit&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a Flutter Project&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add the following dependencies for getting the app's local version. You can either add this to &lt;code&gt;pubspec.yaml&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;package_info_plus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;latestVersion&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   flutter pub add package_info_plus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Now open your app in Xcode:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd &lt;/span&gt;ios &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; open Runner.xcodeproj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open the &lt;code&gt;AppDelegate.swift&lt;/code&gt; file and add the following content to create a platform channel for using &lt;code&gt;StoreKit&lt;/code&gt;.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;In Flutter, check if an update is available:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;If an update is available, call the platform-specific method:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;That's it! Run your application.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;For demonstration purposes, I am using the &lt;strong&gt;Bitwarden&lt;/strong&gt; app ID because Apple won’t allow me to publish my to-do app on the** App Store**.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/_NjiiANnj0Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Djsmk123/flutter_ios_in_app_updator" rel="noopener noreferrer"&gt;&lt;code&gt;flutter_ios_in_app_updator&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Follow me:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com//djsmk123" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/Smkwinner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>swift</category>
      <category>zomato</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Unlocking the Future: Passwordless Authentication(Passkey) With Flutter and Node.js</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Sun, 16 Jun 2024 14:45:31 +0000</pubDate>
      <link>https://dev.to/djsmk123/unlocking-the-future-passwordless-authenticationpasskey-with-flutter-and-nodejs-1ojh</link>
      <guid>https://dev.to/djsmk123/unlocking-the-future-passwordless-authenticationpasskey-with-flutter-and-nodejs-1ojh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Welcome Back, Fellow Coders!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hey everyone! It’s been a hot minute since our last deep dive, but I’m thrilled to be back and diving headfirst into some seriously cool tech. Today, we’re tackling a topic that’s bound to make your life (and your users’ lives) a whole lot easier: passwordless authentication. Yes, you heard that right – we’re talking about a future where you’ll never have to remember another password again.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore how to implement passwordless authentication in your Flutter app with the help of Node.js on the server side. It’s a powerful combo that promises to enhance security and improve user experience. So, grab your favorite coding beverage, get comfy, and let’s embark on this exciting journey together. Welcome back, and let’s get coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Setup Your Own WebAuthn Server&lt;/li&gt;
&lt;li&gt;Integrate into Flutter&lt;/li&gt;
&lt;li&gt;Outputs&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Passwordless Authentication: A New Era of Security
&lt;/h3&gt;

&lt;p&gt;Passwordless authentication is an innovative approach to securing user accounts and sensitive data without relying on traditional passwords. Instead of requiring users to create and remember complex passwords, passwordless authentication leverages advanced technologies like biometrics, security tokens, and public key cryptography. This method not only enhances security but also improves user experience by simplifying the login process.&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Benefits of Passwordless Authentication
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Traditional passwords are prone to being weak, reused across multiple sites, and vulnerable to phishing attacks and data breaches. Passwordless methods, such as biometrics or hardware tokens, are significantly harder for attackers to compromise.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved User Experience&lt;/strong&gt;: Users no longer need to remember complex passwords or go through cumbersome password recovery processes. This leads to a smoother and more pleasant authentication experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  What is passkey ?
&lt;/h3&gt;

&lt;p&gt;A passkey is a form of authentication credential used in passwordless authentication systems, typically based on the WebAuthn standard. It serves as a secure, user-friendly alternative to traditional passwords. Here are some key points about passkeys:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Digital Credential&lt;/strong&gt;: A passkey is a digital credential stored securely on a user's device.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public Key Cryptography&lt;/strong&gt;: It utilizes public key cryptography to authenticate users. During registration, a key pair is generated: a private key stored securely on the user's device and a public key shared with the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication Process&lt;/strong&gt;: To authenticate, the user proves possession of the private key associated with their passkey. This can be done through biometric verification (e.g., fingerprint scan, facial recognition) or by unlocking the device.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Security&lt;/strong&gt;: Passkeys are more secure than traditional passwords because they are not susceptible to phishing attacks or credential stuffing. They also eliminate the risk of password reuse and theft.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Convenience&lt;/strong&gt;: Passkeys improve user experience by simplifying the authentication process. Users do not need to remember complex passwords or go through lengthy password reset procedures.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2xdV-xut7EQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flutter&lt;/strong&gt; : Basic knowledge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node-js&lt;/strong&gt; : Node js. is used server, you can use any backend language for framework. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt;: we are using &lt;strong&gt;MongoDB&lt;/strong&gt; for database,you can use whichever you find best.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Active Brain&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup Your Own WebAuthn Server
&lt;/h2&gt;

&lt;p&gt;Before starting setup &lt;strong&gt;webauthn&lt;/strong&gt; server, lets talk about what is &lt;strong&gt;webAuthn&lt;/strong&gt; and &lt;strong&gt;passkey&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passkeys&lt;/strong&gt; and &lt;strong&gt;WebAuthn&lt;/strong&gt; complement each other rather than compete. &lt;strong&gt;WebAuthn&lt;/strong&gt; serves as the framework that facilitates the functionality of passkeys.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key difference
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Passkeys act as digital credentials used for authentication, while WebAuthn is a set of rules enabling communication between browsers and websites for passkey-based authentication. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once this communication setup is complete, WebAuthn instructs the browser on how to communicate with the relying party (the website where passkey authentication is used) for the authentication process.&lt;/p&gt;

&lt;p&gt;Read more about pass key and WebAuthn &lt;a href="https://teampassword.com/blog/passkey-vs-webauthn" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How WebAuthn Works
&lt;/h3&gt;

&lt;p&gt;WebAuthn operates through several essential phases to establish secure and passwordless authentication:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Registration Initiation&lt;/strong&gt;: When a user initiates registration, the server generates an encoded challenge and sends it, along with other necessary data, to the client.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczqp7jczw2b16kij43m6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczqp7jczw2b16kij43m6.jpg" alt="passkey-registration"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Approval&lt;/strong&gt;: During this phase, the user selects and registers a credential. This can be a passkey, biometric (like fingerprint or face-lock), USB key, Bluetooth device, or device security code. The user's approval is crucial to proceed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verification Process&lt;/strong&gt;: Once the user approves the registration, the data returned by the approved credential is sent back to the server. This data includes the public key, the type of device used, and a unique device identifier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Completion of Registration&lt;/strong&gt;: After verification, the user's credentials are securely stored in the database. Key pieces of information such as the &lt;code&gt;userId&lt;/code&gt; and &lt;code&gt;publicCredentialId&lt;/code&gt; are recorded, enabling future authentication without the need for traditional passwords.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process ensures a robust and user-friendly authentication method, leveraging modern security practices to safeguard user accounts effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Relay Servers (rpId)
&lt;/h3&gt;

&lt;p&gt;In the realm of WebAuthn (Web Authentication), the Relay Server, known as rpId, serves as a pivotal intermediary between the client device and the relying party (e.g., a website or service). Here’s a concise breakdown using two focal points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication Endpoint&lt;/strong&gt;: The Relay Server functions as the designated endpoint for authentication requests originating from the client device. It facilitates a secure channel for communication between the client and the relying party.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secure Challenge Management&lt;/strong&gt;: Upon receiving an authentication request, the Relay Server generates and manages a cryptographic challenge. This challenge ensures the integrity of the authentication process, guarding against replay attacks and unauthorized access attempts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Login using &lt;strong&gt;WebAuthn&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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




&lt;p&gt;&lt;strong&gt;Enough with theory, let's implement passkey.&lt;/strong&gt;*&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Server with node JS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add support for Digital Asset Links
&lt;/h3&gt;

&lt;p&gt;To enable passkey support for your flutter Android app, associate your app with a website that your app owns. You can declare this association by completing the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Digital Asset Links JSON file. For example, to declare that the website &lt;code&gt;https://signin.example.com&lt;/code&gt; and an Android app with the package name com.example can share sign-in credentials, create a file named &lt;code&gt;assetlinks.json&lt;/code&gt; with the following content:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "relation" : [
      "delegate_permission/common.handle_all_urls",
      "delegate_permission/common.get_login_creds"
    ],
    "target" : {
      "namespace" : "android_app",
      "package_name" : "com.example.android", //packageId
      "sha256_cert_fingerprints" : [
        SHA_HEX_VALUE
      ]
    }
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Host the Digital Assets Link JSON file at the following location on the sign-in domain:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://domain[:optional_port]/.well-known/assetlinks.json
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;on Sending &lt;code&gt;GET&lt;/code&gt; request following data should be return &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; GET /.well-known/assetlinks.json HTTP/1.1
&amp;gt; User-Agent: curl/7.35.0
&amp;gt; Host: signin.example.com

&amp;lt; HTTP/1.1 200 OK
&amp;lt; Content-Type: application/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;You can validate digital assets link json from &lt;a href="https://developers.google.com/digital-asset-links/tools/generator" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;server.js&lt;/code&gt; for hosting digital assetslink&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from 'express';
const app = express();

app.use('/.well-known', express.static('public'));

app.listen(3000, () =&amp;gt; {
  console.log('Server started on port 3000');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Setup Node server
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Installing following packages&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @simplewebauthn/server base64url dotenv express jsonwebtoken mongodb uuid

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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Connect to MongoDB in &lt;code&gt;src/utils/db.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Util for managing user in &lt;code&gt;src/utils/userManager.ts&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;We need to store every &lt;code&gt;challenge&lt;/code&gt; string generated by the backend during registration or login. This stored data will allow us to verify whether a given &lt;code&gt;challenge&lt;/code&gt; is valid or not. Additional data such as &lt;code&gt;expiryTime&lt;/code&gt; can be included with each challenge, but for simplicity, we are only storing strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;src/utils/challengerManagers.ts&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;When registration is completed, we receive the following in the response: &lt;code&gt;credentialID&lt;/code&gt;, &lt;code&gt;credentialPublicKey&lt;/code&gt;, &lt;code&gt;rpID&lt;/code&gt;, &lt;code&gt;origin&lt;/code&gt;, and other parameters. We need to store these values alongside the &lt;code&gt;userId&lt;/code&gt; for the purpose of verifying sign-ins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;src/utils/passkey.ts&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create basic http server using node&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express, { Request, Response } from 'express';
import { connectToMongoDB } from './utils/db';

// Constants
const rpID = "&amp;lt;domain&amp;gt;.com"; // Replace with your actual rpID
const androidHashKey="&amp;lt;hashKey"&amp;gt;; //android release signing app hashapp 

const origin = [
    rpID,
androidHashKey

];

// Initialize Express app
const app = express();
const port = process.env.PORT || 8080;
app.use(express.json());

// Connect to MongoDB
connectToMongoDB();

// GET endpoint for /
app.get('/', (req: Request, res: Response) =&amp;gt; {
    res.send('Hello World!');
});

// Start server
const localIp = process.env.LOCAL_IP || 'localhost';
app.listen(port, () =&amp;gt; {
    console.log(`Express is listening at http://${localIp}:${port}`);
});

export default app;

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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Generate Android hash key either&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;keytool -printcert -jarfile app.apk

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

&lt;/div&gt;
&lt;p&gt;------------------  OR ---------------------------------------&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;keytool -printcert -file CERT.RSA 

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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Create middleware for verify jwt token:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;src/api/middleware.ts&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now we will create following endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/register/start&lt;/code&gt;:&lt;/strong&gt; Initiates the registration process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/register/complete&lt;/code&gt;:&lt;/strong&gt; Verifies passkey data, stores the user in the database, deletes the challenge once sign-in or user creation is completed, and sends requested data with a JWT token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/login/start&lt;/code&gt;:&lt;/strong&gt; Returns the payload needed to start the login flow on the client side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/login/complete&lt;/code&gt;:&lt;/strong&gt; Verifies data returned by the client with passkey data from MongoDB and returns requested data with a JWT token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/me&lt;/code&gt;:&lt;/strong&gt; An auth-protected route that requires a JWT token to return the user object.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;app.ts&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We are done with server side integration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Integrate into Flutter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add Following packages into flutter app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Credential Manager&lt;/strong&gt; : &lt;a href="https://pub.dev/packages/credential_manager" rel="noopener noreferrer"&gt;Credential Manager&lt;/a&gt; is a Jetpack API that supports multiple sign-in methods, such as username and password, passkeys, and federated sign-in solutions (such as Sign-in with Google) in a single API, thus simplifying the integration for developers.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;flutter pub add credential_manager&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I am author of this package and it is currently support &lt;code&gt;Android&lt;/code&gt; platform for flutter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More about Compose Credential Manager:&lt;br&gt;
&lt;a href="https://developer.android.com/identity/sign-in/credential-manager" rel="noopener noreferrer"&gt;https://developer.android.com/identity/sign-in/credential-manager&lt;/a&gt;&lt;br&gt;
    - &lt;strong&gt;HTTP&lt;/strong&gt;: For implementing server APIs&lt;br&gt;
 &lt;code&gt;flutter pub add http&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I am going talk about screens and UI component we will integrate direct.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Initialise Credential Manager:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;WidgetsFlutterBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ensureInitialized&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credentialManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isSupportedPlatform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;//check if platform is supported&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credentialManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;preferImmediatelyAvailableCredentials:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;userModel&lt;/code&gt; in dart&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create Authentication Service for calling APIs:&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Certainly! Here's the markdown file with the corrected content:&lt;/p&gt;

&lt;h2&gt;
  
  
  Registration
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Get required payload for &lt;code&gt;/register/start&lt;/code&gt; endpoint:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;passKeyRegisterInit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;username:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Send the payload to Credential Manager to start the user approval flow for registration:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;credResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credentialManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;savePasskeyCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;After user approval, send the data to &lt;code&gt;/register/complete&lt;/code&gt; API for verification and user creation:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;passKeyRegisterFinish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;username:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;challenge:&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;credResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Sign-In
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Make a GET request to &lt;code&gt;/login/start&lt;/code&gt; endpoint to retrieve &lt;code&gt;challenge&lt;/code&gt; and other payload:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;passKeyLoginInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Initiate the user approval flow for sign-in by sending the request data from &lt;code&gt;/login/start&lt;/code&gt; to &lt;code&gt;CredentialManager&lt;/code&gt;:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;credResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;credentialManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPasswordCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;passKeyOption:&lt;/span&gt; &lt;span class="n"&gt;CredentialLoginOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;challenge:&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;rpId:&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rpId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;userVerification:&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userVerification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Verify the user by sending the data returned from &lt;code&gt;getPasswordCredentials&lt;/code&gt; method to &lt;code&gt;/login/complete/&lt;/code&gt; endpoint to retrieve the user object:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;passKeyLoginFinish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;br&gt;
      &lt;span class="nl"&gt;challenge:&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
      &lt;span class="nl"&gt;request:&lt;/span&gt; &lt;span class="n"&gt;credResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publicKeyCredential&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Outputs&lt;br&gt;
&lt;/h2&gt;

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

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

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

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

&lt;p&gt;In conclusion, exploring passkey authentication with WebAuthn integration for a seamless login experience in Flutter has been insightful. This technology offers a secure and user-friendly alternative to traditional password-based authentication methods. By leveraging cryptographic authentication mechanisms and user verification processes, passkey authentication enhances security while simplifying user interactions.&lt;/p&gt;

&lt;p&gt;Throughout this article, we've delved into the foundational concepts of passkey authentication, including its integration with WebAuthn for server-side operations using Node.js. We've explored essential endpoints for user registration and login, demonstrating how to initiate and complete these processes securely.&lt;/p&gt;

&lt;p&gt;Moreover, the content of this article has been refined using AI-powered tools to ensure clarity and grammatical accuracy. This approach underscores the importance of leveraging technology to enhance content quality and readability.&lt;/p&gt;

&lt;p&gt;For those interested in implementing passkey authentication in their projects, the provided code snippets and explanations serve as a practical guide. Each step, from initiating registration to verifying credentials during login, has been detailed to facilitate smooth integration into Flutter applications.&lt;/p&gt;

&lt;p&gt;More info about credential manager read following blog:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/androiddevelopers/bringing-seamless-authentication-to-your-apps-using-credential-manager-api-b3f0d09e0093" rel="noopener noreferrer"&gt;Bringing seamless authentication to your apps with passkeys using Credential Manager API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Source code&lt;/strong&gt; :
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Djsmk123/passkey_example_flutter" rel="noopener noreferrer"&gt;Flutter App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Djsmk123/pass_key_backend" rel="noopener noreferrer"&gt;Backend Server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Djsmk123/flutter_deeplinking_example/tree/main/reactjs_blogs_deeplink_example" rel="noopener noreferrer"&gt;React App which has digital Assets link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/credential_manager" rel="noopener noreferrer"&gt;Credential Manager Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Follow me on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.instagram.com/smkwinner/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.github.com/djsmk123" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@djsmk123" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;By leveraging AI for grammar correction and clarity enhancement, this article aims to provide a polished and informative guide to passkey authentication in Flutter. All credits for the code snippets and technical insights go to the authors and contributors of the referenced resources.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>android</category>
      <category>passworldless</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>WTF is Grpc? Part 3: Real time Chat ft. Flutter and Golang</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Fri, 27 Oct 2023 18:30:00 +0000</pubDate>
      <link>https://dev.to/djsmk123/wtf-is-grpc-part-3-real-time-chat-ft-flutter-and-golang-35ko</link>
      <guid>https://dev.to/djsmk123/wtf-is-grpc-part-3-real-time-chat-ft-flutter-and-golang-35ko</guid>
      <description>&lt;p&gt;Welcome back, dear readers, to the world of "Wtf is gRPC?" – where we turn tech into a comedy show! 🚀&lt;/p&gt;

&lt;p&gt;In Part 3, we're diving into real-time chat with the grace of caffeine-fueled squirrels and the humor of rubber chickens at a stand-up gig!&lt;/p&gt;

&lt;p&gt;Get ready to leave slow chats in the dust and make your app as snappy as a squirrel on roller skates. Grab your code editor, sense of humor, and let's chat it up in style. Part 3 is gonna be chat-tastic! 🍰💬😄&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Table of Contents: Part 3 - Real-Time Chat Extravaganza 🎉
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction to gRPC Bi-directional: The Magic of Two-Way Chats&lt;/li&gt;
&lt;li&gt;Setup Your Server: Where the Real-Time Chat Spells Begin&lt;/li&gt;
&lt;li&gt;Start with Flutter Application: Embark on Your Chatting Adventure&lt;/li&gt;
&lt;li&gt;Outputs: The Hilarious Messages of Success&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction to gRPC Bi-directional: The Magic of Two-Way Chats
&lt;/h2&gt;

&lt;p&gt;As we've journeyed through gRPC, we first delved into unary communication, which is essentially one-way communication from the client to the server. Then, we ventured into the world of server-side streaming.&lt;/p&gt;

&lt;p&gt;Now, let's dive into gRPC Bi-directional communication. This is where things get really interesting! In Bi-directional streaming RPC, both the client and server engage in a lively conversation by sending a continuous stream of messages back and forth.&lt;/p&gt;

&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The client initiates the action by setting up an HTTP stream with some initial header frames.&lt;/li&gt;
&lt;li&gt;Once this connection is established, both the client and the server can send messages simultaneously, without waiting for the other party to finish. It's like a dynamic chat where no one has to take turns.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of gRPC as the superhero chat line. It's like Tony's suit and Bruce's lab assistant, J.A.R.V.I.S., having a direct line to each other.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Socket vs. gRPC Bi-directional: A Cinematic Showdown" 🍿🎥
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Socket&lt;/th&gt;
&lt;th&gt;gRPC Bi-directional&lt;/th&gt;
&lt;th&gt;Top-Rated Movie Comparison&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup Complexity&lt;/td&gt;
&lt;td&gt;Generally low, traditional sockets require less setup&lt;/td&gt;
&lt;td&gt;Requires setting up gRPC services and protocols, which can be more complex&lt;/td&gt;
&lt;td&gt;Like the simplicity of "The Shawshank Redemption"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Communication Type&lt;/td&gt;
&lt;td&gt;Low-level and can be used for any data format&lt;/td&gt;
&lt;td&gt;High-level, designed for structured data transmission&lt;/td&gt;
&lt;td&gt;Similar to the plot intricacies of "The Godfather"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Can be efficient for simple data exchange&lt;/td&gt;
&lt;td&gt;Optimized for high-performance, especially in microservices&lt;/td&gt;
&lt;td&gt;Like the action-packed "Mad Max: Fury Road"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bi-directional&lt;/td&gt;
&lt;td&gt;Possible with added complexity and custom handling&lt;/td&gt;
&lt;td&gt;Inherently supports efficient bi-directional communication&lt;/td&gt;
&lt;td&gt;Much like "Pulp Fiction's" non-linear storytelling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Handling&lt;/td&gt;
&lt;td&gt;Requires custom error handling&lt;/td&gt;
&lt;td&gt;Provides built-in status codes and error handling&lt;/td&gt;
&lt;td&gt;Comparable to the suspense in "The Silence of the Lambs"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-Language&lt;/td&gt;
&lt;td&gt;Supports multiple programming languages&lt;/td&gt;
&lt;td&gt;Multi-language support with protocol buffers&lt;/td&gt;
&lt;td&gt;Like the multilingual charm of "Inglourious Basterds"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration Ease&lt;/td&gt;
&lt;td&gt;Requires more manual integration&lt;/td&gt;
&lt;td&gt;Easier integration into gRPC-compatible systems&lt;/td&gt;
&lt;td&gt;Much like the seamless blend in "The Dark Knight"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;h2&gt;
  
  
  Setup Your Server: Where the Real-Time Chat Spells Begin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Let's continue code from part 2,where we left.&lt;/li&gt;
&lt;/ul&gt;

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

git clone git@github.com:Djsmk123/Wtf-is-grpc.git


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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Switch to part-2 branch(for starter code)
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;git switch part2&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- As we are going to create Chat one to one service for that we required to get all the users and then communicate.

- Create profobuf for getting users(`rpc_users.proto`)

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;GetUsers&lt;/code&gt; rpc service in &lt;code&gt;GRPCServerService&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;// add this line to import&lt;br&gt;
import "rpc_users.proto";&lt;/p&gt;

&lt;p&gt;service GrpcServerService {&lt;br&gt;
// add this line too&lt;br&gt;
    rpc GetUsers(UsersListRequest) returns (ListUserMessage) {};&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Generate equivalent code for server(`Golang`)

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

&lt;/div&gt;


&lt;p&gt;protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \&lt;br&gt;
    --go-grpc_out=pb --go-grpc_opt=paths=source_relative \&lt;br&gt;
    proto/*.proto&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Add function in `auth` package `get_users.go`


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Let's call this function in grpc-server interface(package &lt;code&gt;gapi&lt;/code&gt;) and add this function in &lt;code&gt;auth.go&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;We are done with this. But we required users so that we can send message so don't, I am providing list of users (around 35), just load them into &lt;code&gt;mongodb&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/Djsmk123/30e0b95258206911055c03f0f481c624" rel="noopener noreferrer"&gt;users-data.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;gt; Note: Password for each user is &lt;code&gt;12345678&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up Your Chat Service:
&lt;/h2&gt;

&lt;p&gt;Establishing a chat service is a straightforward procedure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RPC Service Creation&lt;/strong&gt; : Creating an RPC (Remote Procedure Call) service is at the core of this setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bidirectional Message Stream&lt;/strong&gt;: Implement a bidirectional stream of messages.Messages are initiated by the client and sent to the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bidirectional Response Stream&lt;/strong&gt;: Create a bidirectional stream for responses.Responses are generated by the server and sent back to the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Acknowledgment Mechanism&lt;/strong&gt;: Before actual communication begins, an acknowledgment mechanism is necessary to connect both the client and the server.This mechanism helps ensure a successful connection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Handling&lt;/strong&gt;: Notably, the acknowledgment message is designed to be transient and is not stored in the database.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confirmation of Connection&lt;/strong&gt;: The acknowledgment message serves as a confirmation of a successful connection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Setting up &lt;code&gt;protos&lt;/code&gt;&lt;/strong&gt;: Setup proto for Sending message and get messages&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Add these &lt;code&gt;rpc&lt;/code&gt; function into service&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;service GrpcServerService {&lt;br&gt;
//add these lines&lt;br&gt;
    rpc SendMessage(stream SendMessageRequest) returns (stream Message){};&lt;br&gt;
    rpc GetAllMessage(GetAllMessagesRequest) returns (GetAllMessagesResponse){};&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Generate equivalent code

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

&lt;/div&gt;


&lt;p&gt;protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \&lt;br&gt;
    --go-grpc_out=pb --go-grpc_opt=paths=source_relative \&lt;br&gt;
    proto/*.proto&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- We need JWT-based authentication for both receiving and sending messages. While there are two types of interceptors available, namely `unary interceptors` and `stream interceptors`, we currently utilize the `unary interceptor` as our middleware and lets add middleware for stream service too.

![user-auth](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uze08vugtzoi7h5mkqw2.jpg)

- update `middleware.go` in `gapi` package.

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Register middleware in &lt;code&gt;main.go&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;//update this line&lt;br&gt;
grpcServer := grpc.NewServer(&lt;br&gt;
        grpc.UnaryInterceptor(server.UnaryAuthInterceptor),&lt;br&gt;
        grpc.StreamInterceptor(server.StreamAuthInterceptor),&lt;br&gt;
    )&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Now create `Message` Object for `mongodb` collection

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Update MongoCollection &lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;type MongoCollections struct {&lt;br&gt;
    Users *mongo.Collection&lt;br&gt;
    Chats *mongo.Collection&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Before going to into complex part to listen message and store them into db, lets deal with simple rpc function `GetAllMessage` from db and `SendMessage` to store the message.

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Now we are going to use go-routine to listen and send message to the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;gt; We are using &lt;code&gt;goroutines&lt;/code&gt; and channels to listen for changes in the database. If you want to learn more about &lt;code&gt;goroutines&lt;/code&gt;, you can read this &lt;a href="https://betterprogramming.pub/golang-how-to-implement-concurrency-with-goroutines-channels-2b78b8077984" rel="noopener noreferrer"&gt;blog&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let's Define &lt;code&gt;SendMessage&lt;/code&gt; and &lt;code&gt;GetAllMesasge&lt;/code&gt; for grpc server interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&amp;gt; Note: To make connection b/w two users, its required to send first message &lt;code&gt;Join_room&lt;/code&gt; so that other user can get acknowledgment.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing gRPC in CLI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5sodv01i5kry8xruqru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5sodv01i5kry8xruqru.png" alt="grpc-testing"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Start with Flutter Application: Embark on Your Chatting Adventure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add following dependency into your project:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;flutter pub add intl&lt;/p&gt;



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

- Generate equivalent code for dart from `protos`.

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

&lt;/div&gt;


&lt;p&gt;protoc --proto_path=proto --dart_out=grpc:lib/pb proto/*.proto&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- As it is known issue dart does not generate code `google/protobuf/timestamp.pb.dart`,[read here](https://github.com/google/protobuf.dart/issues/483). So download this file [google_timestamp.pb.dart](https://gist.github.com/Djsmk123/83025e60cbf2c5a931ffefdf2b3a5b7e) and update the generated code as required(just change import path)

- Before starting chat service we need to display all the users into home screen so that we can send message to other users

&amp;gt; PS: I am not going to talk about basic UI building for showing the list of users.

- Add function to get all users in `AuthService` class

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

&lt;/div&gt;


&lt;p&gt;class AuthServices{&lt;br&gt;
//keep same, add this function&lt;br&gt;
  static Future&amp;gt; getUsers(&lt;br&gt;
      {int pageNumber = 1, String? search}) async {&lt;br&gt;
    final res = await GrpcService.client.getUsers(&lt;br&gt;
        UsersListRequest(pageSize: 10, pageNumber: pageNumber, name: search),&lt;br&gt;
        options: CallOptions(metadata: {'authorization': 'bearer $authToken'}));&lt;br&gt;
    return res.users.map((e) =&amp;gt; UserModel(e.id, e.username, e.name)).toList();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Call this function in `HomeScreen()`,for reference use [this ui](https://gist.github.com/Djsmk123/3c39818c3a00c0b01ad8eadb36486102) or create your own.

- Let's Create chat service to get messages(History)

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

&lt;/div&gt;


&lt;p&gt;class ChatService {&lt;br&gt;
  static Future&amp;gt; getMessages(String username) async {&lt;br&gt;
    final res = await GrpcService.client.getAllMessage(&lt;br&gt;
        GetAllMessagesRequest(&lt;br&gt;
          reciever: username,&lt;br&gt;
        ),&lt;br&gt;
        options: CallOptions(&lt;br&gt;
            metadata: {'authorization': 'bearer ${AuthService.authToken}'}));&lt;br&gt;
    return res.messages;&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Let's listen for messages,send message and fetch history.

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&amp;gt; Note: UI component maybe missing or you create your own. for reference check &lt;a href="https://github.com/Djsmk123/Wtf-is-grpc/tree/part3/" rel="noopener noreferrer"&gt;source code&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let's talk about what we did in above class.

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;initState()&lt;/code&gt; we are starting to listen message by sending first message &lt;code&gt;Join_room&lt;/code&gt; to make connection and another function &lt;code&gt;fetchChatsHistory()&lt;/code&gt; as name suggested, getting earlier messages by calling &lt;code&gt;grpc&lt;/code&gt; unary service.&lt;/li&gt;
&lt;li&gt;on sending message we are adding message data to stream-controller to send stream of message to the server.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Outputs: The Hilarious Messages of Success
&lt;/h2&gt;

&lt;p&gt;&lt;iframe&gt;
  width="710"&lt;br&gt;
  height="399"&lt;br&gt;
  src="https://www.youtube.com/embed/93dg_-RDV_k"&lt;br&gt;
  allowfullscreen&lt;br&gt;
  loading="lazy"&amp;gt;&lt;br&gt;
&lt;/iframe&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  End of the series
&lt;/h2&gt;

&lt;p&gt;Thank you to everyone who has supported me throughout this series. I promise to be more consistent next time. The blog series titled &lt;strong&gt;'Wtf is gRPC?'&lt;/strong&gt; comes to an end with this post. In Part 1, we delved into authentication and authorization using unary gRPC communication. In Part 2, we had some fun creating a custom notification service in Flutter, along with server-side streaming gRPC. And now, in the grand finale, Part 3, we explored bi-directional gRPC to create a simple real-time chat application. Thanks again, and remember, gRPC isn't as mysterious as it sounds – it's just a protocol! Stay tuned for more tech adventures!&lt;/p&gt;
&lt;h2&gt;
  
  
  Thank for 2k+ Followers on &lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;
&lt;/h2&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhq6bz7ceafgvn8ymh9g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhq6bz7ceafgvn8ymh9g.jpeg" alt="flutter meme"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Source code :
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Djsmk123/wtf-is-grpc/tree/part3" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Follow me on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.instagram.com/smkwinner/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.github.com/djsmk123" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@djsmk123" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>go</category>
      <category>grpc</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Wtf is Grpc? Part 2: Custom Notification(without Firebase) in Flutter and Golang.</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Sat, 30 Sep 2023 08:22:57 +0000</pubDate>
      <link>https://dev.to/djsmk123/wtf-is-grpc-part-2-custom-notificationwithout-firebase-in-flutter-and-golang-1ang</link>
      <guid>https://dev.to/djsmk123/wtf-is-grpc-part-2-custom-notificationwithout-firebase-in-flutter-and-golang-1ang</guid>
      <description>&lt;p&gt;Welcome back, intrepid tech explorers! If you survived the roller-coaster of gRPC, Flutter, and Golang from our last adventure, congratulations! You're now ready for Part 2: &lt;strong&gt;"Wtf is gRPC? Part 2: Custom Notification (without Firebase) in Flutter and Golang."&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;p&gt;In this next thrilling installment, we'll tackle custom notifications without the Firebase safety net. Get ready to dive into the world of Flutter and Golang, where we'll craft notifications that are as unique as your favorite coding quirks. So, fasten your seatbelts and keep your code close; it's going to be a wild ride through the land of tech enchantment! 🚀💻✨&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Table of Contents: Navigating the Streaming Circus 🎪
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Streaming Showdown: gRPC Unleashed&lt;/li&gt;
&lt;li&gt;Let's Play with Data: The Streaming Playground&lt;/li&gt;
&lt;li&gt;Setting Up the Golang Server: Where Code Meets Magic&lt;/li&gt;
&lt;li&gt;Fluttering into Action: Your Magical Streaming App&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Streaming Showdown: gRPC Unleashed
&lt;/h2&gt;

&lt;p&gt;In simple words &lt;code&gt;A server-streaming RPC is similar to a unary RPC, except that the server returns a stream of messages in response to a client’s request. After sending all its messages, the server’s status details (status code and optional status message) and optional trailing metadata are sent to the client. This completes processing on the server side. The client completes once it has all the server’s messages.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Unary vs. Server Streaming: The Tech Battle of the Century
&lt;/h2&gt;

&lt;p&gt;In this epic tech showdown, I am going to compare Unary Streaming and Server Streaming, with a touch of TV series for better understanding.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Unary Streaming&lt;/th&gt;
&lt;th&gt;Server Streaming&lt;/th&gt;
&lt;th&gt;TV Series Comparison&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data Transfer&lt;/td&gt;
&lt;td&gt;Single requests&lt;/td&gt;
&lt;td&gt;Continuous data stream&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;The Office&lt;/em&gt; (Episode-by-episode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Communication Overhead&lt;/td&gt;
&lt;td&gt;Less&lt;/td&gt;
&lt;td&gt;Slightly more&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Friends&lt;/em&gt; (Constant chatter)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time Updates&lt;/td&gt;
&lt;td&gt;Not suitable&lt;/td&gt;
&lt;td&gt;Ideal for real-time&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Stranger Things&lt;/em&gt; (Always suspenseful)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Simpler&lt;/td&gt;
&lt;td&gt;More complex&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Black Mirror&lt;/em&gt; (Mind-bending)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Limited by requests&lt;/td&gt;
&lt;td&gt;Highly scalable&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Game of Thrones&lt;/em&gt; (Vast world)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use Cases&lt;/td&gt;
&lt;td&gt;Simple operations&lt;/td&gt;
&lt;td&gt;Real-time data streaming&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Breaking Bad&lt;/em&gt; (Intense)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource Consumption&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;The Mandalorian&lt;/em&gt; (High production)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parallel Processing&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Effective&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;The Big Bang Theory&lt;/em&gt; (Many characters)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Handling&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Handling errors in streams&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;The Twilight Zone&lt;/em&gt; (Mysterious)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Suitable for small-scale&lt;/td&gt;
&lt;td&gt;Suitable for large-scale&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;The Walking Dead&lt;/em&gt; (Epic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Winner&lt;/td&gt;
&lt;td&gt;Suitable for specific tasks&lt;/td&gt;
&lt;td&gt;Ideal for real-time data&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Server Streaming Showdown!&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;h2&gt;
  
  
  Let's Play with Data: The Streaming Playground
&lt;/h2&gt;

&lt;p&gt;Let's play with streaming! Imagine that the client is a stand-up comedian whose job is to tell jokes, and the server, representing the audience, responds with laughter until they've had enough."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let's create required message and service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;joke.proto&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

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

syntax = "proto3";

package pb;
option go_package="github.com/djsmk123/server/pb";
//Response send by standup comedian to audience 
message Joke{
    string joke=1;
}
// Response Recieved by standup comedian from audience
message JokeResponse{
    string laugh_intensity=1;
};


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

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;rpc_services.proto&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

syntax="proto3";
package pb;
option go_package="github.com/djsmk123/server/pb";
import "joke.proto";
service GrpcServerService {
    rpc ThrowAJoke(Joke) returns(stream JokeResponse){};
}


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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Implement &lt;code&gt;ThrowAJoke()&lt;/code&gt; function in golang.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

func (s *Server) ThrowAJoke(joke *pb.Joke, stream pb.GrpcServerService_ThrowAJokeServer) error {
    for i := 1; i &amp;lt;= 10; i++ {
        response := &amp;amp;pb.JokeResponse{
            LaughIntensity: fmt.Sprintf("LOL %d", i),
        }
        if err := stream.Send(response); err != nil {
            return err
        }
        time.Sleep(1 * time.Second)
    }
    return nil
}


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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Let's call service now.&lt;/li&gt;
&lt;/ul&gt;

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

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


&lt;h2&gt;
  
  
  Setting Up the Golang Server: Where Code Meets Magic
&lt;/h2&gt;

&lt;p&gt;We are going to create a notification service that will listen to a notification-gRPC streaming service in the background. If any new user is added to the MongoDB collection named users, we will send data to the listener, which will then display notifications in the application.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Let's continue code from part 1,where we left.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;git clone git@github.com:Djsmk123/Wtf-is-grpc.git&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new message for the notification response that has to be returned by the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;rpc_notification.proto&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

syntax="proto3";
package pb;

option go_package="github.com/djsmk123/server/pb";


message NotificationMessage{
    int32 id=1;
    string title=2;
    string description=3;
}


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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Create new &lt;code&gt;rpc&lt;/code&gt; in &lt;code&gt;rpc_services.proto&lt;/code&gt;.
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;//keep everything same&lt;br&gt;
//import NotificationMessage&lt;br&gt;
import "rpc_notifications.proto";&lt;br&gt;
//same &lt;br&gt;
service GrpcServerService {&lt;br&gt;
//keep same &lt;br&gt;
// create new rpc function here&lt;br&gt;
    rpc GetNotifications(EmptyRequest) returns (stream NotificationMessage){}&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Create equivalent code for golang from proto.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \&lt;br&gt;
    --go-grpc_out=pb --go-grpc_opt=paths=source_relative \&lt;br&gt;
    proto/*.proto&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Create new package called `services` in root folder(`server`).

&amp;gt; We are using **goroutines** and **channels** to listen for changes in the database. If you want to learn more about goroutines, you can read this [blog](https://betterprogramming.pub/golang-how-to-implement-concurrency-with-goroutines-channels-2b78b8077984)

- Create new services `notification.go`.
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Now just have to define &lt;code&gt;rpc GetNotification&lt;/code&gt; in &lt;code&gt;gapi&lt;/code&gt;. so create a new file &lt;code&gt;notification.go&lt;/code&gt; in package &lt;code&gt;gapi&lt;/code&gt;.&lt;/p&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wait, we can't listen change stream in &lt;code&gt;mongodb&lt;/code&gt; without converting &lt;strong&gt;standalone&lt;/strong&gt; to &lt;strong&gt;replica set&lt;/strong&gt; more info &lt;a href="https://www.mongodb.com/docs/manual/changeStreams/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In terminal open path (windows)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;cd C:\Program Files\MongoDB\Server\&amp;amp;lt;mongo-version&amp;amp;gt;\bin&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute this command
&lt;/li&gt;
&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;mongod --port 27017 --dbpath="C:\Program Files\MongoDB\Server&amp;lt;mongo-version&amp;gt;\data" --replSet rs0 --bind_ip localhost&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Re-run mongo service

`rs.initiate()`

- Run server and let's test in `evans-cli`.

&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/PCSkI_i461Y"&gt;
&lt;/iframe&gt;



## Fluttering into Action: Your Magical Streaming App

- Add following dependencies to flutter project

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

&lt;/div&gt;


&lt;p&gt;dependencies:&lt;br&gt;
  flutter_background_service: ^5.0.1&lt;br&gt;
  flutter_local_notifications: ^15.1.1&lt;br&gt;
  flutter_background_service_android: ^6.0.1&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Create equivalent code for dart 

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

&lt;/div&gt;


&lt;p&gt;protoc --proto_path=proto --dart_out=grpc:lib/pb proto/*.proto&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Create new services,called `notification.dart`

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Call notification service function in &lt;code&gt;splash_screen.dart&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;Future initAsync() async {&lt;br&gt;
    try {&lt;br&gt;
      await NotificationServices.initializeService();&lt;br&gt;
      //keep same&lt;br&gt;
    } catch (e) {&lt;br&gt;
      log(e.toString());&lt;br&gt;
      navigateToLogin();&lt;br&gt;
    }&lt;br&gt;
  }&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Let's build application and listen for new user.

![flutter notification without firebase](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osm2tlvnlp35mxhj2f1v.jpg)

Thank you for joining us on this journey so far! We've covered a lot in this series, from setting up the server-side of our application in Golang, handling notifications, to building a foundation for communication.

In the next and final part of this series, we'll explore one of the most exciting features of gRPC: bi-directional streaming. We'll be implementing a real-time chat service using Flutter and Golang. Get ready for a dynamic and interactive experience that showcases the power and flexibility of gRPC.

So, stay tuned and keep coding! We can't wait to see you in the last part of this series where we'll bring it all together and create a fully functional chat application.

If you have any questions or feedback, please feel free to reach out. Happy coding, and see you in the next installment!



## Source code : 
[Github Repo](https://github.com/Djsmk123/wtf-is-grpc/tree/part2)

## Follow me on 

- [Twitter](https://twitter.com/smk_winner)

- [Instagram](https://www.instagram.com/smkwinner/)

- [Github](https://www.github.com/djsmk123)

- [linkedin](https://www.linkedin.com/in/md-mobin-bb928820b/)

- [dev.to](https://dev.to/djsmk123)

- [Medium](https://medium.com/@djsmk123)




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

&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>go</category>
      <category>grpc</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Wtf is Grpc? Part 1: Authentication and Authorization in Flutter and Golang.</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Mon, 04 Sep 2023 13:53:10 +0000</pubDate>
      <link>https://dev.to/djsmk123/wtf-is-grpc-part-1-authentication-and-authorization-in-flutter-and-golang-37jj</link>
      <guid>https://dev.to/djsmk123/wtf-is-grpc-part-1-authentication-and-authorization-in-flutter-and-golang-37jj</guid>
      <description>&lt;p&gt;Greetings, fellow tech enthusiasts, and welcome to a tech journey like no other! 🚀 Today, we're embarking on a roller-coaster ride through the fascinating world of gRPC, Flutter, and Golang, but with a twist – we've spiced it up with a generous serving of humor. Buckle up; it's going to be a wild ride!&lt;/p&gt;

&lt;p&gt;In our three-part series, we'll explore the ins and outs of gRPC, the delightful dance between Flutter and Golang, and discover how these technologies can come together to create harmonious applications that can bring a smile to even the most stoic developer's face.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Introduction&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gRPC: A Not-So-Boring Bookish and Slightly Amusing Definition&lt;/li&gt;
&lt;li&gt;gRPC vs REST: The Iron Throne of APIs&lt;/li&gt;
&lt;li&gt;Types of gRPC&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prerequisites&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create project&lt;/li&gt;
&lt;li&gt;Setup  Database model&lt;/li&gt;
&lt;li&gt;Password Hashing&lt;/li&gt;
&lt;li&gt;Environment&lt;/li&gt;
&lt;li&gt;JWT&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="//#basic-of-protocol-buffer-(.proto)."&gt;Basic of Protocol Buffer&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service&lt;/li&gt;
&lt;li&gt;Message&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setup Basic Project in Golang&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create Protocol Buffers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate Golang code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authentication in Go&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setup Server and Middleware&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start Server&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Flutter Project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup Project&lt;/li&gt;
&lt;li&gt;Output&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Conclusion&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source Code&lt;/li&gt;
&lt;li&gt;Follow Me&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Quick Peek
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1: Authentication &amp;amp; Authorization&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2: Custom Flutter Notifications&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3: Chat Applications with gRPC&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  gRPC: A Not-So-Boring Bookish and Slightly Amusing Definition
&lt;/h2&gt;

&lt;p&gt;Alright, time to don our academic spectacles and approach this with a hint of whimsy. Imagine you're at a fancy library soirée, and someone asks, "What, pray tell, is gRPC?" You respond with a grin:&lt;/p&gt;

&lt;p&gt;"Gentlefolk, gRPC, which stands for 'Google Remote Procedure Call,' is like the telegraph system of the digital realm. It's a meticulously structured, high-tech method for different programs to chit-chat with each other, just like sending telegrams back in the day, but without the Morse code."&lt;/p&gt;

&lt;p&gt;"But wait, there's more! Instead of tapping out dots and dashes, gRPC uses Protocol Buffers, which is like encoding your messages in secret spy code, only it's not so secret. This makes your data compact and zippy, perfect for today's speedy world."&lt;/p&gt;

&lt;p&gt;"Picture it as a virtual switchboard operator connecting your apps with finesse. Plus, it plays nice with HTTP/2, which is like giving your data a sleek, sports car to zoom around in, making it super fast."&lt;/p&gt;

&lt;p&gt;"So, in essence, gRPC is the gentleman's agreement between software, ensuring they can communicate efficiently and reliably, like two well-mannered tea-drinking robots having a chat. It's the technological equivalent of a courteous bow, allowing different systems to exchange information harmoniously."&lt;/p&gt;

&lt;h2&gt;
  
  
  gRPC vs REST: The Iron Throne of APIs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;gRPC&lt;/th&gt;
&lt;th&gt;REST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;Uses HTTP/2 for communication.&lt;/td&gt;
&lt;td&gt;Typically uses HTTP/1.1, but can use HTTP/2.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Serialization&lt;/td&gt;
&lt;td&gt;Uses Protocol Buffers (ProtoBuf) for efficient binary serialization.&lt;/td&gt;
&lt;td&gt;Uses human-readable formats like JSON or XML.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Communication Patterns&lt;/td&gt;
&lt;td&gt;Supports unary, server streaming, client streaming, and bidirectional streaming.&lt;/td&gt;
&lt;td&gt;Primarily supports request-response (HTTP GET, POST, etc.).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payload Size&lt;/td&gt;
&lt;td&gt;Smaller payload due to binary serialization, making it more efficient.&lt;/td&gt;
&lt;td&gt;Larger payload due to text-based serialization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;High performance and low latency, suitable for microservices.&lt;/td&gt;
&lt;td&gt;Slightly slower due to text-based serialization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language Agnostic&lt;/td&gt;
&lt;td&gt;Supports multiple programming languages, making it polyglot.&lt;/td&gt;
&lt;td&gt;Can be used with any language, but not as standardized.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Generation&lt;/td&gt;
&lt;td&gt;Generates client and server code automatically from Protobuf definitions.&lt;/td&gt;
&lt;td&gt;No automatic code generation for client or server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Handling&lt;/td&gt;
&lt;td&gt;Uses status codes and detailed error messages for robust error handling.&lt;/td&gt;
&lt;td&gt;Typically relies on HTTP status codes and custom error messages.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tooling&lt;/td&gt;
&lt;td&gt;Well-documented and supported by various libraries and tools.&lt;/td&gt;
&lt;td&gt;Widely supported with many tools and libraries available.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;h2&gt;
  
  
  Types of gRPC
&lt;/h2&gt;

&lt;p&gt;Here are the primary types of gRPC communication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unary RPC (Request-Response)&lt;/strong&gt;: This is the simplest form of gRPC communication. The client sends a single request to the server and waits for a single response. It's similar to traditional request-response interactions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server Streaming RPC&lt;/strong&gt;: In this pattern, the client sends a single request, but the server responds with a stream of messages. It's useful when the server needs to push multiple pieces of data to the client, such as real-time updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client Streaming RPC&lt;/strong&gt;: Here, the client sends a stream of messages to the server, and the server responds with a single message. This can be beneficial for scenarios like uploading large files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bidirectional Streaming RPC&lt;/strong&gt;: In this type, both the client and server can send a stream of messages to each other concurrently. It's ideal for interactive and real-time applications like chat or gaming.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In this we are going to explore &lt;strong&gt;unary gRPC&lt;/strong&gt; using &lt;strong&gt;login&lt;/strong&gt;,&lt;strong&gt;signup&lt;/strong&gt; and &lt;strong&gt;get-user&lt;/strong&gt; request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic understanding of Golang,Flutter and &lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDb&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Excitement to learn something new.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup Basic Project in Golang
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create new project 
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;go mod init &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Install Mongo package for go

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

&lt;/div&gt;


&lt;p&gt;go get go.mongodb.org/mongo-driver&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Setup Database Model
    - Create package `db` 
      - Create Struct to access database
        &lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  - Create new model `user.go`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Password hashing : we are going to create function for hashing password and check password, &lt;strong&gt;Never store password in simple text&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create package &lt;code&gt;utils&lt;/code&gt; in root.&lt;/li&gt;
&lt;li&gt;Install required package &lt;code&gt;go get golang.org/x/crypto/bcrypt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;HashPasword&lt;/code&gt; and &lt;code&gt;CheckPassword&lt;/code&gt; function in &lt;code&gt;password.go&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Setup Environment: We required few environment variable like &lt;code&gt;server_address&lt;/code&gt;,&lt;code&gt;mongodburl&lt;/code&gt; and &lt;code&gt;jwt_key&lt;/code&gt; etc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install Viper package for loading &lt;code&gt;app.env&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
go get github.com/spf13/viper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;

&lt;p&gt;for more information read about &lt;a href="//github.com/spf13/viper"&gt;viper&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create function in &lt;code&gt;utils&lt;/code&gt; to load &lt;code&gt;app.env&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;app.env&lt;/code&gt; in root folder.&lt;/li&gt;
&lt;/ul&gt;

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

DB_NAME=grpc 
DB_SOURCE=mongodb://localhost:27017
RPC_SERVER_ADDRESS=0.0.0.0:9090
GIN_MODE=debug
TOKEN_SYMMETRIC_KEY=12345678123456781234567812345678
ACCESS_TOKEN_DURATION=600m



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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Setup Jwt: We need authorization in few requests so its going to &lt;code&gt;bearer&lt;/code&gt; token based authentication. 

&lt;ul&gt;
&lt;li&gt;Install required Jwt package&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

     go get github.com/dgrijalva/jwt-go


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

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: Not Going to cover Jwt Basics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Create new package token and create interface to create token and verify token.
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;





&lt;h2&gt;
  
  
  Basic of Protocol Buffer (&lt;code&gt;.proto&lt;/code&gt;).
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Service&lt;/strong&gt;: A unary service in gRPC involves a single request from the client to the server, which then sends a single response back to the client. To define a unary service, you need to create a .proto file that describes the service and the message types it uses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Message&lt;/strong&gt;: A message is a data structure that represents the information being sent between the client and server. It's defined in your .proto file using the message keyword. Here's a simple example:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;syntax = "proto3";&lt;/p&gt;

&lt;p&gt;message MyRequest {&lt;br&gt;
  string name = 1;&lt;br&gt;
  int32 age = 2;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;message MyResponse {&lt;br&gt;
  string greeting = 1;&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In this example, we have two messages, `MyRequest`and `MyResponse`. `MyRequest` has two fields, name and age, while `MyResponse` has a single field, greeting.

- Importing Data Structures from Another Package:

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

&lt;/div&gt;


&lt;p&gt;import "other_package.proto";&lt;/p&gt;

&lt;p&gt;message MyRequest {&lt;br&gt;
  other_package.SomeMessage some_data = 1;&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Create Protocol buffers for our project:

- Services: We will have following services called `login`, `signup` and `get-user`. 

- Create `user.proto`, User object to return to the client.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;syntax = "proto3";&lt;/p&gt;

&lt;p&gt;package pb;&lt;br&gt;
option go_package="github.com/djsmk123/server/pb";&lt;/p&gt;

&lt;p&gt;message User{&lt;br&gt;
    int32 id=1;&lt;br&gt;
    string username=2;&lt;br&gt;
    string name=3;&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Create `rpc_login.proto`, it will have LoginRequestMessage and LoginReponseMessage

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

&lt;/div&gt;


&lt;p&gt;syntax="proto3";&lt;br&gt;
package pb;&lt;br&gt;
import "user.proto";&lt;br&gt;
option go_package="github.com/djsmk123/server/pb";&lt;/p&gt;

&lt;p&gt;message LoginRequestMessage{&lt;br&gt;
    string username=1;&lt;br&gt;
    string password=2;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;message LoginResponseMessage{&lt;br&gt;
    User user=1;&lt;br&gt;
    string access_token=2;&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Same for `rpc_signup.proto`:

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

&lt;/div&gt;


&lt;p&gt;syntax="proto3";&lt;br&gt;
package pb;&lt;br&gt;
import "user.proto";&lt;br&gt;
option go_package="github.com/djsmk123/server/pb";&lt;/p&gt;

&lt;p&gt;message SignupRequestMessage{&lt;br&gt;
    string username=1;&lt;br&gt;
    string password=2;&lt;br&gt;
    string name=3;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;message SignupResponseMessage{&lt;br&gt;
    User user=1;&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- To create message `rpc_get_user.proto`: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;syntax="proto3";&lt;br&gt;
package pb;&lt;br&gt;
import "user.proto";&lt;br&gt;
option go_package="github.com/djsmk123/server/pb";&lt;/p&gt;

&lt;p&gt;message GetUserResponse{&lt;br&gt;
    User user=1;&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Create service `rpc_services.proto`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;syntax="proto3";&lt;br&gt;
package pb;&lt;br&gt;
option go_package="github.com/djsmk123/server/pb";&lt;br&gt;
import "empty_request.proto";&lt;br&gt;
import "rpc_get_user.proto";&lt;br&gt;
import "rpc_login.proto";&lt;br&gt;
import "rpc_signup.proto";&lt;/p&gt;

&lt;p&gt;service GrpcServerService {&lt;br&gt;
    rpc SignUp(SignupRequestMessage) returns (SignupResponseMessage){};&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rpc login(LoginRequestMessage) returns (LoginResponseMessage){};
rpc GetUser(EmptyRequest) returns (GetUserResponse) {};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;br&gt;
message EmptyRequest{&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Generate code for Golang

- Create `pb` package in root folder.
- Run command to generate equivalent code for golang 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;protoc --proto_path=proto --go_out=pb --go_opt=paths=source_relative \&lt;br&gt;
    --go-grpc_out=pb --go-grpc_opt=paths=source_relative \&lt;br&gt;
    proto/*.proto&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
![image 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xrjl4bhl3vh8woq1pzsv.png)

## Create Basic Auth function
- Create `Auth` Package: This package has basic function like `get-user`,`login`, `signup`,`tokenCreation` and function to convert MongoDB userModel to gRPC `user` Messsage.

- Create `converter.go` in `auth` package.
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create &lt;code&gt;token_generate.go&lt;/code&gt; to generate token.&lt;/p&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create &lt;code&gt;login.go&lt;/code&gt;&lt;/p&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create &lt;code&gt;signup.go&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;get-user.go&lt;/code&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;

&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Setup Server and Middlware
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;server.go&lt;/code&gt; in &lt;code&gt;gapi&lt;/code&gt; package struct to register &lt;code&gt;GrpcServiceServer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;For creating middlware for Authentication, we will need &lt;a href="https://shijuvar.medium.com/writing-grpc-interceptors-in-go-bf3e7671fe48" rel="noopener noreferrer"&gt;&lt;code&gt;Unary Interceptors&lt;/code&gt;&lt;/a&gt; but before that we will check,if request service required authentication or not, we will list all method that are not required token.
&amp;gt; Note by default all the method have restriction for auth token.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create &lt;code&gt;services.go&lt;/code&gt;&lt;/p&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;middlware.go&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;h2&gt;
  
  
  Connect whole and start server
&lt;/h2&gt;

&lt;p&gt;finally we are going to main function in &lt;code&gt;main.go&lt;/code&gt;,lets load environment, connect database(MongoDb) and RunServer.&lt;/p&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;h2&gt;
  
  
  Run Server
&lt;/h2&gt;

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

&lt;/div&gt;



&lt;p&gt;go run .&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Test server

You can test gRPC services using [`evans-cli`](https://github.com/ktr0731/evans).
- Start CLI Tool

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

&lt;/div&gt;


&lt;p&gt;evans --host localhost --port 9090 -r repl&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Call `login` service

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

&lt;/div&gt;


&lt;p&gt;call login&lt;/p&gt;


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


![output1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vkdhip9jx584ne8z3h8p.png)

!!!Oops,we forgot to define `login`,`get-user` and `signup` from `pb` interface.


- Create `auth.go` in `gapi` package.

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;h2&gt;
  
  
  Try again
&lt;/h2&gt;

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


&lt;h2&gt;
  
  
  Flutter Project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This Flutter application features four key pages: &lt;code&gt;SplashScreen&lt;/code&gt;, &lt;code&gt;LoginScreen&lt;/code&gt;, &lt;code&gt;SignUpScreen&lt;/code&gt;, and &lt;code&gt;HomeScreen&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SplashScreen&lt;/strong&gt;: This initial page verifies the user's authentication status. If authenticated, it communicates with the &lt;code&gt;gRPC&lt;/code&gt; server using the &lt;code&gt;get-user&lt;/code&gt; service and redirects the user to the &lt;code&gt;HomeScreen&lt;/code&gt;. Otherwise, it navigates to the &lt;code&gt;LoginScreen&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LoginScreen&lt;/strong&gt;: The LoginScreen presents fields for entering a &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt;. Upon pressing the submit button, the application invokes the &lt;code&gt;login&lt;/code&gt; service to authenticate the user. The &lt;code&gt;access_token&lt;/code&gt; is saved to local storage, and the user is redirected to the &lt;code&gt;HomeScreen&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SignUpScreen&lt;/strong&gt;: The SignUpScreen offers fields for &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;, and &lt;code&gt;confirmpassword&lt;/code&gt;. When the user submits the form, the application calls the &lt;code&gt;signup&lt;/code&gt; service on the &lt;code&gt;gRPC&lt;/code&gt; server.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&amp;gt; Note: Not going to create whole UI and remaining Login,but only will cover required parts. You are free to create your own ui or can refer from whole in github repo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup Project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add following dependencies into &lt;code&gt;pubspec.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;shared_preferences: ^2.2.1&lt;br&gt;
  protobuf: ^3.1.0&lt;br&gt;
  grpc: ^3.2.3&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Install `protoc_plugin` for dart.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;dart pub global activate protoc_plugin&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Copy `.proto` files from server to root folder of the flutter project.

- Created equivalent code in dart

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

&lt;/div&gt;


&lt;p&gt;protoc --proto_path=proto --dart_out=grpc:lib/pb proto/*.proto&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Create channel for gRPC services `services/grpc_services.dart`
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Create AuthServices for different method like &lt;code&gt;login&lt;/code&gt;,&lt;code&gt;signup&lt;/code&gt; and &lt;code&gt;get-user&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;We are done just call function in presentation layer and add your own logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  OUTPUT
&lt;/h2&gt;

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

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

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

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

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

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

&lt;p&gt;Thanks for joining me on this tech adventure! We hope you've enjoyed the journey through gRPC, Flutter, and Golang as much as I have. But wait, the fun doesn't stop here!&lt;/p&gt;

&lt;p&gt;Stay tuned for my upcoming blogs, where I'll sprinkle more humor, insights, and tech wizardry into your day. Who knows, in my next episode, we might just unveil the secret to debugging code with a magic wand (disclaimer: we can't actually do that, but a coder can dream, right?).&lt;/p&gt;

&lt;p&gt;Until then, keep coding, keep laughing, and keep those fingers ready on the refresh button because the next tech tale is just around the corner! 🚀✨"&lt;/p&gt;
&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Djsmk123/wtf-is-grpc" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Follow me on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.instagram.com/smkwinner/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.github.com/djsmk123" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@djsmk123" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>go</category>
      <category>grpc</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Hydrated Bloc: Persist your App State.</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Mon, 07 Aug 2023 06:52:33 +0000</pubDate>
      <link>https://dev.to/djsmk123/hydrated-bloc-persist-your-app-state-54fe</link>
      <guid>https://dev.to/djsmk123/hydrated-bloc-persist-your-app-state-54fe</guid>
      <description>&lt;p&gt;Quench your app's thirst with the magic of Flutter and the power of "Hydrated Bloc" - the ultimate elixir to keep your application state hydrated and happy! 🧊💧&lt;/p&gt;

&lt;p&gt;"Hydrated Bloc" simplifies state persistence in Flutter, making it effortless to store and restore your app's state. Say goodbye to manual state serialization and deserialization – this nifty extension takes care of it all! With Hydrated Bloc, you can focus on crafting your app's features and delighting your users without worrying about state management complexities.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Hydrated Bloc and How Does it Work?
&lt;/h2&gt;

&lt;p&gt;Hydrated Bloc is a powerful extension built on top of the bloc package in Flutter. It automates the process of state persistence and restoration, making it easy to maintain your app's state across different sessions. When you create a &lt;code&gt;BlocProvider&lt;/code&gt;, Hydrated Bloc automatically leverages the &lt;code&gt;fromJson&lt;/code&gt; method to retrieve the stored state from the local storage.&lt;/p&gt;

&lt;p&gt;Whenever the bloc's state changes, the &lt;code&gt;toJson&lt;/code&gt; method comes into play. It converts the current state into a &lt;code&gt;Map&amp;lt;String, dynamic&amp;gt;&lt;/code&gt; format, which is then stored in the local storage. This ensures that your app's state is always up-to-date and ready to be restored when needed, even if the app is closed or the user navigates between screens.&lt;/p&gt;

&lt;p&gt;Hydrated Bloc streamlines the process of state persistence, preventing the hassle of handling manual serialization and deserialization. By automating state management, it enhances your app's reliability and eliminates the risk of state loss.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Hydrated Bloc Over Traditional Bloc
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Hydrated Bloc&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Bloc&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Persistence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automates state persistence and restoration.&lt;/td&gt;
&lt;td&gt;Manual state serialization and deserialization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Package Dependency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Built as an extension to package:bloc.&lt;/td&gt;
&lt;td&gt;Directly uses package:bloc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simplifies state management with automatic persistence.&lt;/td&gt;
&lt;td&gt;Requires additional handling for state persistence.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Initialization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HydratedBloc initializes from stored state if available.&lt;/td&gt;
&lt;td&gt;Bloc starts with initial state on each launch.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Utilizes local storage(&lt;code&gt;hive&lt;/code&gt;[No SQL]) to persist the states.&lt;/td&gt;
&lt;td&gt;No built-in state storage, left to developers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App Reliability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enhances app reliability by preventing state loss.&lt;/td&gt;
&lt;td&gt;Prone to state loss if not handled properly.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Getting Started with Hydrated Bloc
&lt;/h2&gt;

&lt;p&gt;Using Hydrated Bloc is a breeze! You have two options to get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By Extending: You can use Hydrated Bloc by extending the existing bloc class.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends HydratedBloc&amp;lt;CounterEvent, int&amp;gt; {
  CounterBloc() : super(0) {
    on&amp;lt;CounterIncrementPressed&amp;gt;((event, emit) =&amp;gt; emit(state + 1));
  }

  @override
  int fromJson(Map&amp;lt;String, dynamic&amp;gt; json) =&amp;gt; json['value'] as int;

  @override
  Map&amp;lt;String, int&amp;gt; toJson(int state) =&amp;gt; { 'value': state };
}

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

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Using HydratedMixin:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CounterBloc extends Bloc&amp;lt;CounterEvent, int&amp;gt; with HydratedMixin {
  CounterBloc() : super(0) {
    on&amp;lt;CounterIncrementPressed&amp;gt;((event, emit) =&amp;gt; emit(state + 1));
  }

  @override
  int fromJson(Map&amp;lt;String, dynamic&amp;gt; json) =&amp;gt; json['value'] as int;

  @override
  Map&amp;lt;String, int&amp;gt; toJson(int state) =&amp;gt; { 'value': state };
}

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

&lt;/div&gt;


&lt;p&gt;To access and manipulate the storage object in Hydrated Bloc, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a custom storage class by extending the &lt;code&gt;Storage&lt;/code&gt; class.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyHydratedStorage implements Storage {
  @override
  dynamic read(String key) {
    // TODO: implement read
  }

  @override
  Future&amp;lt;void&amp;gt; write(String key, dynamic value) async {
    // TODO: implement write
  }

  @override
  Future&amp;lt;void&amp;gt; delete(String key) async {
    // TODO: implement delete
  }

  @override
  Future&amp;lt;void&amp;gt; clear() async {
    // TODO: implement clear
  }
}

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

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Set the custom storage implementation:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HydratedBloc.storage = MyHydratedStorage();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside the custom storage class, you can implement methods to read, write, delete, and clear data. These methods allow you to interact with the local storage and manage data for the Hydrated Bloc.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's Create a Simple To-Do Application
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a Flutter project and add the required dependencies to your &lt;code&gt;pubspec.yaml&lt;/code&gt; file.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  hydrated_bloc: ^9.1.2  //hydrated bloc
  path_provider: ^2.0.15  // for getTemporaryDirectory
  flutter_easyloading: ^3.0.5 // Helpful for showing loading without context
  flutter_bloc: ^8.1.3 //flutter bloc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;for more information &lt;a href="https://pub.dev/packages/hydrated_bloc" rel="noopener noreferrer"&gt;&lt;code&gt;hydrated_bloc&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define the To-Do model for JSON serialization.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create the Todo repository to manage tasks.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Define the possible states and events for the Todo Bloc.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Implement the Todo Bloc with Hydrated Bloc.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Initialize the Hydrated Bloc storage in &lt;code&gt;main.dart&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Future&amp;lt;void&amp;gt; main() async {
  WidgetsFlutterBinding.ensureInitialized();
  HydratedBloc.storage = await HydratedStorage.build(
    storageDirectory: kIsWeb
        ? HydratedStorage.webStorageDirectory
        : await getTemporaryDirectory(),
  );
  runApp(const MyApp());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now we are remaining with UI and widgets for that you can create own or refer to &lt;a href="https://github.com/Djsmk123/todo_app_hydrated_bloc" rel="noopener noreferrer"&gt;github-repo&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Various function to manipulate Todo List
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Adding New Todo:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 if (formKey.currentState!.validate()) {
BlocProvider.of&amp;lt;TodoBloc&amp;gt;(context).add(TodoAdd(TodoModel.fromJson({
                        'id': DateTime.now().millisecondsSinceEpoch+
                            Random().nextInt(9999999),
                        'title': titleText.text,
                        'description': description.text,
                        'is_completed': false,
                        'created_at': DateTime.now().toUtc().toIso8601String(),
                        'updated_at': DateTime.now().toUtc().toIso8601String()
                      })));
     Navigator.pop(context);
                    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update ToDo:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BlocProvider.of&amp;lt;TodoBloc&amp;gt;(context).add(TodoUpdate(task.id, value!));

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Remove ToDo:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BlocProvider.of&amp;lt;TodoBloc&amp;gt;(context).add(TodoRemove(task.id));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OUTPUT
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/uxZSam3Tl-s"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Certainly! My gratitude goes out to the amazing authors and contributors of the hydrated_bloc package, including &lt;a href="https://twitter.com/felangelov" rel="noopener noreferrer"&gt;Felix Angelov&lt;/a&gt;and others, for their brilliant work in developing this powerful tool for state management in Flutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code :
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Djsmk123/todo_app_hydrated_bloc" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.instagram.com/smkwinner/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.github.com/djsmk123" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@djsmk123" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>android</category>
      <category>codenewbie</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Connecting the Dots: Exploring Flutter Deep-linking for Seamless User Experiences without Firebase.</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Sun, 25 Jun 2023 16:14:58 +0000</pubDate>
      <link>https://dev.to/djsmk123/connecting-the-dots-exploring-flutter-deep-linking-for-seamless-user-experiences-without-firebase-part-1-47ep</link>
      <guid>https://dev.to/djsmk123/connecting-the-dots-exploring-flutter-deep-linking-for-seamless-user-experiences-without-firebase-part-1-47ep</guid>
      <description>&lt;p&gt;Welcome, fellow Flutter enthusiasts, to a whimsical world where widgets dance, hot reloads rule, and bugs tremble in fear of the mighty Flutter framework! Prepare yourself for a journey through the land of code and laughter as we unravel the hilarious side of Flutter development. So put on your debugging cape, grab a cup of virtual coffee, and get ready to flutter your way through this delightful adventure!.&lt;/p&gt;

&lt;p&gt;We are about to embark on a journey to learn and implement Deep-linking in Flutter. But before we set sail, let's take a moment to review the basics. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Deep-linking?
&lt;/h2&gt;

&lt;p&gt;Let's face it: humans are inherently lazy creatures. We want everything instantly, at the snap of our fingers, or rather, the tap of our screens. When we stumble upon an intriguing post or a captivating cat video on social media, the last thing we want is to be redirected to some random website in a browser. Deep linking swoops in to save the day, allowing us to seamlessly transition from our social media feeds to the juicy content we crave, all within the confines of our favorite apps.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Difference between App-link and Deep links?
&lt;/h2&gt;

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

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/46169025/android-deep-links-and-app-links-confused" rel="noopener noreferrer"&gt;Read More&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note we will started with App Links and deep-links will be integrated in next part using firebase dynamic link.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting Started with App links
&lt;/h2&gt;

&lt;p&gt;So basically we are going to use blog website with &lt;a href="https://reactjs.com" rel="noopener noreferrer"&gt;react js&lt;/a&gt;,hosted on &lt;a href="//vercel.com"&gt;vercel&lt;/a&gt; which will have following routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'/': Home routes,basic dummy data.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;'/blogs': will list all the blogs from &lt;a href="https://dev.to/api/articles?username=djsmk123"&gt;dev.to/api/articles?username=name&lt;/a&gt; &lt;br&gt;
and on click will navigate to route '/blog/:id'.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;'/blog/:id': will show html content from the following API &lt;a href="https://dev.to/api/articles/1483531"&gt;https://dev.to/api/articles/id&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This is completely web framework independent you can you use any web technology for website.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setup Website:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You can create website own your own or just copy following content for example.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;As mentioned above, App Links require digital assets links hosted on your website. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://simonmarquis.github.io/Android-App-Linking/#app-links" rel="noopener noreferrer"&gt;for more information&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create &lt;code&gt;/.well-known/assetlinks.json&lt;/code&gt; file and it should be accessible with following url &lt;code&gt;your-domain.com/.well-known/assetlinks.json&lt;/code&gt;.  (for ReactJS add this file in &lt;code&gt;public&lt;/code&gt; folder)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
    {
        "relation": [
            "delegate_permission/common.handle_all_urls"
        ],
        "target": {
            "namespace": "android_app",
            "package_name": "package_name",
            "sha256_cert_fingerprints": [
               "SHA256KEY"
            ]
        }
    }
]

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

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;package_name&lt;/code&gt;: Add your flutter application package name.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sha256_cert_fingerprints&lt;/code&gt;: Add your SHA256 keys. &lt;a href="https://stackoverflow.com/questions/42290681/android-studio-only-gives-me-sha1-i-need-sha256" rel="noopener noreferrer"&gt;How to create?&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now make this file accessible&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Deploy your project.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;We are done with website part.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setup Flutter Project
&lt;/h2&gt;

&lt;p&gt;For flutter, we are not going to create project from starting, I am just going to share how link is handled.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add following dependencies:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies:
  uni_links: ^0.5.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;For Android&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uni Links supports two types of Android links: "App Links" and "Deep Links".&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- App Links only work with https scheme and require a specified host, plus a hosted file - assetlinks.json. Check the Guide links below.
- Deep Links can have any custom scheme and do not require a host, nor a hosted file. The downside is that any app can claim a scheme + host combo, so make sure yours are as unique as possible, eg. HST0000001://host.com.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You need to declare at least one of the two intent filters in &lt;code&gt;android/app/src/main/AndroidManifest.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;manifest ...&amp;gt;
  &amp;lt;!-- ... other tags --&amp;gt;
  &amp;lt;application ...&amp;gt;
    &amp;lt;activity ...&amp;gt;
      &amp;lt;!-- ... other tags --&amp;gt;

      &amp;lt;!-- Deep Links --&amp;gt;
      &amp;lt;intent-filter&amp;gt;
        &amp;lt;action android:name="android.intent.action.VIEW" /&amp;gt;
        &amp;lt;category android:name="android.intent.category.DEFAULT" /&amp;gt;
        &amp;lt;category android:name="android.intent.category.BROWSABLE" /&amp;gt;
        &amp;lt;!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST --&amp;gt;
        &amp;lt;data
          android:scheme="[YOUR_SCHEME]"
          android:host="[YOUR_HOST]" /&amp;gt;
      &amp;lt;/intent-filter&amp;gt;

      &amp;lt;!-- App Links --&amp;gt;
      &amp;lt;intent-filter android:autoVerify="true"&amp;gt;
        &amp;lt;action android:name="android.intent.action.VIEW" /&amp;gt;
        &amp;lt;category android:name="android.intent.category.DEFAULT" /&amp;gt;
        &amp;lt;category android:name="android.intent.category.BROWSABLE" /&amp;gt;
        &amp;lt;!-- Accepts URIs that begin with https://YOUR_HOST --&amp;gt;
        &amp;lt;data
          android:scheme="https"
          android:host="[YOUR_HOST]" /&amp;gt;
      &amp;lt;/intent-filter&amp;gt;
    &amp;lt;/activity&amp;gt;
  &amp;lt;/application&amp;gt;
&amp;lt;/manifest&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Note: The &lt;code&gt;android:host&lt;/code&gt; attribute is optional for Deep Links.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For iOS: It requires valid apple associated domain, you can refer to this for &lt;a href="https://pub.dev/packages/uni_links" rel="noopener noreferrer"&gt;iOS&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Lets Dive into App Link
&lt;/h2&gt;

&lt;p&gt;if you are using on generated routes which match/exact same as link shared then you do not need to handle Link, it will automatically redirect you to received &lt;code&gt;route&lt;/code&gt;. but if you are not using or want to handle link manually then we have &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getInitialLink()&lt;/code&gt;: 
Returns the link that the app was started with, if any.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should handle this very early in your app's life and handle it only once.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart';
import 'package:flutter/services.dart' show PlatformException;

// ...

  Future&amp;lt;void&amp;gt; initUniLinks() async {
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      final initialLink = await getInitialLink();
      // Parse the link and warn the user, if it is not correct,
      // but keep in mind it could be `null`.
    } on PlatformException {
      // Handle exception by warning the user their action did not succeed
      // return?
    }
  }

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

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Listen for links:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart';

// ...

  StreamSubscription _sub;

  Future&amp;lt;void&amp;gt; initUniLinks() async {
    // ... check initialLink

    // Attach a listener to the stream
    _sub = linkStream.listen((String? link) {
      // Parse the link and warn the user, if it is not correct
    }, onError: (err) {
      // Handle exception by warning the user their action did not succeed
    });

    // NOTE: Don't forget to call _sub.cancel() in dispose()
  }

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

&lt;/div&gt;

&lt;h2&gt;
  
  
  Testing Your Deep Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Testing for Android&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the ADB tool to send test deep-links to your app and verify that they open the correct content.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://host/path/subpath"'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Testing for iOS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use Xcode to simulate test deep-links and verify that they open the correct content.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/bin/xcrun simctl openurl booted "unilinks://host/path/subpath"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Let's Look into Sample Project:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Using Material &lt;code&gt;onGeneratedRoutes&lt;/code&gt; in Flutter as follow:&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Link Share: Using &lt;code&gt;share_plus&lt;/code&gt; package to share the link.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; onPressed: () {
                Share.share(
                    'check out my blog "https://blogs-deeplink-example.vercel.app/blog/${blog?.id}');
              },

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

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Link Handling or listening for AppLinks:&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  OUTPUT
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Website&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;

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

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

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flutter Application&lt;/strong&gt;: &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;PART 2 Soon !!!!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code :
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Djsmk123/flutter_deeplinking_example" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.instagram.com/smkwinner/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.github.com/djsmk123" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@djsmk123" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>webdev</category>
      <category>firebase</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Flutter CC-Avenue Payment Gateway Integration ft. PHP</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Sun, 28 May 2023 08:25:19 +0000</pubDate>
      <link>https://dev.to/djsmk123/flutter-cc-avenue-payment-gateway-integration-ft-php-1nj7</link>
      <guid>https://dev.to/djsmk123/flutter-cc-avenue-payment-gateway-integration-ft-php-1nj7</guid>
      <description>&lt;p&gt;In today's digital era, businesses are constantly seeking secure and reliable payment gateways to facilitate seamless transactions. &lt;a href="https://www.ccavenue.com/" rel="noopener noreferrer"&gt;CCAvenue&lt;/a&gt; is a popular payment gateway in India, offering a wide range of payment options for online businesses. In this blog, we will explore how to integrate CCAvenue into a Flutter application using PHP server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: There is no official SDK available for flutter, so we are going to integrate through Flutter Webview.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have the following requirements in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flutter SDK installed on your machine&lt;/li&gt;
&lt;li&gt;Basic knowledge of Flutter and PHP&lt;/li&gt;
&lt;li&gt;CCAvenue merchant account credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: CCAvenue does not allow payment transaction in live or test mode until you whitelist your Domain/IP. For Whitelisting your domain, you can email them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Lets understand how does transaction Handle through CCAvenue?
&lt;/h2&gt;

&lt;p&gt;CCAvenue has two phase for completing transaction&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Initiate Payment&lt;/strong&gt; : In this step,client send &lt;code&gt;amount&lt;/code&gt; and &lt;code&gt;currency&lt;/code&gt; and other required details to server and than server encrypt these values to a single encrypted(lets says &lt;code&gt;enc_val&lt;/code&gt;) string and send &lt;code&gt;enc_val&lt;/code&gt; and &lt;code&gt;access_code&lt;/code&gt; to client.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: When your CCAvenue approved and whitelisted for transaction then you can get following &lt;code&gt;access_code&lt;/code&gt;,&lt;code&gt;working_key&lt;/code&gt; and &lt;code&gt;merchant_id&lt;/code&gt; from CCAvenue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Generate enc_val: Merchant need to encrypt following set of parameters using encryption tool provided by CCAvenue(We will use PHP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will encrypt following String: &lt;/p&gt;

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

merchant_id=123&amp;amp;order_id=123456redirect_url=www.amazonaws.com/payment
/ccav_resp.phpcancel_url=www.amazonaws.com/payment/ccav_resp.phpamount=1.00&amp;amp;currency=INR



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

&lt;/div&gt;
&lt;p&gt;after encryption we will get string like&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

a5eed2d2a51358773bb1de741b0b2f1261308043c2a8f78bf59d9d3d5081f785792599d64876
220964ebdd1578ce633aae959b804f2b2d53cda3821baf5480b3a082ea89a0e6784af4fef98e0
54f3a5c78f0ec9e611a01dd7666e9903e6b5d62c7a11d8db869d665c0077a292cfa6aba80a1ab
a94882168ede009b2c3806a4b08c781e2e5a7d54411b5a288ff28d499bc9de


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

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Type (length)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merchant Id&lt;/td&gt;
&lt;td&gt;Merchant Id is a unique identifier generated by CCAvenue for each activated merchant.&lt;/td&gt;
&lt;td&gt;Numeric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Order Id&lt;/td&gt;
&lt;td&gt;This ID is used by merchants to identify the order. Ensure that you send a unique id with each request. CCAvenue will not check the uniqueness of this order id. As it generates a unique payment reference number for each order which is sent by the merchant.&lt;/td&gt;
&lt;td&gt;Alphanumeric (30)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redirect Url&lt;/td&gt;
&lt;td&gt;CCAvenue will post the status of the order along with the parameters to this URL.&lt;/td&gt;
&lt;td&gt;Alphanumeric (100)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cancel Url&lt;/td&gt;
&lt;td&gt;CCAvenue will redirect the customer to this URL if the customer cancels the transaction on the billing page.&lt;/td&gt;
&lt;td&gt;Alphanumeric (100)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amount&lt;/td&gt;
&lt;td&gt;Order amount&lt;/td&gt;
&lt;td&gt;Numeric (12, 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Currency&lt;/td&gt;
&lt;td&gt;Currency in which you want to process the transaction. &lt;br&gt; AED - Arab Emirates dirham &lt;br&gt; USD - United States Dollar &lt;br&gt; SAR - Saudi Arabia Riyal &lt;br&gt; INR – Indian Rupee &lt;br&gt; SGD – Singapore Dollar &lt;br&gt; GBP – Pound Sterling &lt;br&gt; EUR – Euro, official currency of Eurozone&lt;/td&gt;
&lt;td&gt;Alphabets (3)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: we will talk about &lt;code&gt;cancel_url&lt;/code&gt; and &lt;code&gt;redirect_url&lt;/code&gt; in the next steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Start payment in WebView: you can start payment in webview through following url&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

https://secure.ccavenue.com/transaction.do?command=initiateTransaction&amp;amp;encRequest=enc_val&amp;amp;access_code=access_code



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

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note for testing replace &lt;code&gt;secure.ccavenue.com&lt;/code&gt; to &lt;code&gt;test.ccavenue.com&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Response Handler: When a user completes a payment, either in case of failure or success, CCAvenue will send an encrypted string to either the redirect_url or cancel_url using a POST request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;redirect_url&lt;/code&gt;: It refers to a webpage hosted on your server's domain/IP, which must be whitelisted by CCAVenue. This webpage will handle the necessary steps after the payment request is completed, including handling both failure and success scenarios.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cancel_url&lt;/code&gt;: Similar to the &lt;code&gt;redirect_url&lt;/code&gt;, the &lt;code&gt;cancel_url&lt;/code&gt; is also hosted on your server's domain/IP. However, it specifically handles requests where the user cancels the payment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we will use decryption tool to decrypt string given by CCAvenue &lt;br&gt;
after payment complete.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lets Start Integration (Server Side)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We need encryption and decryption function to encrypt and decrypted request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;create &lt;code&gt;crypto.php&lt;/code&gt; and with following content:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Install required Library:&lt;/p&gt;

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

sudo apt-get install php7.4-openssl


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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Now we need to create a page that accept &lt;code&gt;POST&lt;/code&gt; request and return &lt;code&gt;enc_val&lt;/code&gt; and &lt;code&gt;access_code&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ccAvenueRequestHandler.php&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: you can edit page as per your requirement,but later on flutter side you need JavaScript function to get desired result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;We also need to create a page like above which accept &lt;code&gt;POST&lt;/code&gt; request and return decrypted data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ccavResponseHandler.php&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: Following url should be accessible through whitelisted domain &lt;code&gt;https://your-domain/ccavResponseHandler.php&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Integration in Flutter(Client):
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Adding following to &lt;code&gt;pubspec.yaml&lt;/code&gt;
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;dependencies:&lt;br&gt;
  http: ^0.13.6&lt;br&gt;
  webview_flutter: ^2.0.6 &lt;br&gt;
  html: &lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Android Configuration: Change in `build.gradle`(android/app)

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

&lt;/div&gt;


&lt;p&gt;android {&lt;br&gt;
    defaultConfig {&lt;br&gt;
        minSdkVersion 19&lt;br&gt;
    }&lt;br&gt;
}&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- Create Payment handler Screen with following content: 
lets say `payment_screen.dart`:

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;isTesting&lt;/code&gt; (Variable):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description: A boolean variable used to indicate whether the application is in testing mode or production mode.&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;bool&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;jsChannels&lt;/code&gt; (Variable):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description: A set of &lt;code&gt;JavascriptChannel&lt;/code&gt; objects that define the JavaScript channels available for communication between the Flutter app and the WebView.&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;Set&amp;amp;lt;JavascriptChannel&amp;amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;initPayment&lt;/code&gt; (Method):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description: This method is responsible for initializing the payment by making an HTTP POST request to the &lt;code&gt;requestInitiateUrl&lt;/code&gt; with the specified amount.&lt;/li&gt;
&lt;li&gt;Parameters:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;amount&lt;/code&gt; (Type: &lt;code&gt;String&lt;/code&gt;): The amount for the payment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Returns: A Future object that resolves to the payment data in JSON format.&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;Future&amp;amp;lt;dynamic&amp;amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;onPageFinished&lt;/code&gt; (Method):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description: This method is called when the WebView finishes loading a page.&lt;/li&gt;
&lt;li&gt;Parameters:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt; (Type: &lt;code&gt;String&lt;/code&gt;): The URL of the loaded page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Returns: &lt;code&gt;void&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;void&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;navigationDelegate&lt;/code&gt; (Method):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description: This method is used to control the navigation behavior of the WebView based on the requested URL.&lt;/li&gt;
&lt;li&gt;Parameters:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nav&lt;/code&gt; (Type: &lt;code&gt;NavigationRequest&lt;/code&gt;): The navigation request object containing information about the requested URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Returns: A &lt;code&gt;NavigationDecision&lt;/code&gt; that determines whether to allow or prevent the navigation.&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;NavigationDecision&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;a href="https://github.com/Djsmk123/ccavenue_flutter_example" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Follow me on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.instagram.com/smkwinner/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.github.com/djsmk123" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@djsmk123" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>php</category>
      <category>tutorial</category>
      <category>android</category>
    </item>
    <item>
      <title>Create Switch Case Kind Widget in Flutter</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Wed, 17 May 2023 17:15:30 +0000</pubDate>
      <link>https://dev.to/djsmk123/create-switch-case-kind-widget-in-flutter-2onj</link>
      <guid>https://dev.to/djsmk123/create-switch-case-kind-widget-in-flutter-2onj</guid>
      <description>&lt;p&gt;Welcome back guys,Today we are going to learn some new quick technique by which we can enhance our flutter code readability.&lt;/p&gt;

&lt;p&gt;We are going to create own Switch-Case like conditional widgets or same like &lt;a href="https://pub.dev/packages/flutter_bloc" rel="noopener noreferrer"&gt;bloc state&lt;/a&gt; (builder).&lt;/p&gt;

&lt;p&gt;We will be going through basics,&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Switch-Case?
&lt;/h2&gt;

&lt;p&gt;In Dart, the switch statement is used for conditional branching based on the value of a variable. It provides a convenient way to execute different code blocks based on the value of an expression. The structure of a switch statement in Dart is as follows&lt;/p&gt;

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

switch (expression) {
  case value1:
    // Code to execute if expression matches value1
    break;
  case value2:
    // Code to execute if expression matches value2
    break;
  // Additional cases
  default:
    // Code to execute if none of the cases match the expression
}


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Conditional Widget
&lt;/h2&gt;

&lt;p&gt;In flutter, for example on the basis of condition you have to hide or show some widget,for example loading indicator so for that we can use &lt;a href="https://api.flutter.dev/flutter/widgets/Visibility-class.html" rel="noopener noreferrer"&gt;visibility&lt;/a&gt; widget,if-else, or ternary operators.&lt;/p&gt;
&lt;h2&gt;
  
  
  If we have options then why we need switch case widget
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Visibility: we know that visible hide the specified on widget on given &lt;code&gt;boolean&lt;/code&gt; value,for example
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Visibility(&lt;br&gt;
   visible: false,&lt;br&gt;
   child: Text("Hello,world")),&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;it is obviously good option but it will not work on multiple choices  and it can be either Widget A or Widget B (if replacement specified).

- if else or ternary operator: if else/ternary operator are useful to show conditional widget but when the no of cases increase it become difficult to read.

## Solution ??

![meme 1](https://i.redd.it/if3ldk2w2j841.jpg)


for that we are going to create Switch-case like custom widget 

-  Creating Custom StatelessWidget `SwitchCaseWidget`:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;class SwitchCaseWidget extends StatelessWidget {&lt;br&gt;
  final Widget? Function(T? t) stateBuilder;&lt;br&gt;
  final T activeState;&lt;/p&gt;

&lt;p&gt;const SwitchCaseWidget({&lt;br&gt;
    super.key,&lt;br&gt;
    required this.stateBuilder,&lt;br&gt;
    required this.activeState,&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/override"&gt;@override&lt;/a&gt;&lt;br&gt;
  Widget build(BuildContext context) {&lt;br&gt;
    return stateBuilder(activeState) ?? const SizedBox.shrink();&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let's go through each part of the `SwitchCaseWidget` class and its functionality:

1. `class SwitchCaseWidget&amp;lt;T&amp;gt; extends StatelessWidget`: This line defines a Dart class named `SwitchCaseWidget` that is generic over a type `T`. It extends the `StatelessWidget` class, indicating that this widget does not have any mutable state.

2. `final Widget? Function(T? t) stateBuilder;`: It represents a function that takes a value of type `T` or nullable `T` and returns a widget or `null`. The function will be responsible for determining the widget to return based on the value passed to it.

3. `final T activeState;`: This line declares a final variable `value` of type `T`. It represents the value that will be passed to the `stateBuilder` function to determine the widget.

The purpose of the `SwitchCaseWidget` class is to simplify the process of creating widgets based on different cases or values. By providing a `cases` function and a value, you can dynamically determine and render different widgets based on the provided value.

- Before use it let's create states possible,
 for the following example I am going to have 3 states,
   - Loading State 
   - Error State
   - Data Loaded State
most commonly used states in flutter.

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

&lt;/div&gt;



&lt;p&gt;abstract class WidgetSwitchCase {}&lt;/p&gt;

&lt;p&gt;class LoadingWidgetCase extends WidgetSwitchCase {}&lt;/p&gt;

&lt;p&gt;class ErrorWidgetCase extends WidgetSwitchCase {&lt;br&gt;
  final String message;&lt;br&gt;
  ErrorWidgetCase({required this.message});&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;class DataLoadedCase extends WidgetSwitchCase {&lt;br&gt;
  List data = List.generate(12, (index) =&amp;gt; index);&lt;br&gt;
}&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Here's an explanation of each state class:

1. `LoadingWidgetCase`: This class represents the loading state of the widget. It is a subclass of `WidgetSwitchCase`, indicating that it is one of the possible cases handled by the `WidgetSwitchCase`. It doesn't have any additional properties or methods.

2. `ErrorWidgetCase`: This class represents the error state of the widget. It is also a subclass of `WidgetSwitchCase`. It has an additional property `message` of type `String` that stores the error message. When an instance of `ErrorWidgetCase` is used, the error message can be passed to it via the constructor.

3. `DataLoadedCase`: This class represents the state when data is successfully loaded. It is also a subclass of `WidgetSwitchCase`. It has an additional property `data` which is a `List&amp;lt;int&amp;gt;` holding some sample data. In this example, it generates a list of integers from 0 to 11 using the `List.generate` method.

&amp;gt; Note : You can define your own custom states.

## Usage:

It has very easy usage and also good readability

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

&lt;/div&gt;



&lt;p&gt;SwitchCaseWidget(&lt;br&gt;
            activeState: switchCaseExample,&lt;br&gt;
            stateBuilder: (WidgetSwitchCase? value) {&lt;br&gt;
              if (value is LoadingWidgetCase) {&lt;br&gt;
                return const CircularProgressIndicator();&lt;br&gt;
              }&lt;br&gt;
              if (value is ErrorWidgetCase) {&lt;br&gt;
                return Text(&lt;br&gt;
                  value.message,&lt;br&gt;
                  style: const TextStyle(color: Colors.red, fontSize: 30),&lt;br&gt;
                );&lt;br&gt;
              }&lt;br&gt;
              if (value is DataLoadedCase) {&lt;br&gt;
                return Center(&lt;br&gt;
                  child: Column(&lt;br&gt;
                    mainAxisAlignment: MainAxisAlignment.center,&lt;br&gt;
                    children: value.data&lt;br&gt;
                        .map((e) =&amp;gt; Text(&lt;br&gt;
                              e.toString(),&lt;br&gt;
                              style: const TextStyle(fontSize: 20),&lt;br&gt;
                            ))&lt;br&gt;
                        .toList(),&lt;br&gt;
                  ),&lt;br&gt;
                );&lt;br&gt;
              }&lt;br&gt;
              //default case&lt;br&gt;
              return null;&lt;br&gt;
            }),&lt;/p&gt;



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

OUTPUT


- Loading state:
  ![loading state](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8lumy44uoigoae65pum.png)

- Error State

![Error State](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o1acdkghb2k6p6fgvs19.png)

- Data loaded State


![data loaded state](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/89qk5jc2oav41j390mo3.png)


&amp;gt; Note: it is not going to reduce if-else condition but add more readability then normal if else in Flutter State widget.














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

&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>tutorial</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Flutter pagination ft. Bloc</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Sat, 22 Apr 2023 14:52:16 +0000</pubDate>
      <link>https://dev.to/djsmk123/flutter-pagination-ft-bloc-4hg3</link>
      <guid>https://dev.to/djsmk123/flutter-pagination-ft-bloc-4hg3</guid>
      <description>&lt;p&gt;Welcome back, everyone! Today, we will be learning about implementing pagination in Flutter using Bloc as our state management solution. To fetch products, we will be using a modified version of the &lt;a href="https://fakestoreapi.com/" rel="noopener noreferrer"&gt;fake-store APIs&lt;/a&gt; that includes pagination.&lt;/p&gt;

&lt;p&gt;Let's begin with the basics before diving into Flutter-specific details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7j39wp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7j39wp.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not worry we are going to talk about bloc's basic&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-why-pagination"&gt;Why Pagination?&lt;/h2&gt;

&lt;p&gt;Pagination refers to the practice of dividing content into multiple pages to improve user experience and page load times. It is commonly used in websites and applications that feature long-form content such as articles, product listings, and search results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7j3ad1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7j3ad1.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="heading-setup-project"&gt;Setup Project&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add the following dependency to your Flutter project(&lt;code&gt;pubspec.yaml&lt;/code&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &lt;span class="hljs-attr"&gt;flutter_bloc:&lt;/span&gt; &lt;span class="hljs-string"&gt;^8.1.2&lt;/span&gt;
    &lt;span class="hljs-attr"&gt;http:&lt;/span&gt; &lt;span class="hljs-string"&gt;^0.13.5&lt;/span&gt;
    &lt;span class="hljs-attr"&gt;dartz:&lt;/span&gt; &lt;span class="hljs-string"&gt;^0.10.1&lt;/span&gt;
    &lt;span class="hljs-attr"&gt;shimmer:&lt;/span&gt; &lt;span class="hljs-string"&gt;^2.0.0&lt;/span&gt;
    &lt;span class="hljs-attr"&gt;cached_network_image:&lt;/span&gt; &lt;span class="hljs-string"&gt;^3.2.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;darts&lt;/code&gt;: In Flutter, Dartz is a library that provides functional programming tools such as the Either data type. Either data type represents a value that can be one of two possible types, typically used to represent the success or failure of a computation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;shimmer&lt;/code&gt;: Shimmer library is being used for showing loading indicator link on LinkedIn, Twitter, Facebook etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cached_network_image&lt;/code&gt;: Flutter library, as its name suggests, is used for caching network images.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;http&lt;/code&gt;: Flutter library for calling &lt;code&gt;HTTP&lt;/code&gt; requests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="heading-project-file-structure"&gt;Project file Structure:&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682166877182%2Fd01a0be6-ef11-4a7c-a75c-d6614652cdc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682166877182%2Fd01a0be6-ef11-4a7c-a75c-d6614652cdc5.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;models&lt;/code&gt;: following folders will contain models that will be used in the application.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;presentation&lt;/code&gt;: The following layer has been divided further into &lt;code&gt;blocs&lt;/code&gt;,&lt;code&gt;pages&lt;/code&gt;, and &lt;code&gt;widgets&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;widgets&lt;/code&gt;: Widgets are the building blocks of the user interface in Flutter. They are reusable UI elements that can be combined to create complex layouts. Widgets can be stateless, meaning they do not change over time, or stateful, meaning they can change based on user interaction or other events.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;blocs&lt;/code&gt;: Blocs are a pattern for managing state in Flutter applications. They stand for Business Logic Components and are responsible for managing the flow of data between the presentation layer and the data layer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;pages&lt;/code&gt;: Pages are the top-level UI elements in a Flutter application. Each page typically represents a separate screen or view.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;li&gt;

&lt;p&gt;&lt;code&gt;repo&lt;/code&gt;: Stands for the repository which has logic for calling APIs it passes data to models.&lt;/p&gt;


&lt;/li&gt;


&lt;/ul&gt;

&lt;h2 id="heading-parsing-apis-data-to-dart-model-classes"&gt;Parsing APIs Data to Dart Model-Classes&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codewithandrea.com/articles/parse-json-dart/" rel="noopener noreferrer"&gt;Don't know how to parse JSON data to Dart class?&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are going to use a modified version of FakeStore APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;Base URL=&lt;span class="hljs-string"&gt;"https://flutter-pagination-api-djsmk123.vercel.app/api&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Endpoint &lt;code&gt;/get-products?page=1&lt;/code&gt; by default &lt;code&gt;page=1&lt;/code&gt;. How the response will look like?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  {

      &lt;span class="hljs-attr"&gt;"products"&lt;/span&gt;: [
          {
              &lt;span class="hljs-attr"&gt;"id"&lt;/span&gt;: &lt;span class="hljs-number"&gt;1&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"title"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"price"&lt;/span&gt;: &lt;span class="hljs-number"&gt;109.95&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"description"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"category"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"men's clothing"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"image"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"rating"&lt;/span&gt;: {
                  &lt;span class="hljs-attr"&gt;"rate"&lt;/span&gt;: &lt;span class="hljs-number"&gt;3.9&lt;/span&gt;,
                  &lt;span class="hljs-attr"&gt;"count"&lt;/span&gt;: &lt;span class="hljs-number"&gt;120&lt;/span&gt;
              }
          },
          {
              &lt;span class="hljs-attr"&gt;"id"&lt;/span&gt;: &lt;span class="hljs-number"&gt;2&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"title"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"Mens Casual Premium Slim Fit T-Shirts "&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"price"&lt;/span&gt;: &lt;span class="hljs-number"&gt;22.3&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"description"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight &amp;amp; soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket."&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"category"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"men's clothing"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"image"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"https://fakestoreapi.com/img/71-3HjGNDUL._AC_SY879._SX._UX._SY._UY_.jpg"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"rating"&lt;/span&gt;: {
                  &lt;span class="hljs-attr"&gt;"rate"&lt;/span&gt;: &lt;span class="hljs-number"&gt;4.1&lt;/span&gt;,
                  &lt;span class="hljs-attr"&gt;"count"&lt;/span&gt;: &lt;span class="hljs-number"&gt;259&lt;/span&gt;
              }
          },
          {
              &lt;span class="hljs-attr"&gt;"id"&lt;/span&gt;: &lt;span class="hljs-number"&gt;3&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"title"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"Mens Cotton Jacket"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"price"&lt;/span&gt;: &lt;span class="hljs-number"&gt;55.99&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"description"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"great outerwear jackets for Spring/Autumn/Winter, suitable for many occasions, such as working, hiking, camping, mountain/rock climbing, cycling, traveling or other outdoors. Good gift choice for you or your family member. A warm hearted love to Father, husband or son in this thanksgiving or Christmas Day."&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"category"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"men's clothing"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"image"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"rating"&lt;/span&gt;: {
                  &lt;span class="hljs-attr"&gt;"rate"&lt;/span&gt;: &lt;span class="hljs-number"&gt;4.7&lt;/span&gt;,
                  &lt;span class="hljs-attr"&gt;"count"&lt;/span&gt;: &lt;span class="hljs-number"&gt;500&lt;/span&gt;
              }
          },
          {
              &lt;span class="hljs-attr"&gt;"id"&lt;/span&gt;: &lt;span class="hljs-number"&gt;4&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"title"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"Mens Casual Slim Fit"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"price"&lt;/span&gt;: &lt;span class="hljs-number"&gt;15.99&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"description"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"The color could be slightly different between on the screen and in practice. / Please note that body builds vary by person, therefore, detailed size information should be reviewed below on the product description."&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"category"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"men's clothing"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"image"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"https://fakestoreapi.com/img/71YXzeOuslL._AC_UY879_.jpg"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"rating"&lt;/span&gt;: {
                  &lt;span class="hljs-attr"&gt;"rate"&lt;/span&gt;: &lt;span class="hljs-number"&gt;2.1&lt;/span&gt;,
                  &lt;span class="hljs-attr"&gt;"count"&lt;/span&gt;: &lt;span class="hljs-number"&gt;430&lt;/span&gt;
              }
          },
          {
              &lt;span class="hljs-attr"&gt;"id"&lt;/span&gt;: &lt;span class="hljs-number"&gt;5&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"title"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"John Hardy Women's Legends Naga Gold &amp;amp; Silver Dragon Station Chain Bracelet"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"price"&lt;/span&gt;: &lt;span class="hljs-number"&gt;695&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"description"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"From our Legends Collection, the Naga was inspired by the mythical water dragon that protects the ocean's pearl. Wear facing inward to be bestowed with love and abundance, or outward for protection."&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"category"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"jewelery"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"image"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"https://fakestoreapi.com/img/71pWzhdJNwL._AC_UL640_QL65_ML3_.jpg"&lt;/span&gt;,
              &lt;span class="hljs-attr"&gt;"rating"&lt;/span&gt;: {
                  &lt;span class="hljs-attr"&gt;"rate"&lt;/span&gt;: &lt;span class="hljs-number"&gt;4.6&lt;/span&gt;,
                  &lt;span class="hljs-attr"&gt;"count"&lt;/span&gt;: &lt;span class="hljs-number"&gt;400&lt;/span&gt;
              }
          }
      ],
      &lt;span class="hljs-attr"&gt;"current_page"&lt;/span&gt;: &lt;span class="hljs-number"&gt;1&lt;/span&gt;,
      &lt;span class="hljs-attr"&gt;"reach_max"&lt;/span&gt;: &lt;span class="hljs-literal"&gt;false&lt;/span&gt;

  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="heading-lets-create-dart-class-model"&gt;Let's Create Dart Class Model&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ProductModel&lt;/code&gt;: create a new file in models named &lt;code&gt;product_model.dart&lt;/code&gt; with the following content.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/models/rating_model.dart'&lt;/span&gt;;

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductModel&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; id;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; title;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; description;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; category;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;double&lt;/span&gt; price;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; image;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; RatingModel rating;
    ProductModel({
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.id,
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.title,
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.description,
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.category,
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.price,
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.image,
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.rating,
    });
    &lt;span class="hljs-keyword"&gt;factory&lt;/span&gt; ProductModel.fromJson(&lt;span class="hljs-built_in"&gt;Map&lt;/span&gt;&amp;lt;&lt;span class="hljs-built_in"&gt;String&lt;/span&gt;, &lt;span class="hljs-built_in"&gt;dynamic&lt;/span&gt;&amp;gt; json) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; ProductModel(
        id: json[&lt;span class="hljs-string"&gt;'id'&lt;/span&gt;],
        title: json[&lt;span class="hljs-string"&gt;'title'&lt;/span&gt;],
        description: json[&lt;span class="hljs-string"&gt;'description'&lt;/span&gt;],
        category: json[&lt;span class="hljs-string"&gt;'category'&lt;/span&gt;],
        price: (json[&lt;span class="hljs-string"&gt;'price'&lt;/span&gt;] &lt;span class="hljs-keyword"&gt;as&lt;/span&gt; &lt;span class="hljs-built_in"&gt;num&lt;/span&gt;).toDouble(),
        image: json[&lt;span class="hljs-string"&gt;'image'&lt;/span&gt;],
        rating: RatingModel.fromJson(json[&lt;span class="hljs-string"&gt;'rating'&lt;/span&gt;]),
      );
    }
    toJson() {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; {
        &lt;span class="hljs-string"&gt;"id"&lt;/span&gt;: id,
        &lt;span class="hljs-string"&gt;"title"&lt;/span&gt;: title,
        &lt;span class="hljs-string"&gt;"description"&lt;/span&gt;: description,
        &lt;span class="hljs-string"&gt;"category"&lt;/span&gt;: category,
        &lt;span class="hljs-string"&gt;"price"&lt;/span&gt;: price,
        &lt;span class="hljs-string"&gt;"image"&lt;/span&gt;: image,
        &lt;span class="hljs-string"&gt;"rating"&lt;/span&gt;: rating.toJson(),
      };
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;RatingModel&lt;/code&gt;: same create a new dart file in the same folder with the named &lt;code&gt;rating_model.dart&lt;/code&gt; and the content will be followed as.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;RatingModel&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;double&lt;/span&gt; rate;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; count;
    RatingModel({&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.rate, &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.count});
    &lt;span class="hljs-keyword"&gt;factory&lt;/span&gt; RatingModel.fromJson(&lt;span class="hljs-built_in"&gt;Map&lt;/span&gt;&amp;lt;&lt;span class="hljs-built_in"&gt;String&lt;/span&gt;, &lt;span class="hljs-built_in"&gt;dynamic&lt;/span&gt;&amp;gt; json) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; RatingModel(
          rate: (json[&lt;span class="hljs-string"&gt;'rate'&lt;/span&gt;] &lt;span class="hljs-keyword"&gt;as&lt;/span&gt; &lt;span class="hljs-built_in"&gt;num&lt;/span&gt;).toDouble(), count: json[&lt;span class="hljs-string"&gt;'count'&lt;/span&gt;]);
    }
    toJson() {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; {&lt;span class="hljs-string"&gt;"rate"&lt;/span&gt;: rate, &lt;span class="hljs-string"&gt;"count"&lt;/span&gt;: count};
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;ProductsListModel&lt;/code&gt;: The following model requires for parsing JSON data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/models/product_model.dart'&lt;/span&gt;;

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsListModel&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;List&lt;/span&gt;&amp;lt;ProductModel&amp;gt; products;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;bool&lt;/span&gt; reachMax;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; currentPage;
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; ProductsListModel(
        {&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.products,
        &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.currentPage,
        &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.reachMax});
    &lt;span class="hljs-keyword"&gt;factory&lt;/span&gt; ProductsListModel.fromJson(&lt;span class="hljs-built_in"&gt;Map&lt;/span&gt;&amp;lt;&lt;span class="hljs-built_in"&gt;String&lt;/span&gt;, &lt;span class="hljs-built_in"&gt;dynamic&lt;/span&gt;&amp;gt; json) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; ProductsListModel(
          products: parseProducts(json[&lt;span class="hljs-string"&gt;'products'&lt;/span&gt;]),
          currentPage: json[&lt;span class="hljs-string"&gt;'current_page'&lt;/span&gt;],
          reachMax: json[&lt;span class="hljs-string"&gt;'reach_max'&lt;/span&gt;]);
    }
    &lt;span class="hljs-built_in"&gt;Map&lt;/span&gt;&amp;lt;&lt;span class="hljs-built_in"&gt;String&lt;/span&gt;, &lt;span class="hljs-built_in"&gt;dynamic&lt;/span&gt;&amp;gt; toJson() {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; {
        &lt;span class="hljs-string"&gt;'products'&lt;/span&gt;: products.map((e) =&amp;gt; e.toJson()),
        &lt;span class="hljs-string"&gt;'current_page'&lt;/span&gt;: currentPage,
        &lt;span class="hljs-string"&gt;'reach_max'&lt;/span&gt;: reachMax
      };
    }

    &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; &lt;span class="hljs-built_in"&gt;List&lt;/span&gt;&amp;lt;ProductModel&amp;gt; parseProducts(&lt;span class="hljs-built_in"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="hljs-built_in"&gt;dynamic&lt;/span&gt;&amp;gt; p) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; &lt;span class="hljs-built_in"&gt;List&lt;/span&gt;.generate(p.length, (index) =&amp;gt; ProductModel.fromJson(p[index]));
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;Failure&lt;/code&gt;: The failure model will be used to throw errors in &lt;code&gt;Either&lt;/code&gt; and following in &lt;code&gt;models/error_model.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;Failure&lt;/span&gt; &lt;/span&gt;{ 
  &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; message;
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; Failure({&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.message}); 
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="heading-lets-call-api"&gt;Let's Call API&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create class &lt;code&gt;ProductRepo&lt;/code&gt; for calling APIs which has &lt;code&gt;getProducts&lt;/code&gt; static function which will return Either &lt;code&gt;Failure&lt;/code&gt; (error) or &lt;code&gt;ProductListModel&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'dart:convert'&lt;/span&gt;;

  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:dartz/dartz.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:http/http.dart'&lt;/span&gt; &lt;span class="hljs-keyword"&gt;as&lt;/span&gt; http;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/models/error_model.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/models/product_list_model.dart'&lt;/span&gt;;

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductRepo&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;static&lt;/span&gt; Future&amp;lt;Either&amp;lt;Failure, ProductsListModel&amp;gt;&amp;gt; getProducts(
        {&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-built_in"&gt;int&lt;/span&gt; page}) &lt;span class="hljs-keyword"&gt;async&lt;/span&gt; {
      &lt;span class="hljs-keyword"&gt;try&lt;/span&gt; {
        &lt;span class="hljs-built_in"&gt;Map&lt;/span&gt;&amp;lt;&lt;span class="hljs-built_in"&gt;String&lt;/span&gt;, &lt;span class="hljs-built_in"&gt;String&lt;/span&gt;&amp;gt; query = {&lt;span class="hljs-string"&gt;"page"&lt;/span&gt;: page.toString()};
        &lt;span class="hljs-keyword"&gt;var&lt;/span&gt; uri = &lt;span class="hljs-built_in"&gt;Uri&lt;/span&gt;.parse(
                &lt;span class="hljs-string"&gt;"https://flutter-pagination-api-djsmk123.vercel.app/api/get-products"&lt;/span&gt;)
            .replace(queryParameters: query);
        &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; response =
            &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; http.&lt;span class="hljs-keyword"&gt;get&lt;/span&gt;(uri, headers: {&lt;span class="hljs-string"&gt;"Content-Type"&lt;/span&gt;: &lt;span class="hljs-string"&gt;"application/json"&lt;/span&gt;});
        &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (response.statusCode == &lt;span class="hljs-number"&gt;200&lt;/span&gt;) {
          &lt;span class="hljs-keyword"&gt;var&lt;/span&gt; json = jsonDecode(response.body);
          ProductsListModel products = ProductsListModel.fromJson(json);
          &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Right(products);
        } &lt;span class="hljs-keyword"&gt;else&lt;/span&gt; {
          &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; Left(Failure(message: &lt;span class="hljs-string"&gt;'Failed to parse json response'&lt;/span&gt;));
        }
      } &lt;span class="hljs-keyword"&gt;catch&lt;/span&gt; (e) {
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; Left(Failure(message: &lt;span class="hljs-string"&gt;'Something went wrong'&lt;/span&gt;));
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcouchto5krunandblog.files.wordpress.com%2F2015%2F05%2Ffinally-half-way.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcouchto5krunandblog.files.wordpress.com%2F2015%2F05%2Ffinally-half-way.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before creating Blocs states, event and their implementation, Let's talk about Bloc's Basics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-blocs"&gt;Bloc's:&lt;/h2&gt;

&lt;p&gt;Flutter Widgets that make it easy to implement the BLoC (Business Logic Component) design pattern. Built to be used with the bloc state management package.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not going to cover Cubit in Flutter Bloc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bloc has three different components &lt;code&gt;state&lt;/code&gt;, &lt;code&gt;event&lt;/code&gt; and &lt;code&gt;emit&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;State&lt;/code&gt;: represents the current state of the application. a &lt;code&gt;state&lt;/code&gt; is an immutable object that represents a snapshot of the application at a specific moment in time. When the &lt;code&gt;State&lt;/code&gt; changes and the UI is rebuilt with the new state.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;event&lt;/code&gt;: an Event is a message or signal that triggers a change in the State of the application. Event objects represent user actions, system events, or any other external change that affects the state of the application.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;emit&lt;/code&gt;: emit method is used to notify the Bloc or Cubit that a new State has been produced. The emit method accepts a single argument, which is the new State object.&lt;/p&gt;
&lt;h2 id="heading-blocbuilder"&gt;&lt;strong&gt;BlocBuilder:&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;  a Flutter widget that requires a &lt;code&gt;bloc&lt;/code&gt; and a &lt;code&gt;builder&lt;/code&gt; function. &lt;code&gt;BlocBuilder&lt;/code&gt; handles building the widget in response to new states. &lt;code&gt;BlocBuilder&lt;/code&gt; is very similar to &lt;code&gt;StreamBuilder&lt;/code&gt; has a more simple API to reduce the amount of boilerplate code needed. The &lt;code&gt;builder&lt;/code&gt; function will potentially be called many times and should be a &lt;a href="https://en.wikipedia.org/wiki/Pure_function" rel="noopener noreferrer"&gt;pure function&lt;/a&gt; that returns a widget in response to the state.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://pub.dev/packages/flutter_bloc" rel="noopener noreferrer"&gt;For more info&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682170093654%2F227cf309-166d-4ce3-a0b6-0bd184cc2de8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682170093654%2F227cf309-166d-4ce3-a0b6-0bd184cc2de8.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2 id="heading-lets-back-to-where-were-we"&gt;Let's Back to where were we.&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-creating-blocs-for-product"&gt;Creating Blocs for product&lt;/h2&gt;

&lt;p&gt;You can create your bloc classes but it is time-consuming so use plugins&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Android Studio: &lt;a href="https://plugins.jetbrains.com/plugin/12129-bloc" rel="noopener noreferrer"&gt;https://plugins.jetbrains.com/plugin/12129-bloc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=FelixAngelov.bloc" rel="noopener noreferrer"&gt;&lt;strong&gt;Vs code&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Keeping Bloc class as `&lt;code&gt;ProductsBloc` &lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-events"&gt;Events&lt;/h2&gt;

&lt;p&gt;We have only one event that is &lt;code&gt;ProductLoadEvent&lt;/code&gt; so create a class for that&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;part&lt;/span&gt; of &lt;span class="hljs-string"&gt;'products_bloc.dart'&lt;/span&gt;;

&lt;span class="hljs-meta"&gt;@immutable&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;abstract&lt;/span&gt; &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsEvent&lt;/span&gt; &lt;/span&gt;{}

&lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsLoadEvent&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;ProductsEvent&lt;/span&gt; &lt;/span&gt;{}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="heading-states"&gt;States&lt;/h2&gt;

&lt;p&gt;we could have multiple states like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Initial loading:&lt;/strong&gt; Initially we have to show a loading indicator while the first page is keep fetching from APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initial Error:&lt;/strong&gt; While fetching items from API, there could be &lt;strong&gt;an&lt;/strong&gt; error.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Empty List:&lt;/strong&gt; it might happen that we got an empty list in response.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Product fetch success:&lt;/strong&gt; When you have successfully fetched data from API and now load more while keeping earlier data so it has conditional value, isLoading or hasError.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;part&lt;/span&gt; of &lt;span class="hljs-string"&gt;'products_bloc.dart'&lt;/span&gt;;

  &lt;span class="hljs-meta"&gt;@immutable&lt;/span&gt;
  &lt;span class="hljs-keyword"&gt;abstract&lt;/span&gt; &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsState&lt;/span&gt; &lt;/span&gt;{}

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsInitial&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;ProductsState&lt;/span&gt; &lt;/span&gt;{}

  &lt;span class="hljs-comment"&gt;//State for initial Loading when current page will be 1&lt;/span&gt;
  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsInitialLoading&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;ProductsState&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; message;
    ProductsInitialLoading({&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.message});
  }

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductInitialError&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;ProductsState&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; message;
    ProductInitialError({&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.message});
  }

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsEmpty&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;ProductsState&lt;/span&gt; &lt;/span&gt;{}

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsLoaded&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;ProductsState&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; ProductsListModel products;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; LoadingMore? loading;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; LoadMoreError? error;
    ProductsLoaded({
      &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.products,
      &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.loading,
      &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.error,
    });
  }
  &lt;span class="hljs-comment"&gt;// LoadingMore Model&lt;/span&gt;
  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;LoadingMore&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; message;
    LoadingMore({&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.message});
  }
  &lt;span class="hljs-comment"&gt;// LoadingMoreError Model&lt;/span&gt;
  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;LoadMoreError&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;String&lt;/span&gt; message;
    LoadMoreError({&lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.message});
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="heading-lets-implement-event-and-states"&gt;Let's implement Event and States&lt;/h2&gt;

&lt;p&gt;Add the following content in &lt;code&gt;products_bloc.dart&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter_bloc/flutter_bloc.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/models/product_list_model.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/repo/products_repo.dart'&lt;/span&gt;;

&lt;span class="hljs-keyword"&gt;part&lt;/span&gt; &lt;span class="hljs-string"&gt;'products_event.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;part&lt;/span&gt; &lt;span class="hljs-string"&gt;'products_state.dart'&lt;/span&gt;;

&lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsBloc&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;Bloc&lt;/span&gt;&amp;lt;&lt;span&gt;ProductsEvent&lt;/span&gt;, &lt;span&gt;ProductsState&lt;/span&gt;&amp;gt; &lt;/span&gt;{
  ProductsListModel products = &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; ProductsListModel(
    products: [],
    currentPage: &lt;span class="hljs-number"&gt;1&lt;/span&gt;,
    reachMax: &lt;span class="hljs-keyword"&gt;false&lt;/span&gt;,
  );
  ProductsBloc() : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(ProductsInitial()) {
    &lt;span class="hljs-keyword"&gt;on&lt;/span&gt;&amp;lt;ProductsEvent&amp;gt;((event, emit) &lt;span class="hljs-keyword"&gt;async&lt;/span&gt; {
      &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (event &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; ProductsLoadEvent) {
        &lt;span class="hljs-built_in"&gt;bool&lt;/span&gt; isInitial = products.currentPage == &lt;span class="hljs-number"&gt;1&lt;/span&gt;;
        isInitial
            ? emit(ProductsInitialLoading(message: &lt;span class="hljs-string"&gt;'Fetching products....'&lt;/span&gt;))
            : emit(ProductsLoaded(
                products: products,
                loading: LoadingMore(message: &lt;span class="hljs-string"&gt;'Fetching more products...'&lt;/span&gt;)));
        &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; response =
            &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; ProductRepo.getProducts(page: products.currentPage);
        response.fold(
            (l) =&amp;gt; isInitial
                ? emit(ProductInitialError(message: &lt;span class="hljs-string"&gt;'Failed to load products'&lt;/span&gt;))
                : emit(ProductsLoaded(
                    products: products,
                    error: LoadMoreError(
                        message: &lt;span class="hljs-string"&gt;'Failed to load more products'&lt;/span&gt;))), (r) {
          &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (isInitial) {
            products = ProductsListModel(
                products: r.products,
                currentPage: r.currentPage + &lt;span class="hljs-number"&gt;1&lt;/span&gt;,
                reachMax: r.reachMax);

            &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (products.products.isEmpty) {
              emit(ProductsEmpty());
            }
          } &lt;span class="hljs-keyword"&gt;else&lt;/span&gt; {
            &lt;span class="hljs-comment"&gt;//Adding products to existing list&lt;/span&gt;
            products = ProductsListModel(
                products: products.products + r.products,
                currentPage: r.currentPage + &lt;span class="hljs-number"&gt;1&lt;/span&gt;,
                reachMax: r.reachMax);
          }
          emit(ProductsLoaded(products: products));
        });
      }
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;Done with Flutter Bloc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="heading-lets-implement-ui"&gt;Let's Implement UI&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;create a new &lt;code&gt;stateful&lt;/code&gt; class named &lt;code&gt;ProductPage&lt;/code&gt; in &lt;code&gt;/presentation/pages/&lt;/code&gt; with the following content.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter_bloc/flutter_bloc.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/colors.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/models/product_model.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/presentation/blocs/products_bloc.dart'&lt;/span&gt;;

  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'../widgets/widgets.dart'&lt;/span&gt;;

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductsPage&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;StatefulWidget&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; ProductsPage({Key? key}) : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(key: key);

    &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
    State&amp;lt;ProductsPage&amp;gt; createState() =&amp;gt; _ProductsPageState();
  }

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;_ProductsPageState&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;State&lt;/span&gt;&amp;lt;&lt;span&gt;ProductsPage&lt;/span&gt;&amp;gt; &lt;/span&gt;{
    &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; initState() {
      BlocProvider.of&amp;lt;ProductsBloc&amp;gt;(context).add(ProductsLoadEvent());
      &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;.initState();
    }

    &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
    Widget build(BuildContext context) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Scaffold(
          appBar: AppBar(
            leading: Icon(
              Icons.menu,
              color: primaryColor,
              size: &lt;span class="hljs-number"&gt;30&lt;/span&gt;,
            ),
            title: Text(
              &lt;span class="hljs-string"&gt;"Flutter Pagination"&lt;/span&gt;,
              style: TextStyle(
                color: primaryColor,
                fontSize: &lt;span class="hljs-number"&gt;20&lt;/span&gt;,
              ),
            ),
            centerTitle: &lt;span class="hljs-keyword"&gt;true&lt;/span&gt;,
          ),
          body: PaginationWidget&amp;lt;ProductModel&amp;gt;(
            loadMore: () {
              BlocProvider.of&amp;lt;ProductsBloc&amp;gt;(context).add(ProductsLoadEvent());
            },
            initialEmpty: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EmptyWidget(),
            initialLoading: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; LoadingWidget(),
            initialError: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; CustomErrorWidget(),
            child: (ProductModel productModel) {
              &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; ProductCard(product: productModel);
            },
          ));
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create &lt;code&gt;PaginationWidget&lt;/code&gt; stateless class in &lt;code&gt;widgets&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;PaginationWidget&lt;/span&gt;&amp;lt;&lt;span&gt;t&lt;/span&gt;&amp;gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;StatelessWidget&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; &lt;span class="hljs-built_in"&gt;Function&lt;/span&gt;() loadMore;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; Widget initialError;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; Widget initialLoading;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; Widget initialEmpty;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; Widget &lt;span class="hljs-built_in"&gt;Function&lt;/span&gt;(t p) child;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; Widget? onLoadMoreError;
    &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; Widget? onLoadMoreLoading;
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; PaginationWidget(
        {Key? key,
        &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.loadMore,
        &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.initialError,
        &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.initialLoading,
        &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.initialEmpty,
        &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.onLoadMoreError,
        &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.onLoadMoreLoading,
        &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.child})
        : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(key: key);

    &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
    Widget build(BuildContext context) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; BlocBuilder&amp;lt;ProductsBloc, ProductsState&amp;gt;(
        builder: (context, state) {
          &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (state &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; ProductsLoaded) {
            &lt;span class="hljs-built_in"&gt;List&lt;/span&gt;&amp;lt;ProductModel&amp;gt; products = state.products.products;
            &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; NotificationListener&amp;lt;ScrollEndNotification&amp;gt;(
                onNotification: (scrollInfo) {
                  scrollInfo.metrics.pixels ==
                              scrollInfo.metrics.maxScrollExtent &amp;amp;&amp;amp;
                          !state.products.reachMax
                      ? loadMore()
                      : &lt;span class="hljs-keyword"&gt;null&lt;/span&gt;;
                  &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; &lt;span class="hljs-keyword"&gt;true&lt;/span&gt;;
                },
                child: Column(
                  children: [
                    Expanded(
                        child: ListView.builder(
                            itemCount: products.length,
                            itemBuilder: (context, index) =&amp;gt;
                                child(products[index] &lt;span class="hljs-keyword"&gt;as&lt;/span&gt; t))),
                    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                      height: &lt;span class="hljs-number"&gt;20&lt;/span&gt;,
                    ),
                    &lt;span class="hljs-comment"&gt;//if error occured while loading more&lt;/span&gt;
                    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (state.error != &lt;span class="hljs-keyword"&gt;null&lt;/span&gt;)
                      Expanded(child: onLoadMoreError ?? initialError),
                    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (state.loading != &lt;span class="hljs-keyword"&gt;null&lt;/span&gt;)
                      Expanded(child: onLoadMoreLoading ?? initialLoading),
                  ],
                ));
          }
          &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (state &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; ProductsInitialLoading) {
            &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; initialLoading;
          }
          &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (state &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; ProductsEmpty) {
            &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; initialEmpty;
          }
          &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (state &lt;span class="hljs-keyword"&gt;is&lt;/span&gt; ProductInitialError) {
            &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; initialError;
          }
          &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox.shrink();
        },
      );
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;strong&gt;PaginationWidget&lt;/strong&gt; class is a Flutter stateless widget that renders a list of items with pagination support. It takes in several parameters through its constructor to configure its behavior and appearance.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The constructor for the &lt;strong&gt;PaginationWidget&lt;/strong&gt; class has the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;loadMore&lt;/strong&gt;: a function that is called when the user reaches the end of the list and needs to load more items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;initialError&lt;/strong&gt;: a widget to display when an error occurs while loading the initial list of items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;initialLoading&lt;/strong&gt;: a widget to display while the initial list of items is being loaded.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;initialEmpty&lt;/strong&gt;: a widget to display when the initial list of items is empty.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;child&lt;/strong&gt;: a function that takes an item of type t and returns a widget that represents that item in the list.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;onLoadMoreError&lt;/strong&gt;: a widget to display when an error occurs while loading more items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;onLoadMoreLoading&lt;/strong&gt;: a widget to display while more items are being loaded.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;/li&gt;


&lt;/ul&gt;


&lt;/li&gt;


&lt;/ul&gt;


&lt;/li&gt;


&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Empty List item Widget:&lt;/strong&gt; create a widget in &lt;code&gt;widgets/empty_widget&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:cached_network_image/cached_network_image.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;EmptyWidget&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;StatelessWidget&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EmptyWidget({Key? key}) : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(key: key);

    &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
    Widget build(BuildContext context) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CachedNetworkImage(
              imageUrl:
                  &lt;span class="hljs-string"&gt;"https://static.vecteezy.com/system/resources/previews/005/073/073/original/no-item-in-the-shopping-cart-add-product-click-to-shop-now-concept-illustration-flat-design-eps10-modern-graphic-element-for-landing-page-empty-state-ui-infographic-icon-vector.jpg"&lt;/span&gt;,
              height: &lt;span class="hljs-number"&gt;200&lt;/span&gt;,
            ),
            &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
              height: &lt;span class="hljs-number"&gt;30&lt;/span&gt;,
            ),
            Text(&lt;span class="hljs-string"&gt;"No products available"&lt;/span&gt;,
                style: TextStyle(color: Colors.grey.shade500, fontSize: &lt;span class="hljs-number"&gt;24&lt;/span&gt;)),
          ],
        ),
      );
    }
  }
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;


&lt;li&gt;

&lt;p&gt;&lt;code&gt;CustomErrorWidget&lt;/code&gt;: do the same with this, name &lt;code&gt;error_widget.dart.&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:cached_network_image/cached_network_image.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;CustomErrorWidget&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;StatelessWidget&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; CustomErrorWidget({Key? key}) : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(key: key);

    &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
    Widget build(BuildContext context) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CachedNetworkImage(
              imageUrl: &lt;span class="hljs-string"&gt;"https://gcdnb.pbrd.co/images/5Rz9dEYkdYdm.png?o=1"&lt;/span&gt;,
              height: &lt;span class="hljs-number"&gt;200&lt;/span&gt;,
            ),
            &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
              height: &lt;span class="hljs-number"&gt;30&lt;/span&gt;,
            ),
            Text(&lt;span class="hljs-string"&gt;"Error Occured,try again"&lt;/span&gt;,
                style: TextStyle(color: Colors.grey.shade500, fontSize: &lt;span class="hljs-number"&gt;24&lt;/span&gt;)),
          ],
        ),
      );
    }
  }
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;


&lt;li&gt;

&lt;p&gt;&lt;code&gt;LoadingWidget&lt;/code&gt;: For showing Loading, We are using &lt;code&gt;shimmer&lt;/code&gt; widget.&lt;/p&gt;


&lt;/li&gt;


&lt;/ul&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/colors.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:shimmer/shimmer.dart'&lt;/span&gt;;

&lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;LoadingWidget&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;StatelessWidget&lt;/span&gt; &lt;/span&gt;{
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; LoadingWidget({Key? key}) : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(key: key);

  &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
  Widget build(BuildContext context) {
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; ListView.builder(
      itemBuilder: (c, i) =&amp;gt; Padding(
        padding: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EdgeInsets.symmetric(horizontal: &lt;span class="hljs-number"&gt;10&lt;/span&gt;, vertical: &lt;span class="hljs-number"&gt;5&lt;/span&gt;),
        child: Card(
          elevation: &lt;span class="hljs-number"&gt;5&lt;/span&gt;,
          surfaceTintColor: Colors.grey,
          color: Colors.grey.shade300,
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(&lt;span class="hljs-number"&gt;16&lt;/span&gt;)),
          child: Padding(
            padding: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EdgeInsets.all(&lt;span class="hljs-number"&gt;20&lt;/span&gt;),
            child: Stack(
              children: [
                Column(
                  children: [
                    Row(
                      children: [
                        Flexible(
                            child: shimmerBuilder(
                          &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; CircleAvatar(
                            radius: &lt;span class="hljs-number"&gt;40&lt;/span&gt;,
                            backgroundColor: Colors.grey,
                          ),
                        )),
                        &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                          width: &lt;span class="hljs-number"&gt;20&lt;/span&gt;,
                        ),
                        Flexible(
                          child: shimmerBuilder(
                            Container(
                              height: &lt;span class="hljs-number"&gt;20&lt;/span&gt;,
                              width: &lt;span class="hljs-number"&gt;150&lt;/span&gt;,
                              color: Colors.black,
                            ),
                          ),
                        )
                      ],
                    ),
                    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                      height: &lt;span class="hljs-number"&gt;20&lt;/span&gt;,
                    ),
                    Row(
                      children: [
                        shimmerBuilder(
                          &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; Icon(
                            Icons.favorite_border,
                            color: Colors.grey,
                          ),
                        ),
                        &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                          width: &lt;span class="hljs-number"&gt;10&lt;/span&gt;,
                        ),
                        shimmerBuilder(
                          Container(
                            height: &lt;span class="hljs-number"&gt;10&lt;/span&gt;,
                            width: &lt;span class="hljs-number"&gt;100&lt;/span&gt;,
                            color: Colors.black,
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
                Positioned(
                    right: &lt;span class="hljs-number"&gt;0&lt;/span&gt;,
                    bottom: &lt;span class="hljs-number"&gt;0&lt;/span&gt;,
                    child: shimmerBuilder(
                      Container(
                        width: &lt;span class="hljs-number"&gt;125&lt;/span&gt;,
                        padding: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EdgeInsets.symmetric(
                            horizontal: &lt;span class="hljs-number"&gt;20&lt;/span&gt;, vertical: &lt;span class="hljs-number"&gt;5&lt;/span&gt;),
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(&lt;span class="hljs-number"&gt;8&lt;/span&gt;),
                          border: Border.all(color: primaryColor),
                        ),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; [
                            Icon(
                              Icons.shopping_cart_outlined,
                              color: Colors.grey,
                            ),
                          ],
                        ),
                      ),
                    ))
              ],
            ),
          ),
        ),
      ),
      itemCount: &lt;span class="hljs-number"&gt;5&lt;/span&gt;,
    );
  }

  Shimmer shimmerBuilder(child) {
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Shimmer(
      gradient: LinearGradient(
        colors: [
          Colors.grey.shade300,
          Colors.grey.shade100,
          Colors.grey.shade50,
        ],
      ),
      child: child,
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="heading-card-widget"&gt;Card widget:&lt;/h2&gt;

&lt;p&gt;create a dart file named &lt;code&gt;product_card.dart&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:cached_network_image/cached_network_image.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/colors.dart'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/models/product_model.dart'&lt;/span&gt;;

&lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProductCard&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;StatelessWidget&lt;/span&gt; &lt;/span&gt;{
  &lt;span class="hljs-keyword"&gt;final&lt;/span&gt; ProductModel product;
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; ProductCard({Key? key, &lt;span class="hljs-keyword"&gt;required&lt;/span&gt; &lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.product}) : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(key: key);

  &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
  Widget build(BuildContext context) {
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Padding(
      padding: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EdgeInsets.symmetric(horizontal: &lt;span class="hljs-number"&gt;10&lt;/span&gt;, vertical: &lt;span class="hljs-number"&gt;5&lt;/span&gt;),
      child: Card(
        elevation: &lt;span class="hljs-number"&gt;5&lt;/span&gt;,
        surfaceTintColor: primaryColor,
        color: Colors.blueAccent,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(&lt;span class="hljs-number"&gt;16&lt;/span&gt;)),
        child: Padding(
          padding: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EdgeInsets.all(&lt;span class="hljs-number"&gt;20&lt;/span&gt;),
          child: Stack(
            &lt;span class="hljs-comment"&gt;//crossAxisAlignment: CrossAxisAlignment.start,&lt;/span&gt;
            children: [
              Column(
                children: [
                  Row(
                    children: [
                      Flexible(
                        child: CircleAvatar(
                          radius: &lt;span class="hljs-number"&gt;40&lt;/span&gt;,
                          backgroundColor: Colors.white,
                          child: ClipOval(
                            child: CachedNetworkImage(
                              width: &lt;span class="hljs-built_in"&gt;double&lt;/span&gt;.maxFinite,
                              height: &lt;span class="hljs-number"&gt;130.0&lt;/span&gt;,
                              fit: BoxFit.scaleDown,
                              imageUrl: product.image,
                              placeholder: (context, url) =&amp;gt; &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                                width: &lt;span class="hljs-number"&gt;10&lt;/span&gt;,
                                height: &lt;span class="hljs-number"&gt;10&lt;/span&gt;,
                              ),
                              errorWidget: (context, url, error) =&amp;gt; Image.asset(
                                &lt;span class="hljs-string"&gt;'assets/images/sinimagen.png'&lt;/span&gt;,
                                height: &lt;span class="hljs-number"&gt;30&lt;/span&gt;,
                                fit: BoxFit.cover,
                              ),
                            ),
                          ),
                        ),
                      ),
                      &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                        width: &lt;span class="hljs-number"&gt;20&lt;/span&gt;,
                      ),
                      Flexible(
                          child: Text(
                        product.title,
                        maxLines: &lt;span class="hljs-number"&gt;2&lt;/span&gt;,
                        style:
                            &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; TextStyle(color: Colors.white, fontSize: &lt;span class="hljs-number"&gt;14&lt;/span&gt;),
                      ))
                    ],
                  ),
                  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                    height: &lt;span class="hljs-number"&gt;20&lt;/span&gt;,
                  ),
                  Row(
                    children: [
                      &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; Icon(
                        Icons.favorite_border,
                        color: Colors.white,
                      ),
                      &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                        width: &lt;span class="hljs-number"&gt;10&lt;/span&gt;,
                      ),
                      Text(
                        product.rating.count.toString(),
                        maxLines: &lt;span class="hljs-number"&gt;2&lt;/span&gt;,
                        style:
                            &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; TextStyle(color: Colors.white, fontSize: &lt;span class="hljs-number"&gt;14&lt;/span&gt;),
                      ),
                    ],
                  ),
                ],
              ),
              Positioned(
                right: &lt;span class="hljs-number"&gt;0&lt;/span&gt;,
                bottom: &lt;span class="hljs-number"&gt;0&lt;/span&gt;,
                child: Container(
                  width: &lt;span class="hljs-number"&gt;125&lt;/span&gt;,
                  padding:
                      &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; EdgeInsets.symmetric(horizontal: &lt;span class="hljs-number"&gt;20&lt;/span&gt;, vertical: &lt;span class="hljs-number"&gt;5&lt;/span&gt;),
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(&lt;span class="hljs-number"&gt;8&lt;/span&gt;),
                    border: Border.all(color: primaryColor),
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text(product.price.toString()),
                      &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; SizedBox(
                        width: &lt;span class="hljs-number"&gt;10&lt;/span&gt;,
                      ),
                      &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; Icon(Icons.shopping_cart_outlined),
                    ],
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
&lt;li&gt;Add the following content in &lt;code&gt;main.dart&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter_bloc/flutter_bloc.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/presentation/blocs/products_bloc.dart'&lt;/span&gt;;
  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:pagination_in_flutter/presentation/pages/products_page.dart'&lt;/span&gt;;

  &lt;span class="hljs-keyword"&gt;void&lt;/span&gt; main() {
    runApp(&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; MyApp());
  }

  &lt;span class="hljs-class"&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;MyApp&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;StatelessWidget&lt;/span&gt; &lt;/span&gt;{
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; MyApp({Key? key}) : &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;(key: key);

    &lt;span class="hljs-meta"&gt;@override&lt;/span&gt;
    Widget build(BuildContext context) {
      &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; MultiBlocProvider(
          providers: [BlocProvider(create: (context) =&amp;gt; ProductsBloc())],
          child: MaterialApp(
            title: &lt;span class="hljs-string"&gt;'Pagination Example with Flutter'&lt;/span&gt;,
            debugShowCheckedModeBanner: &lt;span class="hljs-keyword"&gt;false&lt;/span&gt;,
            theme: ThemeData(useMaterial3: &lt;span class="hljs-keyword"&gt;true&lt;/span&gt;),
            home: &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; ProductsPage(),
          ));
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create &lt;code&gt;colors.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; &lt;span class="hljs-string"&gt;'package:flutter/material.dart'&lt;/span&gt;;

  Color primaryColor = Colors.blueAccent;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="heading-output"&gt;OUTPUT&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fetching Items when No Error Occurred&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682172979909%2Fdb2e3e03-defa-4004-be9a-f8fec8326b60.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682172979909%2Fdb2e3e03-defa-4004-be9a-f8fec8326b60.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fetching items when the error occurred while loading more:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682173085976%2F63abcd77-5489-47eb-9b68-d61ea1fc73ed.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1682173085976%2F63abcd77-5489-47eb-9b68-d61ea1fc73ed.gif" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="heading-hurry-we-successfully-implemented-pagination-in-flutter-using-blocs"&gt;Hurry we successfully implemented Pagination in Flutter using Blocs.&lt;/h3&gt;

&lt;h1 id="heading-source-code"&gt;Source code:&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Flutter Github Repo: &lt;a href="https://github.com/Djsmk123/flutter_pagination" rel="noopener noreferrer"&gt;https://github.com/Djsmk123/flutter_pagination&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Backend Repo: &lt;a href="https://github.com/Djsmk123/flutter_pagination_api" rel="noopener noreferrer"&gt;https://github.com/Djsmk123/flutter_pagination_api&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Follow me:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com//djsmk123" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dev.to: &lt;a href="https://dev.to/djsmk123/"&gt;https://dev.to/djsmk123/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>pagination</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Is your Flutter application Secured? Best Practices for Developing and Deploying Secure Flutter Apps</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Fri, 03 Mar 2023 14:41:57 +0000</pubDate>
      <link>https://dev.to/djsmk123/is-your-flutter-application-secured-best-practices-for-developing-and-deploying-secure-flutter-apps-4njm</link>
      <guid>https://dev.to/djsmk123/is-your-flutter-application-secured-best-practices-for-developing-and-deploying-secure-flutter-apps-4njm</guid>
      <description>&lt;p&gt;I have been working on flutter for more than 2 years and I have seen many flutter developers who are not aware of the security issues in a flutter. So I have decided to write this article to make flutter developers aware of the security issues in flutter and how can you make your application secure so let's start.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Securing API Keys
&lt;/h2&gt;

&lt;p&gt;API stands for "Application Programming Interface". An API is a set of rules, protocols, and tools that define how software components should interact with each other. In other words, it is a way for different software applications to communicate and exchange information with each other.&lt;br&gt;
we can secure API hard-coded either in dart or Native(Android, iOS, web)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API keys in Flutter code&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;for example, I have to use the API key in Dart code&lt;/p&gt;

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

void main(){
  String API_KEY="********"; //bad practice
}



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

&lt;/div&gt;

&lt;p&gt;for avoiding this, we can use &lt;a href="https://www.sandromaglione.com/techblog/how-to-use-environmental-variables-in-flutter" rel="noopener noreferrer"&gt;String.fromEnvironment()&lt;/a&gt; or &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;String.fromEnvironment()&lt;/code&gt; implementation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Load API in Dart code 
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;void main(){&lt;br&gt;
    String? apiKey = String.fromEnvironment('API_KEY', defaultValue: null); //get api key from environment&lt;br&gt;
    runApp(MyApp(apiKey: apiKey));&lt;br&gt;
  }&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- While compiling code pass the API key as an environment variable

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

&lt;/div&gt;



&lt;p&gt;flutter build apk --dart-define==API_KEY=SECRET_KEY&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
## Load API key From `.env` file

for fetching API keys from the `.env` file use the following package:

[flutter_dotenv](https://pub.dev/packages/flutter_dotenv)

&amp;gt; Note Do not forget to add the `.env` file in the `.gitignore` file

## API keys in Native code

for example, Google Map API needs to be added to Android, iOS, and the web in respective files but APIs are not secured in `AndroidManifest.xml`, `info.plist`, and `index.html`.

[Adding Google Map to flutter](https://codelabs.developers.google.com/codelabs/google-maps-in-flutter#3)

## Android: 
- Insert your API key in  `local.properties `file

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

&lt;/div&gt;



&lt;p&gt;google.maps_api_key=SECRET_KEY&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  - Add local.properties file in `.gitignore` file
  - Now read the API key from `local.properties` file in `build.gradle(app)` file.


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

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader -&amp;gt;
        localProperties.load(reader)
    }
}
def googleMapsApiKey = localProperties.getProperty("google.maps_api_key")
android {
    defaultConfig {
        manifestPlaceholders = [
                googleMapsApiKey: googleMapsApiKey
        ]
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- and add it to your AndroidManifest.xml file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="${googleMapsApiKey}" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## For iOS: 
- Add your API key in `ios/Flutter/Release.xcconfig` file

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

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; GOOGLE_MAPS_API_KEY=$(SECRET_KEY)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Same as debug.xcconfig file

- Now refer to these keys in `info.plist` file

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

&lt;/div&gt;



&lt;p&gt;google_maps_api_key&lt;br&gt;
    $(GOOGLE_MAPS_API_KEY)&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Now add it to your `AppDelegate.swift` file

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

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GMSServices.provideAPIKey("$(GOOGLE_MAPS_API_KEY)")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# For the web: 
it is a little bit complicated but you can refer to the following link:

[Dyanmic Html Elements- An approach to Flavours in Flutter Web ](https://medium.com/flutter-community/dynamic-html-elements-an-approach-to-flavors-in-flutter-web-6bff1a7d655a) by [gonpalma](https://twitter.com/gonpalma)

## Encrypt Information and Storage

Encryption is the process of converting plain text data into an encoded format to ensure the confidentiality, integrity, and security of the data. In Flutter, encryption is often used to secure sensitive data such as passwords, user credentials, and financial information in mobile applications.

Flutter provides built-in support for encryption through the `dart:crypto` library, which includes various cryptographic functions such as hashing, symmetric encryption, and asymmetric encryption.

![meme 2](https://images.squarespace-cdn.com/content/v1/5c9051237d0c9173e4e58a19/1614147238291-DA792M3SPZ60ZUDBWRHD/Encryption+meme.jpg?format=750w)

- Encryption local storage: Local storage is a place where we store the data locally on the device. So we should encrypt the data before storing it in the local storage. You can use the following packages to encrypt the data before storing it in the local storage.

  - [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage)
  - [sqflite](https://pub.dev/packages/sqflite)
  - [file_cryptor](https://pub.dev/packages/file_cryptor)

- Encryption Information: Sensitive data such as user credentials, payment information, and personal information should be encrypted both in transit and at rest. Flutter has built-in support for encryption through packages like [crypto](https://pub.dev/packages/crypto) and [encrypt](https://pub.dev/packages/encrypt). 


# Make Authentication Proof Your App
Authentication is the process of verifying the identity of a user. There are two types of authentication: client-side and server-side. Client-side authentication is done on the client side, and server-side authentication is done on the server side. Server-side authentication is more secure than client-side authentication. So we should use server-side authentication. You can use the following packages to implement server-side authentication as per your requirement.

- [firebase_auth](https://pub.dev/packages/firebase_auth)

- [oauth2](https://pub.dev/packages/oauth2)

- [jwt_decoder](https://pub.dev/packages/jwt_decoder)

As server-side authentication is more secure but it is not that local authentication is not secure. So we should use local authentication as well. For local authentication, you can use bio-metric, pins, or patterns.

![image 2](https://i.pinimg.com/736x/66/02/68/660268df52e603c798f1291f6b09110c.jpg)

local authentication is most used in banking applications or for any financial application.


## Network Calls: 

Network communication should be secured using `HTTPS` and `SSL/TLS` protocols to prevent data interception and tampering and to ensure data integrity. You can use the following packages to make network calls.

- [dio](https://pub.dev/packages/dio)
- [http](https://pub.dev/packages/http)

&amp;gt; Note: try to ignore making network calls from a host starting with http:// and try to make network calls from a host starting with https://

## is the Device Secure? 

Any Banking application or Financial Application needs to be installed only on a safe device. 

How can you check if a device is **Rooted** or **Jail Breaked**

You can use the following packages to check if the device is rooted or not.

- [flutter_root_detection](https://pub.dev/packages/flutter_root_detection)

- [flutter_jailbreak_detection](https://pub.dev/packages/flutter_jailbreak_detection)

## Can your App could be Reverse engineered?

![image 1](https://i.stack.imgur.com/ZeBe6.png)

yeah, it is possible to reverse-engineer the source code. So we should obfuscate the source code. see here if you distribute debug build then it is easy to reverse-engineer the source code. So we should distribute the release build. But still, it is possible to reverse engineer the source code. So we should obfuscate the source code.

- [Decompile flutter app](https://stackoverflow.com/questions/53666487/flutter-debug-apk-decompile-to-get-the-source-code)

To obfuscate the source code you can use the following command

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

&lt;/div&gt;



&lt;p&gt;flutter build apk --obfuscate --split-debug-info=debug_info&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
read more about obfuscation [here](https://flutter.dev/docs/deployment/android#obfuscate)

# Control Background snapshots:
The following package [secure_application](https://pub.dev/packages/secure_application), Secures app content visibility when users leave the app. It will hide content in the app switcher and display a frost barrier above locked content when the user gets backs. 


## Best Practices for Flutter App Development to make it more secure

- Avoid using third-party libraries: As we know flutter has a lot of packages to make our work easy but we should avoid using third-party libraries as much as possible. Because we don't know what is going on inside the package. So it is better to use only trusted packages.
before using any package we should check the package's popularity and the number of contributors. If the package has a lot of contributors and a lot of people are using it then it is safe to use it.


![example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xwg3meedc5djfwh1oq61.png)


- Overloading Permissions: overloading permissions is a technique to ask for more permissions than required. For example, if you want to access the camera then you should ask for camera permission only. But if you ask for storage permission also then it is called overloading permissions. So we should avoid overloading permissions as much as possible because it is a bad practice and users will not like it.

- Avoid global variables or methods: for example, if you want to load a token from local storage then you should not store it in a global variable. Because if you store it in a global variable then it is easy to access it from anywhere. So it is better to store it in a local variable.


- Perform security testing: Regularly perform security testing to identify potential vulnerabilities and ensure that security measures are effective. This can include penetration testing, code reviews, and vulnerability scanning.

- Add Bug Bounty Program: A bug bounty program is a way to encourage ethical hackers to find and report vulnerabilities in your application. This can be a great way to find vulnerabilities that you may have missed. You can also offer a reward for finding vulnerabilities. This can help you find vulnerabilities before they are exploited by malicious hackers.


- Use a Secure Coding Standard: A secure coding standard is a set of rules that developers must follow when writing code. This can help ensure that developers are writing secure code and that vulnerabilities are not introduced into the codebase. You can use a secure coding standard to ensure that developers are following best practices and that vulnerabilities are not introduced into the codebase.


- For Production release use Pipeline: For production release use pipeline. The pipeline is a set of steps that are performed to build and release the application. This can include building the application, running tests, and deploying the application. This can help ensure that the application is secure and that vulnerabilities are not introduced into the application.
Pipeline tools example GitHub actions, bitrise, circleci, Jenkins, etc.

GitHub action to build flutter app and distribute to using firebase app distribution read [here](https://dev.to/djsmk123/flutter-firebase-app-distribution-using-github-actions-1fbo).

If I forgot anything you can add it in the comment section, will add it later.


![meme 3](https://i.pinimg.com/736x/40/d7/24/40d7243dcec513224602860a72ff6b90--done-meme-friday-memes.jpg)

&amp;gt; Follow me:

- [GitHub](https://github.com//djsmk123)

- [LinkedIn](https://www.linkedin.com/in/md-mobin-bb928820b)

- [Twitter](https://twitter.com/smk_winner)




















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

&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>security</category>
      <category>dart</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Flutter Resources: Beginner to Advance</title>
      <dc:creator>Md. Mobin</dc:creator>
      <pubDate>Sun, 05 Feb 2023 09:38:32 +0000</pubDate>
      <link>https://dev.to/djsmk123/flutter-resources-beginner-to-advance-15on</link>
      <guid>https://dev.to/djsmk123/flutter-resources-beginner-to-advance-15on</guid>
      <description>&lt;h2&gt;
  
  
  Flutter Resources
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Future&amp;lt;void&amp;gt; main() await {
        final res=await getFlutterResources();
        print(res);
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://flutter.dev/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt; is a mobile app SDK for building high-performance, high-fidelity, apps for iOS and Android, from a single codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dart.dev/" rel="noopener noreferrer"&gt;Dart&lt;/a&gt; is a client-optimized language for fast apps on any platform.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Beginner
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Flutter Installation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=fDnqXmLSqtg" rel="noopener noreferrer"&gt;Android Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=0SRvmcsRu2w" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/playlist?list=PLlxmoA0rQ-LyHW9voBdNo4gEEIh0SjG-q" rel="noopener noreferrer"&gt;Dart Programming Basics&lt;/a&gt;: A playlist of 10 videos that will teach you the basics of Dart programming.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=1ukSR1GRtMU&amp;amp;list=PL4cUxeGkcC9jLYyp2Aoh6hcWuxFDX6PBJ" rel="noopener noreferrer"&gt;Flutter Programming Basics&lt;/a&gt;:  A youtube playlist of vidoes that will teach you the basics of Flutter programming.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade" rel="noopener noreferrer"&gt;Navigation&lt;/a&gt; : Learn how to navigate between pages in Flutter using the new navigation and routing system.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://suragch.medium.com/how-to-make-http-requests-in-flutter-d12e98ee1cef" rel="noopener noreferrer"&gt;Http Requests&lt;/a&gt; : Learn how to make http requests in Flutter.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Projects
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=K4P5DZ9TRns" rel="noopener noreferrer"&gt;Flutter Todo App&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/Djsmk123/BmiCalCSmk" rel="noopener noreferrer"&gt;Bmi Calculator&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=aJdIkRipgSk" rel="noopener noreferrer"&gt;Flutter Login UI&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/londonappbrewery/Clima-Flutter" rel="noopener noreferrer"&gt;Weather App&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Intermediate
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/olexale/flutter_roadmap" rel="noopener noreferrer"&gt;Flutter RoadMap&lt;/a&gt;: A roadmap to learn Flutter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://pub.dev/packages/firebase_core" rel="noopener noreferrer"&gt;Firebase Integration&lt;/a&gt; : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=fxDusoMcWj8" rel="noopener noreferrer"&gt;Setup firebase Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=rWamixHIKmQ" rel="noopener noreferrer"&gt;Firebase Auth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=ErP_xomHKTw" rel="noopener noreferrer"&gt;Firebase Firestore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=sM-WMcX66FI" rel="noopener noreferrer"&gt;Firebase Storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=UJexwPc_Mso" rel="noopener noreferrer"&gt;Firebase Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=54vgoPgB8xEg" rel="noopener noreferrer"&gt;Firebase Messaging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=31KpJXqCayo" rel="noopener noreferrer"&gt;Firebase Analytics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=DFcr0k2nrP4" rel="noopener noreferrer"&gt;Firebase Crashlytics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=kFwrb0z5ZxE" rel="noopener noreferrer"&gt;Firebase Remote Config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=AfX1OZ8xIoM" rel="noopener noreferrer"&gt;Firebase Dynamic Links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=m0d_pbgeeG8b" rel="noopener noreferrer"&gt;Firebase Admob&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.flutterdevs.com/explore-firebase-in-app-messaging-in-flutter-eaf219d208b1" rel="noopener noreferrer"&gt;Firebase In App Messaging&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://docs.flutter.dev/development/data-and-backend/state-mgmt/intro" rel="noopener noreferrer"&gt;State Management&lt;/a&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/provider" rel="noopener noreferrer"&gt;Provider&lt;/a&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=L_QMsE2v6dw" rel="noopener noreferrer"&gt;Counter app Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=kN9Yfd4fu04" rel="noopener noreferrer"&gt;Todo app Example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://pub.dev/packages/get" rel="noopener noreferrer"&gt;GetX&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=bccMj5yRvzI&amp;amp;list=PLyvMrTRoa-A5BBd1qOufZLniBIooWB_Y7" rel="noopener noreferrer"&gt;Complete Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://pub.dev/packages/mobx" rel="noopener noreferrer"&gt;MobX&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=7Od55PBxYkI" rel="noopener noreferrer"&gt;Complete Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://pub.dev/packages/velocity_x" rel="noopener noreferrer"&gt;VelocityX&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=IOhJCN4T1V4&amp;amp;list=PLR2qQy0Zxs_UHLXSYbK50jEapx0ORmLYv" rel="noopener noreferrer"&gt;Complete Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=GXIJJkq_H8g&amp;amp;list=PLjxrf2q8roU2v6UqYlt_KPaXlnjbYySua" rel="noopener noreferrer"&gt;Animation Intro&lt;/a&gt;: A youtube playlist of videos that will teach you the basics of Flutter animations.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://codesearchonline.com/index.php/flutter-implicit-animation/" rel="noopener noreferrer"&gt;Animation Cheat Sheet&lt;/a&gt;:A cheat sheet that will help you to find the right animation for your app.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter" rel="noopener noreferrer"&gt;Aws Amplify&lt;/a&gt; : Learn how to integrate AWS Amplify with your Flutter app.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG" rel="noopener noreferrer"&gt;New widget everyweek&lt;/a&gt;: A youtube playlist of videos that will teach you how to use new widgets in Flutter.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advanced
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade" rel="noopener noreferrer"&gt;Advanced Navigation&lt;/a&gt; : Learn how to navigate between pages in Flutter using the new navigation and routing system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://flutter.dev/docs/development/data-and-backend/json" rel="noopener noreferrer"&gt;Json serialization&lt;/a&gt; : Learn how to serialize and deserialize JSON in Flutter.&lt;br&gt;
-&lt;a href="https://www.youtube.com/watch?v=v5xGLrhzDGE" rel="noopener noreferrer"&gt;Json Serialization&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Advacned State Management&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/flutter_bloc" rel="noopener noreferrer"&gt;Bloc&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=oxeYeMHVLII&amp;amp;list=PLB6lc7nQ1n4jCBkrirvVGr5b8rC95VAQ5" rel="noopener noreferrer"&gt;Complete Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://pub.dev/packages/riverpod" rel="noopener noreferrer"&gt;Riverpod&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=grcgR9tUGiU" rel="noopener noreferrer"&gt;Complete Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://flutter.dev/docs/development/data-and-backend/state-mgmt/options" rel="noopener noreferrer"&gt;Flutter Architecture&lt;/a&gt; : Learn how to architect your Flutter app: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/flutterworld/flutter-mvvm-architecture-f8bed2521958" rel="noopener noreferrer"&gt;MVVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=W7J2vfOaCwU" rel="noopener noreferrer"&gt;MVC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/codex/mvp-architecture-pattern-in-flutter-b7bfe712a0ba" rel="noopener noreferrer"&gt;MVP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://thasaquino.medium.com/flutter-reactive-architecture-with-bloc-and-mvi-part-1-94bec725f2eb" rel="noopener noreferrer"&gt;MVI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://resocoder.com/flutter-clean-architecture-tdd/" rel="noopener noreferrer"&gt;TDD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://flutter.dev/docs/testing" rel="noopener noreferrer"&gt;Flutter Testing&lt;/a&gt; : Learn how to test your Flutter app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=CdI0z-xhZks" rel="noopener noreferrer"&gt;Unit Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=73SsK0n_s1o" rel="noopener noreferrer"&gt;Widget Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=WPEsnJgW99M" rel="noopener noreferrer"&gt;Integration Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=vVg9It7cOfY" rel="noopener noreferrer"&gt;Performance Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/documentation/e2e/latest/" rel="noopener noreferrer"&gt;End to End Testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://flutter.dev/docs/development/platform-integration/platform-channels" rel="noopener noreferrer"&gt;Platform Channels &amp;amp; View&lt;/a&gt;: Learn how to integrate native code with Flutter.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;a href="https://flutter.dev/docs/development/packages-and-plugins/developing-packages" rel="noopener noreferrer"&gt;Flutter Plugins&lt;/a&gt; : Learn how to create your own Flutter plugin:&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Packages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://pub.dev/flutter/packages" rel="noopener noreferrer"&gt;Flutter Packages&lt;/a&gt;: A list of Flutter packages that you can use in your app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/google_fonts" rel="noopener noreferrer"&gt;Google Fonts&lt;/a&gt;: A package that allows you to use Google fonts in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/font_awesome_flutter" rel="noopener noreferrer"&gt;Font Awesome&lt;/a&gt;: A package that allows you to use Font Awesome icons in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/flutter_secure_storage" rel="noopener noreferrer"&gt;Flutter Secure Storage&lt;/a&gt;: A package that allows you to store data securely in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/shared_preferences" rel="noopener noreferrer"&gt;Flutter persistant storage&lt;/a&gt;: A package that allows you to store data persistantly in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/dio" rel="noopener noreferrer"&gt;Dio&lt;/a&gt;: A package that allows you to make HTTP requests in your app (similar to axios).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/flutter_svg" rel="noopener noreferrer"&gt;Flutter SVG&lt;/a&gt;: A package that allows you to use SVG images in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/lottie" rel="noopener noreferrer"&gt;Flutter lotie&lt;/a&gt;: A package that allows you to use Lottie animations in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/flutter_local_notifications" rel="noopener noreferrer"&gt;Flutter local notifications&lt;/a&gt;: A package that allows you to show local notifications in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/webview_flutter" rel="noopener noreferrer"&gt;Flutter webview&lt;/a&gt;: A package that allows you to use webviews in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/firebase_core" rel="noopener noreferrer"&gt;Flutter firebase&lt;/a&gt;: A package that allows you to use Firebase in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/web_socket_channel" rel="noopener noreferrer"&gt;websocket&lt;/a&gt;: A package that allows you to use websockets in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/fluttertoast" rel="noopener noreferrer"&gt;fluttertoast&lt;/a&gt;: A package that allows you to show toast messages in your app.
|   - &lt;a href="https://pub.dev/packages/flutter_screenutil" rel="noopener noreferrer"&gt;screenutil&lt;/a&gt;: A package that allows you to use responsive design in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/image_picker" rel="noopener noreferrer"&gt;flutter image picker&lt;/a&gt;: A package that allows you to use image picker in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/device_info" rel="noopener noreferrer"&gt;flutter device info&lt;/a&gt;: A package that allows you to get device info in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/device_preview" rel="noopener noreferrer"&gt;flutter device preview&lt;/a&gt;: A package that allows you to preview your app on different 
devices.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/logger" rel="noopener noreferrer"&gt;logger&lt;/a&gt;: A package that allows you to log messages in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/url_launcher" rel="noopener noreferrer"&gt;url launcher&lt;/a&gt;: A package that allows you to launch URLs in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/flutter_localizations" rel="noopener noreferrer"&gt;flutter localizations&lt;/a&gt;: A package that allows you to use localizations in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/intl" rel="noopener noreferrer"&gt;flutter intl&lt;/a&gt;: A package that allows you to use internationalization in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/auto_route" rel="noopener noreferrer"&gt;auto route&lt;/a&gt;: A package that allows you to use auto routing in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/google_maps_flutter" rel="noopener noreferrer"&gt;google maps&lt;/a&gt;: A package that allows you to use google maps in your app.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/go_router" rel="noopener noreferrer"&gt;go_router&lt;/a&gt;: A package that allows you to use go router in your app.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Full stack Projects
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/djsmk123/meme_maker" rel="noopener noreferrer"&gt;Meme Generator&lt;/a&gt;: A meme generator app built with Flutter and Firebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/backslashflutter/group_chatapp_flutter_firebase" rel="noopener noreferrer"&gt;Flutter Chat&lt;/a&gt;: A chat app built with Flutter and Firebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=j-LOab_PzzU" rel="noopener noreferrer"&gt;Flutter Ecommerce&lt;/a&gt; : An ecommerce app built with Flutter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=mEPm9w5QlJM" rel="noopener noreferrer"&gt;Instagram Clone&lt;/a&gt;:  An Instagram clone app built with Flutter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=B8Sx7wGiY-s&amp;amp;list=PLlzmAWV2yTgCjoZNF3hLX3puYJir9vSQO" rel="noopener noreferrer"&gt;Reddit Clone&lt;/a&gt;: A Reddit clone app built with Flutter.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Complete Flutter Course
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/playlist?list=PL6yRaaP0WPkVtoeNIGqILtRAgd3h2CNpT" rel="noopener noreferrer"&gt;Flutter Course&lt;/a&gt;: A complete Flutter course by the &lt;a href="https://twitter.com/vandadnp" rel="noopener noreferrer"&gt;Vandad Nahavandipoor&lt;/a&gt; on Youtube.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.appbrewery.co/p/flutter-development-bootcamp-with-dart" rel="noopener noreferrer"&gt;Flutter Bootcamp with Dart&lt;/a&gt;: A complete Flutter course by the &lt;a href="https://twitter.com/yu_angela" rel="noopener noreferrer"&gt;Dr. Angela Yu&lt;/a&gt; on Udemy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.udemy.com/course/learn-flutter-dart-to-build-ios-android-apps/" rel="noopener noreferrer"&gt;Flutter &amp;amp; Dart - The Complete Guide&lt;/a&gt;: A complete Flutter course by the &lt;a href="https://twitter.com/maxedapps" rel="noopener noreferrer"&gt;Maximilian Schwarzmüller&lt;/a&gt; on Udemy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/Djsmk123/Flutter_Resources" rel="noopener noreferrer"&gt;Fork repo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow me on
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/smk_winner" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.instagram.com/smkwinner/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.github.com/djsmk123" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.linkedin.com/in/md-mobin-bb928820b/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/djsmk123"&gt;dev.to&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@djsmk123" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>discuss</category>
    </item>
  </channel>
</rss>
