<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ishanga Vidusha]]></title><description><![CDATA[Ishanga Vidusha]]></description><link>https://blog.ishangavidusha.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 00:12:19 GMT</lastBuildDate><atom:link href="https://blog.ishangavidusha.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[[Release] color_palette_plus v2.0.0 - Supercharged Material 3 Theme Generation 🎨]]></title><description><![CDATA[Hey Flutter devs! I'm excited to announce the release of version 2.0.0 of color_palette_plus, a comprehensive Flutter library for color palette generation and theme management. This major update brings powerful Material 3 theme generation capabilitie...]]></description><link>https://blog.ishangavidusha.com/release-colorpaletteplus-v200-supercharged-material-3-theme-generation</link><guid isPermaLink="true">https://blog.ishangavidusha.com/release-colorpaletteplus-v200-supercharged-material-3-theme-generation</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><dc:creator><![CDATA[Ishanga Vidusha]]></dc:creator><pubDate>Wed, 29 Jan 2025 12:59:13 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738155331013/93cs5Z4xr.jpeg?auto=format" alt class="image--center mx-auto" /></p>
<p>Hey Flutter devs! I'm excited to announce the release of version 2.0.0 of color_palette_plus, a comprehensive Flutter library for color palette generation and theme management. This major update brings powerful Material 3 theme generation capabilities and a more robust color handling system.</p>
<h2 id="heading-whats-new-in-200">What's New in 2.0.0?</h2>
<h3 id="heading-theme-generation">🎯 Theme Generation</h3>
<ul>
<li><p>New <code>ThemeGenerator</code> class for automatic theme generation following Material 3 guidelines</p>
</li>
<li><p>Support for both light and dark themes with <code>ThemePair</code></p>
</li>
<li><p>Advanced theme customization through <code>ThemeConfig</code></p>
</li>
<li><p>Comprehensive color role system aligned with Material 3 specifications</p>
</li>
</ul>
<h3 id="heading-color-harmonies">🌈 Color Harmonies</h3>
<ul>
<li><p>Monochromatic palettes for subtle, sophisticated looks</p>
</li>
<li><p>Analogous color schemes for harmonious, unified designs</p>
</li>
<li><p>Complementary colors for strong visual contrast</p>
</li>
<li><p>Customizable harmony configurations (steps, angles, etc.)</p>
</li>
</ul>
<h3 id="heading-improved-color-handling">⚡ Improved Color Handling</h3>
<ul>
<li><p>Enhanced color transformation algorithms</p>
</li>
<li><p>Better contrast ratios in generated shades</p>
</li>
<li><p>Optimized HSL adjustments for natural color variations</p>
</li>
<li><p>Type-safe and null-safe implementation</p>
</li>
</ul>
<h2 id="heading-features-overview">Features Overview</h2>
<pre><code class="lang-dart"><span class="hljs-comment">// Generate a complete theme</span>
<span class="hljs-keyword">final</span> theme = ThemeGenerator.generateTheme(
  baseColor,
  config: ThemeConfig(
    colorSchemeConfig: ColorSchemeConfig(
      harmonyType: HarmonyType.analogous,
      analogousAngle: <span class="hljs-number">30</span>,
      harmonySteps: <span class="hljs-number">5</span>,
    ),
  ),
);

<span class="hljs-comment">// Generate both light and dark themes</span>
<span class="hljs-keyword">final</span> themePair = ThemeGenerator.generateThemePair(baseColor);

<span class="hljs-comment">// Create color harmonies</span>
<span class="hljs-keyword">final</span> monochromaticColors = ColorPalettes.monochromatic(baseColor, steps: <span class="hljs-number">5</span>);
<span class="hljs-keyword">final</span> analogousColors = ColorPalettes.analogous(baseColor, angle: <span class="hljs-number">30</span>);
<span class="hljs-keyword">final</span> complementaryColors = ColorPalettes.complementary(baseColor);
</code></pre>
<h2 id="heading-try-it-out">Try It Out!</h2>
<p>Check out our <a target="_blank" href="https://ishangavidusha.github.io/color_palette_plus/">live demo</a> to see the library in action!</p>
<h3 id="heading-installation">Installation</h3>
<p>Add to your pubspec.yaml:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">color_palette_plus:</span> <span class="hljs-string">^2.0.0</span>
</code></pre>
<h2 id="heading-breaking-changes">Breaking Changes</h2>
<ul>
<li><p>Updated theme generation API to use new configuration classes</p>
</li>
<li><p>Revised color role system to match Material 3 specifications</p>
</li>
</ul>
<h2 id="heading-links">Links</h2>
<ul>
<li><p><a target="_blank" href="https://pub.dev/packages/color_palette_plus">Package on</a> <a target="_blank" href="http://pub.dev">pub.dev</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/ishangavidusha/color_palette_plus">GitHub Repository</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/ishangavidusha/color_palette_plus#readme">Documentation</a></p>
</li>
</ul>
<p>We'd love to hear your feedback and suggestions for future improvements! Feel free to open issues or contribute on GitHub. Happy coding! 🚀</p>
<p>#flutter #dart #material3 #flutterdev #design</p>
]]></content:encoded></item><item><title><![CDATA[Introducing Color Palette Plus: A Modern Color Generation Library for Flutter]]></title><description><![CDATA[Color management in Flutter applications can be challenging, especially when dealing with color variations, shades, and harmonies. Today, I'm excited to introduce Color Palette Plus, a modern Flutter library that makes color palette generation both s...]]></description><link>https://blog.ishangavidusha.com/introducing-color-palette-plus-a-modern-color-generation-library-for-flutter</link><guid isPermaLink="true">https://blog.ishangavidusha.com/introducing-color-palette-plus-a-modern-color-generation-library-for-flutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[color]]></category><category><![CDATA[Dart]]></category><category><![CDATA[pub.dev]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[mobile app development]]></category><category><![CDATA[Material Design]]></category><category><![CDATA[UI]]></category><dc:creator><![CDATA[Ishanga Vidusha]]></dc:creator><pubDate>Sat, 18 Jan 2025 07:55:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-CXTpmnUFPw/upload/d1192154447fc79119f5d4c395931e60.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Color management in Flutter applications can be challenging, especially when dealing with color variations, shades, and harmonies. Today, I'm excited to introduce <a target="_blank" href="https://pub.dev/packages/color_palette_plus"><strong>Color Palette Plus</strong></a>, a modern Flutter library that makes color palette generation both simple and efficient while adhering to Flutter's latest best practices.</p>
<h2 id="heading-why-another-color-library">Why Another Color Library?</h2>
<p>With Flutter's recent updates, many existing color manipulation libraries have become outdated, still using deprecated color APIs. Color Palette Plus was built from the ground up to:</p>
<ul>
<li><p>Use Flutter's modern color component accessors (<code>.r</code>, <code>.g</code>, <code>.b</code>, <code>.a</code>)</p>
</li>
<li><p>Properly handle color components in the 0.0-1.0 range</p>
</li>
<li><p>Provide efficient color transformations</p>
</li>
<li><p>Maintain type safety and null safety</p>
</li>
</ul>
<h2 id="heading-key-features">Key Features</h2>
<h3 id="heading-1-material-design-swatch-generation">1. Material Design Swatch Generation</h3>
<p>Generate complete Material Design color swatches from any base color:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:color_palette_plus/color_palette_plus.dart'</span>;

<span class="hljs-keyword">final</span> baseColor = Color(<span class="hljs-number">0xFF2196F3</span>); <span class="hljs-comment">// Blue</span>
<span class="hljs-keyword">final</span> materialSwatch = ColorPalette.generateSwatch(baseColor);

<span class="hljs-comment">// Access shades</span>
<span class="hljs-keyword">final</span> primaryColor = materialSwatch[<span class="hljs-number">500</span>];
<span class="hljs-keyword">final</span> lightVariant = materialSwatch[<span class="hljs-number">200</span>];
<span class="hljs-keyword">final</span> darkVariant = materialSwatch[<span class="hljs-number">700</span>];
</code></pre>
<h3 id="heading-2-color-harmonies">2. Color Harmonies</h3>
<p>Create harmonious color combinations with built-in generators:</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Monochromatic variations</span>
<span class="hljs-keyword">final</span> monochromaticColors = ColorPalettes.monochromatic(
  baseColor,
  steps: <span class="hljs-number">5</span>,
);

<span class="hljs-comment">// Analogous colors</span>
<span class="hljs-keyword">final</span> analogousColors = ColorPalettes.analogous(
  baseColor,
  steps: <span class="hljs-number">3</span>,
  angle: <span class="hljs-number">30</span>,
);

<span class="hljs-comment">// Complementary colors</span>
<span class="hljs-keyword">final</span> complementaryColors = ColorPalettes.complementary(baseColor);
</code></pre>
<h3 id="heading-3-flexible-shade-access">3. Flexible Shade Access</h3>
<p>Access color shades in multiple ways:</p>
<pre><code class="lang-dart"><span class="hljs-comment">// Get a specific shade</span>
<span class="hljs-keyword">final</span> shade = ColorPalette.getShade(baseColor, <span class="hljs-number">500</span>);

<span class="hljs-comment">// Get all shades as a map</span>
<span class="hljs-keyword">final</span> allShades = ColorPalette.getAllShades(baseColor);
</code></pre>
<h2 id="heading-real-world-usage-examples">Real-World Usage Examples</h2>
<h3 id="heading-1-dynamic-theme-generation">1. Dynamic Theme Generation</h3>
<p>Create dynamic themes based on a brand color:</p>
<pre><code class="lang-dart">MaterialApp(
  theme: ThemeData(
    primarySwatch: ColorPalette.generateSwatch(brandColor),
    <span class="hljs-comment">// Use specific shades for different elements</span>
    scaffoldBackgroundColor: ColorPalette.getShade(brandColor, <span class="hljs-number">50</span>),
    appBarTheme: AppBarTheme(
      backgroundColor: ColorPalette.getShade(brandColor, <span class="hljs-number">500</span>),
    ),
  ),
)
</code></pre>
<h3 id="heading-2-custom-color-schemes">2. Custom Color Schemes</h3>
<p>Generate custom color schemes for your UI:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomColorScheme</span> </span>{
  <span class="hljs-keyword">final</span> Color primary;
  <span class="hljs-keyword">final</span> Color secondary;
  <span class="hljs-keyword">final</span> Color accent;

  CustomColorScheme(Color baseColor) {
    <span class="hljs-keyword">final</span> swatch = ColorPalette.generateSwatch(baseColor);
    primary = swatch[<span class="hljs-number">500</span>]!;

    <span class="hljs-comment">// Generate analogous colors for variety</span>
    <span class="hljs-keyword">final</span> analogous = ColorPalettes.analogous(baseColor);
    secondary = analogous[<span class="hljs-number">1</span>];
    accent = analogous[<span class="hljs-number">2</span>];
  }
}
</code></pre>
<h3 id="heading-3-accessible-color-combinations">3. Accessible Color Combinations</h3>
<p>Create accessible color pairs:</p>
<pre><code class="lang-dart">Widget buildAccessibleText(Color backgroundColor) {
  <span class="hljs-comment">// Get contrasting text color based on background</span>
  <span class="hljs-keyword">final</span> textColor = backgroundColor.computeLuminance() &gt; <span class="hljs-number">0.5</span> 
    ? ColorPalette.getShade(backgroundColor, <span class="hljs-number">900</span>)
    : ColorPalette.getShade(backgroundColor, <span class="hljs-number">50</span>);

  <span class="hljs-keyword">return</span> Container(
    color: backgroundColor,
    child: Text(
      <span class="hljs-string">'Accessible Text'</span>,
      style: TextStyle(color: textColor),
    ),
  );
}
</code></pre>
<h2 id="heading-performance-tips">Performance Tips</h2>
<h3 id="heading-1-cache-generated-colors">1. Cache Generated Colors</h3>
<p>Instead of generating colors in build methods:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ColorProvider</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ChangeNotifier</span> </span>{
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> MaterialColor _primarySwatch;
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Color&gt; _analogousColors;

  ColorProvider(Color baseColor) {
    _primarySwatch = ColorPalette.generateSwatch(baseColor);
    _analogousColors = ColorPalettes.analogous(baseColor);
  }

  MaterialColor <span class="hljs-keyword">get</span> primarySwatch =&gt; _primarySwatch;
  <span class="hljs-built_in">List</span>&lt;Color&gt; <span class="hljs-keyword">get</span> analogousColors =&gt; _analogousColors;
}
</code></pre>
<h3 id="heading-2-avoid-repeated-generations">2. Avoid Repeated Generations</h3>
<p>Cache commonly used shades:</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThemeColors</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-built_in">Map</span>&lt;Color, MaterialColor&gt; _swatchCache = {};

  <span class="hljs-keyword">static</span> MaterialColor getSwatchFor(Color color) {
    <span class="hljs-keyword">return</span> _swatchCache.putIfAbsent(
      color,
      () =&gt; ColorPalette.generateSwatch(color),
    );
  }
}
</code></pre>
<h2 id="heading-best-practices">Best Practices</h2>
<ol>
<li><p><strong>Shade Selection</strong></p>
<ul>
<li><p>Use 500 for the primary color</p>
</li>
<li><p>Use 50-200 for backgrounds</p>
</li>
<li><p>Use 700-900 for text on light backgrounds</p>
</li>
<li><p>Use 50-100 for text on dark backgrounds</p>
</li>
</ul>
</li>
<li><p><strong>Color Harmony</strong></p>
<ul>
<li><p>Use monochromatic palettes for subtle variations</p>
</li>
<li><p>Use analogous colors for related UI elements</p>
</li>
<li><p>Use complementary colors for contrast and emphasis</p>
</li>
</ul>
</li>
<li><p><strong>Accessibility</strong></p>
<ul>
<li><p>Always test contrast ratios</p>
</li>
<li><p>Provide sufficient color contrast for text</p>
</li>
<li><p>Don't rely solely on color to convey information</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-getting-started">Getting Started</h2>
<ol>
<li>Add to your pubspec.yaml:</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">color_palette_plus:</span> <span class="hljs-string">^1.0.0</span>
</code></pre>
<ol start="2">
<li>Import the library:</li>
</ol>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:color_palette_plus/color_palette_plus.dart'</span>;
</code></pre>
<ol start="3">
<li>Start using it in your code!</li>
</ol>
<h2 id="heading-requirements">Requirements</h2>
<ul>
<li><p>Flutter ≥ 3.27.0</p>
</li>
<li><p>Dart ≥ 3.6.0</p>
</li>
</ul>
<h3 id="heading-pub-dev-link-colorpaletteplushttpspubdevpackagescolorpaletteplus">Pub Dev Link: <a target="_blank" href="https://pub.dev/packages/color_palette_plus">color_palette_plus</a></h3>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Color Palette Plus simplifies color management in Flutter while ensuring you're using modern, efficient practices. Whether you're building a theme system, creating a design system, or just need some color variations, Color Palette Plus has you covered.</p>
<p>Check out the <a target="_blank" href="https://github.com/ishangavidusha/color_palette_plus">GitHub repository</a> for more examples and documentation. We welcome contributions and feedback from the community!</p>
<h2 id="heading-whats-next">What's Next?</h2>
<p>We're planning to add more features in future releases:</p>
<ul>
<li><p>Color palette export/import</p>
</li>
<li><p>Additional color harmony algorithms</p>
</li>
<li><p>Color accessibility scoring</p>
</li>
<li><p>Color naming suggestions</p>
</li>
</ul>
<p>Stay tuned for updates, and don't forget to star the repository if you find it useful!</p>
<hr />
<p>Happy coding! 🎨 ✨</p>
]]></content:encoded></item><item><title><![CDATA[Flutter Authentication Flow with Go Router and Provider]]></title><description><![CDATA[Goal of this article
The goal of this article is to get a comprehensive understanding of the flutter declarative routing and the scenarios like login state, app initialization on startup, splash, onboarding, etc... 
Problems that I'm trying to solve
...]]></description><link>https://blog.ishangavidusha.com/flutter-authentication-flow-with-go-router-and-provider</link><guid isPermaLink="true">https://blog.ishangavidusha.com/flutter-authentication-flow-with-go-router-and-provider</guid><category><![CDATA[Flutter Examples]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[Ishanga Vidusha]]></dc:creator><pubDate>Wed, 22 Dec 2021 19:21:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1640200152742/R7CUNhd6Y.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-goal-of-this-article">Goal of this article</h2>
<p>The goal of this article is to get a comprehensive understanding of the flutter declarative routing and the scenarios like login state, app initialization on startup, splash, onboarding, etc... </p>
<h2 id="heading-problems-that-im-trying-to-solve">Problems that I'm trying to solve</h2>
<p>assume we have a flutter application that we have to handle those situations.</p>
<ul>
<li>We need to show the onboarding page when the user opens the app for the first time.</li>
<li>We need to execute some initialisation functions every time the app opens.</li>
<li>The application should be capable of kicking out the user anywhere in the application when the login state changes (log out).</li>
<li>And also app should be capable of handling the deep links regardless of login state.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639994698289/KrAZ9VFCM.webp" alt="omg.webp" /></p>
<p>I know, It looks like a lot. but stay with me I'm going to cover it all in this article</p>
<h2 id="heading-starting-the-project">Starting the project</h2>
<p>First of all, let's create the application. assuming that you have already installed flutter (I use the currently available latest version <strong>v2.8.0</strong>) Create a folder and inside it run this command,</p>
<pre><code class="lang-bash">flutter create --template app --overwrite.
</code></pre>
<p>I'm sure you are using vs-code or android studio for this so you don't have to create an app manually. if you do just ignore the above command and create the app from the IDE.</p>
<p>throughout the project, I'm going to run the application on the web. since all plugging, I'm using here is platform-independent you can use any platform you like it's totally up to you.</p>
<p>Okay, Let's add plugging to the <code>pubspec.yaml</code> under <code>dependencies</code></p>
<ul>
<li><a target="_blank" href="https://pub.dev/packages/provider">provider: ^6.0.1</a><blockquote>
<p>we will be using the provider package to do all the state management in our application.</p>
</blockquote>
</li>
<li><a target="_blank" href="https://gorouter.dev">go_router: ^2.5.5</a><blockquote>
<p>The purpose of the go_router package is to use declarative routes to reduce complexity, regardless of the platform you're targeting (mobile, web, desktop), handle deep and dynamic linking from Android, iOS and the web, along with several other navigation-related scenarios, while still (hopefully) providing an easy-to-use developer experience.</p>
</blockquote>
</li>
<li><a target="_blank" href="https://pub.dev/packages/shared_preferences">shared_preferences: ^2.0.11</a><blockquote>
<p>will be used to store some persistent data that we have kept in our application.</p>
</blockquote>
</li>
</ul>
<p>That's it, that's all the package we are going to use, run <code>flutter pub get</code> to get the packages.</p>
<pre><code class="lang-bash">flutter pub get
</code></pre>
<h2 id="heading-lets-start-the-coding">Let's start the coding</h2>
<p>Open the <code>main.dart</code> file and remove everything and create the main function like this.</p>
<pre><code class="lang-dart">Future&lt;<span class="hljs-keyword">void</span>&gt; main() <span class="hljs-keyword">async</span> {
  WidgetsFlutterBinding.ensureInitialized();
  <span class="hljs-keyword">final</span> SharedPreferences sharedPreferences = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
  runApp(MyApp(sharedPreferences: sharedPreferences));
}
</code></pre>
<p>then create the <code>MyApp</code> class by extending the <code>StatefulWidget</code> because we need <code>initState</code> inside this widget.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">final</span> SharedPreferences sharedPreferences;
  <span class="hljs-keyword">const</span> MyApp({
    Key? key,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.sharedPreferences,
  }) : <span class="hljs-keyword">super</span>(key: key);

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyAppState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyApp</span>&gt; </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Container(

    );
  }
}
</code></pre>
<p>Okay, then let's create some files for the classes,
take a close look and complete the appropriate files and folders for the next step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640005651355/GXCqdsslq.png" alt="flutter_01.PNG" /></p>
<h2 id="heading-route-utils">Route Utils</h2>
<p>Let's create <code>enum</code> class inside the <code>route_utils.dart</code> file that contain all the pages that our application has.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">enum</span> APP_PAGE {
  splash,
  login,
  home,
  error,
  onBoarding
}
</code></pre>
<p>Then we will add <code>extension</code> to the <code>APP_PAGE</code> for purpose convinent.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">extension</span> AppPageExtension <span class="hljs-keyword">on</span> APP_PAGE {
  <span class="hljs-built_in">String</span> <span class="hljs-keyword">get</span> toPath {
    <span class="hljs-keyword">switch</span> (<span class="hljs-keyword">this</span>) {
      <span class="hljs-keyword">case</span> APP_PAGE.home:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"/"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.login:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"/login"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.splash:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"/splash"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.error:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"/error"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.onBoarding:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"/start"</span>;
      <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"/"</span>;
    }
  }

  <span class="hljs-built_in">String</span> <span class="hljs-keyword">get</span> toName {
    <span class="hljs-keyword">switch</span> (<span class="hljs-keyword">this</span>) {
      <span class="hljs-keyword">case</span> APP_PAGE.home:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"HOME"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.login:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"LOGIN"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.splash:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"SPLASH"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.error:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"ERROR"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.onBoarding:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"START"</span>;
      <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"HOME"</span>;
    }
  }

  <span class="hljs-built_in">String</span> <span class="hljs-keyword">get</span> toTitle {
    <span class="hljs-keyword">switch</span> (<span class="hljs-keyword">this</span>) {
      <span class="hljs-keyword">case</span> APP_PAGE.home:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"My App"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.login:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"My App Log In"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.splash:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"My App Splash"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.error:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"My App Error"</span>;
      <span class="hljs-keyword">case</span> APP_PAGE.onBoarding:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Welcome to My App"</span>;
      <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"My App"</span>;
    }
  }
}
</code></pre>
<p>Next, we will create <code>StatelessWidget</code> inside every view file except <code>splash_page.dart</code> file, inside that create a <code>StatefulWidget</code>,</p>
<p>then, let's create the <code>AppService</code> class,</p>
<pre><code class="lang-dart"><span class="hljs-comment">// ignore: non_constant_identifier_names</span>
<span class="hljs-built_in">String</span> LOGIN_KEY = <span class="hljs-string">"5FD6G46SDF4GD64F1VG9SD68"</span>;
<span class="hljs-comment">// ignore: non_constant_identifier_names</span>
<span class="hljs-built_in">String</span> ONBOARD_KEY = <span class="hljs-string">"GD2G82CG9G82VDFGVD22DVG"</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppService</span> <span class="hljs-title">with</span> <span class="hljs-title">ChangeNotifier</span> </span>{
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> SharedPreferences sharedPreferences;
  <span class="hljs-keyword">final</span> StreamController&lt;<span class="hljs-built_in">bool</span>&gt; _loginStateChange = StreamController&lt;<span class="hljs-built_in">bool</span>&gt;.broadcast();
  <span class="hljs-built_in">bool</span> _loginState = <span class="hljs-keyword">false</span>;
  <span class="hljs-built_in">bool</span> _initialized = <span class="hljs-keyword">false</span>;
  <span class="hljs-built_in">bool</span> _onboarding = <span class="hljs-keyword">false</span>;

  AppService(<span class="hljs-keyword">this</span>.sharedPreferences);

  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">get</span> loginState =&gt; _loginState;
  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">get</span> initialized =&gt; _initialized;
  <span class="hljs-built_in">bool</span> <span class="hljs-keyword">get</span> onboarding =&gt; _onboarding;
  Stream&lt;<span class="hljs-built_in">bool</span>&gt; <span class="hljs-keyword">get</span> loginStateChange =&gt; _loginStateChange.stream;

  <span class="hljs-keyword">set</span> loginState(<span class="hljs-built_in">bool</span> state) {
    sharedPreferences.setBool(LOGIN_KEY, state);
    _loginState = state;
    _loginStateChange.add(state);
    notifyListeners();
  }

  <span class="hljs-keyword">set</span> initialized(<span class="hljs-built_in">bool</span> value) {
    _initialized = value;
    notifyListeners();
  }

  <span class="hljs-keyword">set</span> onboarding(<span class="hljs-built_in">bool</span> value) {
    sharedPreferences.setBool(ONBOARD_KEY, value);
    _onboarding = value;
    notifyListeners();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; onAppStart() <span class="hljs-keyword">async</span> {
    _onboarding = sharedPreferences.getBool(ONBOARD_KEY) ?? <span class="hljs-keyword">false</span>;
    _loginState = sharedPreferences.getBool(LOGIN_KEY) ?? <span class="hljs-keyword">false</span>;

    <span class="hljs-comment">// This is just to demonstrate the splash screen is working.</span>
    <span class="hljs-comment">// In real-life applications, it is not recommended to interrupt the user experience by doing such things.</span>
    <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">2</span>));

    _initialized = <span class="hljs-keyword">true</span>;
    notifyListeners();
  }
}
</code></pre>
<p>This is our <code>AppService</code> class, we create it with <code>ChangeNotifier</code> so we can listen for the state changes using <code>Provider</code>. </p>
<p><code>SharedPreferences</code> instance that we created earlier will use in this class. and there are several values to keep track of the different states of the application.</p>
<p>While the <code>initialized</code> value only lives through the app life circle, <code>loginState</code> and <code>onboarding</code> values will be stored in local storage, because those values need to be kept even if the app is entirely closed. the <code>onAppStart</code> method is responsible for reading those values from local storage and finishing the app initialization. we will be showing the <code>Splash</code> screen until the <code>onAppStart</code> future is completed.</p>
<p>Ok, now we can define the routers, I'm going to do it by using Go Router if you're not familiar with its basic concepts first of all you should take a look at its <a target="_blank" href="https://gorouter.dev">documentation</a>.</p>
<h2 id="heading-lets-start-routing">Let's start routing</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640024846341/eN_eU20fs.webp" alt="hooray.webp" /></p>
<p>we need to create the <code>AppRouter</code> class which contains all the routing information in our application.</p>
<pre><code class="lang-dart"> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppRouter</span> </span>{
  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> AppService appService;
  GoRouter <span class="hljs-keyword">get</span> router =&gt; _goRouter;

  AppRouter(<span class="hljs-keyword">this</span>.appService);

  <span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> GoRouter _goRouter = GoRouter(
    refreshListenable: appService,
    initialLocation: APP_PAGE.home.toPath,
    routes: &lt;GoRoute&gt;[
      GoRoute(
        path: APP_PAGE.home.toPath,
        name: APP_PAGE.home.toName,
        builder: (context, state) =&gt; <span class="hljs-keyword">const</span> HomePage(),
      ),
      GoRoute(
        path: APP_PAGE.splash.toPath,
        name: APP_PAGE.splash.toName,
        builder: (context, state) =&gt; <span class="hljs-keyword">const</span> SplashPage(),
      ),
      GoRoute(
        path: APP_PAGE.login.toPath,
        name: APP_PAGE.login.toName,
        builder: (context, state) =&gt; <span class="hljs-keyword">const</span> LogInPage(),
      ),
      GoRoute(
        path: APP_PAGE.onBoarding.toPath,
        name: APP_PAGE.onBoarding.toName,
        builder: (context, state) =&gt; <span class="hljs-keyword">const</span> OnBoardingPage(),
      ),
      GoRoute(
        path: APP_PAGE.error.toPath,
        name: APP_PAGE.error.toName,
        builder: (context, state) =&gt; ErrorPage(error: state.extra.toString()),
      ),
    ],
    errorBuilder: (context, state) =&gt; ErrorPage(error: state.error.toString()),
    redirect: (state) {
      <span class="hljs-keyword">final</span> loginLocation = state.namedLocation(APP_PAGE.login.toPath);
      <span class="hljs-keyword">final</span> homeLocation = state.namedLocation(APP_PAGE.home.toPath);
      <span class="hljs-keyword">final</span> splashLocation = state.namedLocation(APP_PAGE.splash.toPath);
      <span class="hljs-keyword">final</span> onboardLocation = state.namedLocation(APP_PAGE.onBoarding.toPath);

      <span class="hljs-keyword">final</span> isLogedIn = appService.loginState;
      <span class="hljs-keyword">final</span> isInitialized = appService.initialized;
      <span class="hljs-keyword">final</span> isOnboarded = appService.onboarding;

      <span class="hljs-keyword">final</span> isGoingToLogin = state.subloc == loginLocation;
      <span class="hljs-keyword">final</span> isGoingToInit = state.subloc == splashLocation;
      <span class="hljs-keyword">final</span> isGoingToOnboard = state.subloc == onboardLocation;

      <span class="hljs-comment">// If not Initialized and not going to Initialized redirect to Splash</span>
      <span class="hljs-keyword">if</span> (!isInitialized &amp;&amp; !isGoingToInit) {
        <span class="hljs-keyword">return</span> splashLocation;
      <span class="hljs-comment">// If not onboard and not going to onboard redirect to OnBoarding</span>
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (isInitialized &amp;&amp; !isOnboarded &amp;&amp; !isGoingToOnboard) {
        <span class="hljs-keyword">return</span> onboardLocation;
      <span class="hljs-comment">// If not logedin and not going to login redirect to Login</span>
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (isInitialized &amp;&amp; isOnboarded &amp;&amp; !isLogedIn &amp;&amp; !isGoingToLogin) {
        <span class="hljs-keyword">return</span> loginLocation;
      <span class="hljs-comment">// If all the scenarios are cleared but still going to any of that screen redirect to Home</span>
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((isLogedIn &amp;&amp; isGoingToLogin) || (isInitialized &amp;&amp; isGoingToInit) || (isOnboarded &amp;&amp; isGoingToOnboard)) {
        <span class="hljs-keyword">return</span> homeLocation;
      } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Else Don't do anything</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
      }
    },
  );
</code></pre>
<p>Whoa, whoa what the fudge is going on here?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640029871144/JUPaQWCJF.webp" alt="lay-down.webp" /></p>
<p>It looks like too much information to take but don't worry I'm going to break down it for you.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">late</span> <span class="hljs-keyword">final</span> AppService appService;
GoRouter <span class="hljs-keyword">get</span> router =&gt; _goRouter;
</code></pre>
<blockquote>
<p>Here we define the <code>AppService</code> object and the <code>GoRouter</code> object, Since the <code>AppService</code> is listenable we can listen for the state changes and rebuild the router delegate according to the app states. </p>
</blockquote>
<pre><code class="lang-dart">refreshListenable: appService,
initialLocation: APP_PAGE.home.toPath,
</code></pre>
<blockquote>
<p><code>refreshListenable</code> is that we define a listenable class to go router to react to changes. and the <code>initialLocation</code> is the default location that the app will redirect when there is no other path when the app starts (like deep links and dynamic links).</p>
</blockquote>
<pre><code class="lang-dart">GoRoute(
  path: APP_PAGE.home.toPath,
  name: APP_PAGE.home.toName,
  builder: (context, state) =&gt; <span class="hljs-keyword">const</span> HomePage(),
),
</code></pre>
<blockquote>
<p>Here are all the predefined locations in our application. you can see that earlier we create <code>APP_PAGE</code> enum class and an extension for it. it is super convenient to define the path, and name when we have such a class that's why we created it.</p>
<p>If you take a closer look you will see we use <code>ErrorPage</code> on two different locations <code>routers</code> and <code>errorBuilder</code>. one inside the <code>routers</code>, we will use for manually navigate to the error page when there is a critical error in our application. the <code>errorBuilder</code> will use by Go Router to throw exceptions when there is something like an undefined navigation path.</p>
<p>one thing that I forgot to mention, there is a String argument on the error page called <code>error</code> which we will use to show an error to the user. when <code>errorBuilder</code> is triggered, always exception will be stored in the <code>state.error</code> object, when we navigate to the error page manually we will carry the error string inside the <code>state.extra</code> object. more information about it <a target="_blank" href="https://gorouter.dev/parameters#extra-parameter">here</a>.</p>
</blockquote>
<h2 id="heading-redirects">Redirects</h2>
<p>This method is the most important part of our application. it will handle all the core routing logic of the application, let's break down the block by block for better understanding.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> loginLocation = state.namedLocation(APP_PAGE.login.toName);
<span class="hljs-keyword">final</span> homeLocation = state.namedLocation(APP_PAGE.home.toName);
<span class="hljs-keyword">final</span> splashLocation = state.namedLocation(APP_PAGE.splash.toName);
<span class="hljs-keyword">final</span> onboardLocation = state.namedLocation(APP_PAGE.onBoarding.toName);
</code></pre>
<blockquote>
<p>Here we get all the locations from the router so we know where to redirect in different scenarios. </p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> isLogedIn = appService.loginState;
<span class="hljs-keyword">final</span> isInitialized = appService.initialized;
<span class="hljs-keyword">final</span> isOnboarded = appService.onboarding;
</code></pre>
<blockquote>
<p>Then we get all the current states in our application from <code>appService</code> so we know what to do next when we redirect.</p>
</blockquote>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> isGoingToLogin = state.subloc == loginLocation;
<span class="hljs-keyword">final</span> isGoingToInit = state.subloc == splashLocation;
<span class="hljs-keyword">final</span> isGoingToOnboard = state.subloc == onboardLocation;
</code></pre>
<blockquote>
<p>Finally we get where we actually go so we know when to redirect or not.</p>
</blockquote>
<p>See it's not that complicated</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640034664217/afbmDszO4.webp" alt="complicated.webp" /></p>
<p>Ok, ok, the next part is quite complicated, let's break it down,</p>
<p>We know that there are two cases where the redirect method called</p>
<ul>
<li>When we navigate to another location.</li>
<li>When <code>appService</code> object changes.<blockquote>
<p>because the router is always listing for its changes when it happens router will be rebuilt.</p>
</blockquote>
</li>
</ul>
<p>assume the app is starting for the first time, the sub-location of the router will be <code>initialLocation</code> which is home <code>/</code>, so this is the first case that I mentioned above the redirect method will be called, after collecting all the information we are in the <code>if</code> statement. so it will check app is <code>initialized</code> or not, obviously it is false. because we are stating the app and also we are not going to splash screen. so the statement will be valid we will return  <strong>Splash Screen Location</strong> <code>/splash</code>. when we do that unintentionally also triggered the <strong>first case</strong> because the navigator location has changed. so the redirect method will be called again but this time <code>isInitialized</code> is still false but <code>isGoingToInit</code> is true because we are going to splash the screen. so along with the if statement all other conditions will be false. we just let go of the navigator to do its thing without changing anything. so at this point,</p>
<blockquote>
<p>there is a three-state value of this application, and any of that is not changed yet.</p>
<p>app is just going to the <strong>splash screen</strong></p>
<p>and already redirect method is called <strong>3 times</strong></p>
</blockquote>
<p>those are the main three-point I need you to understand here. if you didn't please repeat the above paragraph again until you do.</p>
<p>before we start the splash screen we have to do one thing,</p>
<h2 id="heading-adding-provider-and-router-to-the-app">Adding Provider and Router to the App</h2>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyAppState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyApp</span>&gt; </span>{
  <span class="hljs-keyword">late</span> AppService appService;

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MultiProvider(
      providers: [
        ChangeNotifierProvider&lt;AppService&gt;(create: (_) =&gt; appService),
        Provider&lt;AppRouter&gt;(create: (_) =&gt; AppRouter(appService)),
      ],
      child: Builder(
        builder: (context) {
          <span class="hljs-keyword">final</span> GoRouter goRouter = Provider.of&lt;AppRouter&gt;(context, listen: <span class="hljs-keyword">false</span>).router;
          <span class="hljs-keyword">return</span> MaterialApp.router(
            title: <span class="hljs-string">"Router App"</span>,
            routeInformationParser: goRouter.routeInformationParser,
            routerDelegate: goRouter.routerDelegate,
          );
        },
      ),
    );
  }
}
</code></pre>
<p>You can see in the above code we have created <code>appService</code> object and initialized it inside <code>initState</code> function with <code>sharedPreferences</code> instance. After that we have to wrap <code>MaterialApp</code> with <code>MultiProvider</code> and then add <code>appService</code> as <code>ChangeNotifierProvider</code> also we will declare <code>AppRouter</code> with <code>appService</code> as a provider too. finally we can add <code>routeInformationParser</code> and <code>routerDelegate</code> to the <code>MaterialApp.router</code> by getting it from <code>context</code>. We have not created <code>AuthService</code> yet, it will be added later.</p>
<p>Okay, back to the splash screen</p>
<h2 id="heading-splash-screen">Splash Screen</h2>
<p>There is one thing we have to do in the splash screen which is we need to call the <code>onAppStart</code> function.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SplashPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">const</span> SplashPage({ Key? key }) : <span class="hljs-keyword">super</span>(key: key);

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

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_SplashPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">SplashPage</span>&gt; </span>{
  <span class="hljs-keyword">late</span> AppService _appService;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    _appService = Provider.of&lt;AppService&gt;(context, listen: <span class="hljs-keyword">false</span>);
    onStartUp();
    <span class="hljs-keyword">super</span>.initState();
  }

  <span class="hljs-keyword">void</span> onStartUp() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">await</span> _appService.onAppStart();
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(APP_PAGE.splash.toTitle),
      ),
      body: <span class="hljs-keyword">const</span> Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}
</code></pre>
<p>once we are on the splash screen <code>onAppStart</code> function will be triggered, so it will read <code>_onboarding</code> and <code>_loginState</code> values from the locale storage and will change <code>_initialized</code> to <code>true</code> then will call <code>notifyListeners</code> as soon as that happens, the router will be <strong>re</strong>built and will <strong>re</strong>call the <code>redirect</code> function.</p>
<p>oh god, that's too much <strong>"re"</strong> 😂</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640189327132/8zxVWiZtP.gif" alt="facepalm-seriously.gif" /></p>
<p>Okay, back to the <strong>redirect</strong> function</p>
<p>At this point <code>isInitialized</code> is true and other values will be false so obviously, it will redirect to the <code>onboardLocation</code> and inside the <code>OnBoardingPage</code> user will press the <code>Done</code> button and we will change <code>_onboarding</code> to true, it pretty much the same thing I think you can now understand the <code>redirect</code> function logics. Let's move on to the <code>AuthService</code></p>
<h2 id="heading-authentication-service">Authentication Service</h2>
<p>The reason why we are creating a separate <code>AuthService</code> class while tricking the login state inside the <code>AppService</code>, it will be so clean and understandable to implement authentication logic in a separate class and just expose the login state by an event stream. if you have experience with FirebaseAuth you will easily understand what I'm doing here,</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthService</span> </span>{
  <span class="hljs-keyword">final</span> StreamController&lt;<span class="hljs-built_in">bool</span>&gt; _onAuthStateChange = StreamController.broadcast();

  Stream&lt;<span class="hljs-built_in">bool</span>&gt; <span class="hljs-keyword">get</span> onAuthStateChange =&gt; _onAuthStateChange.stream;

  Future&lt;<span class="hljs-built_in">bool</span>&gt; login() <span class="hljs-keyword">async</span> {

    <span class="hljs-comment">// This is just to demonstrate the login process time.</span>
    <span class="hljs-comment">// In real-life applications, it is not recommended to interrupt the user experience by doing such things.</span>
    <span class="hljs-keyword">await</span> Future.delayed(<span class="hljs-keyword">const</span> <span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">1</span>));

    _onAuthStateChange.add(<span class="hljs-keyword">true</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
  }

  <span class="hljs-keyword">void</span> logOut() {
    _onAuthStateChange.add(<span class="hljs-keyword">false</span>);
  }
}
</code></pre>
<p>ok, now it's time to update the <code>main.dart</code> with <code>AuthService</code> we will declare it in the <code>initState</code> and add a listener to change the <code>loginState</code>.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_MyAppState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyApp</span>&gt; </span>{
  <span class="hljs-keyword">late</span> AppService appService;
  <span class="hljs-keyword">late</span> AuthService authService;
  <span class="hljs-keyword">late</span> StreamSubscription&lt;<span class="hljs-built_in">bool</span>&gt; authSubscription;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    appService = AppService(widget.sharedPreferences);
    authService = AuthService();
    authSubscription = authService.onAuthStateChange.listen(onAuthStateChange);
    <span class="hljs-keyword">super</span>.initState();
  }

  <span class="hljs-keyword">void</span> onAuthStateChange(<span class="hljs-built_in">bool</span> login) {
    appService.loginState = login;
  }

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

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MultiProvider(
      providers: [
        ChangeNotifierProvider&lt;AppService&gt;(create: (_) =&gt; appService),
        Provider&lt;AppRouter&gt;(create: (_) =&gt; AppRouter(appService)),
        Provider&lt;AuthService&gt;(create: (_) =&gt; authService),
      ],
      child: Builder(
        builder: (context) {
          <span class="hljs-keyword">final</span> GoRouter goRouter = Provider.of&lt;AppRouter&gt;(context, listen: <span class="hljs-keyword">false</span>).router;
          <span class="hljs-keyword">return</span> MaterialApp.router(
            title: <span class="hljs-string">"Router App"</span>,
            routeInformationParser: goRouter.routeInformationParser,
            routerDelegate: goRouter.routerDelegate,
          );
        },
      ),
    );
  }
}
</code></pre>
<p>Finally, we have finished almost all the coding now,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640193876165/s07zsjfoT.gif" alt="tired-sleepy.gif" /></p>
<p>we just need to add some buttons and run the application. I'll just add the home page code here the full source code will be available on GitHub.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomePage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-keyword">const</span> HomePage({ Key? key }) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">final</span> authService = Provider.of&lt;AuthService&gt;(context);
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(APP_PAGE.home.toTitle),
      ),
      body: Center(
        child: Column(
          children: [
            TextButton(
              onPressed: () {
                authService.logOut();
              },
              child: <span class="hljs-keyword">const</span> Text(
                <span class="hljs-string">"Log out"</span>
              ),
            ),
            TextButton(
              onPressed: () {
                GoRouter.of(context).goNamed(APP_PAGE.error.toName, extra: <span class="hljs-string">"Erro from Home"</span>);
              },
              child: <span class="hljs-keyword">const</span> Text(
                <span class="hljs-string">"Show Error"</span>
              ),
            ),
          ],
        ),
      ),
    );
  }
}
</code></pre>
<p>Okay, let's run the application,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640197034023/3wcsHcIZR.gif" alt="2021-12-22 23-37-14.gif" /></p>
<p>You will clearly see everything is working as expected.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640195701502/Vpke2_6lq.webp" alt="five.webp" /></p>
<p>Okay, I'm going to wrap up the article here. since it is already too long, Handling <strong>Deep Link</strong> will be discussed in a separate article later. The link will be updated.</p>
<p>I know this is not the most advanced way to do it. But I think it is good, at what it is capable of. Feel free to share your thoughts in the comment section. Don't hesitate to ask questions I will help you. Don't forget to like, and share the article with your Flutter friends.</p>
<p><a target="_blank" href="https://github.com/ishangavidusha/flutter-go-router-example">Github Repository</a></p>
<p>Happy coding.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1640200831163/mm_iJq9ql.webp" alt="byeeee.webp" /></p>
]]></content:encoded></item><item><title><![CDATA[Create OTP/Mobile verification API with python FastAPI and Send.lk SMS Gateway]]></title><description><![CDATA[I was recently trying to implement Mobile number verification on one of my projects. But after a ton of research, I couldn't find any resource or useable service that gives the facilities to do it locally. That makes me think...

Yes, you can and her...]]></description><link>https://blog.ishangavidusha.com/create-otpmobile-verification-api-with-python-fastapi-and-sendlk-sms-gateway</link><guid isPermaLink="true">https://blog.ishangavidusha.com/create-otpmobile-verification-api-with-python-fastapi-and-sendlk-sms-gateway</guid><category><![CDATA[Python]]></category><dc:creator><![CDATA[Ishanga Vidusha]]></dc:creator><pubDate>Sun, 19 Dec 2021 19:52:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1639943371566/F5x3B_hGv.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was recently trying to implement Mobile number verification on one of my projects. But after a ton of research, I couldn't find any resource or useable service that gives the facilities to do it locally. That makes me think...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639916239083/IFy_eWtNB.gif" alt="giphy-downsized-large.gif" />
<em>Yes, you can and here is how we are going to do it.</em></p>
<p> So then I decide to do it by myself creating a separate API for the Mobile verification.</p>
<h2 id="heading-creating-the-project">Creating the project</h2>
<p>First of all, let's create a FastAPI project. I use vs-code for the development. create a folder for the project and inside it create the main python file <code>main.py</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639917105711/ziEXQnAxh.png" alt="image_2021-12-19_180143.png" /></p>
<p>then let's create a virtual environment for the project.  I will use <code>venv</code>, you should have to have python installed in your system I use <code>python 3.10</code> the latest version currently available. </p>
<p>Open a new terminal inside project-root and run this command.</p>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<p>It will create a folder <code>venv</code> in the project root. after that, we can activate the virtual environment using the below command accordingly to your system.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># bash (Unix or MacOS)</span>
<span class="hljs-built_in">source</span> venv/bin/activate

<span class="hljs-comment"># PowerShell (Windows)</span>
venv\Scripts\Activate.ps1
</code></pre>
<p>If you use vs-code with python extension installed, as soon as you create the environment you will be asked to activate it automatically. Just press the <code>YES</code> and then kill and recreate the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639918128827/SxY5UKoWv.png" alt="fastapi-02.PNG" /></p>
<p>To continue we need an SMS gateway, after trying several locally provided services I decided to go with <a target="_blank" href="https://send.lk">send.lk</a> they give you free 30 credits on sign up. <em>(Not sponsored)</em> I'm not going to explain it you can register from their website and get an API key.</p>
<p>Moving on, let's install the necessary packages to start coding.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639919416617/lHxLitYY6.webp" alt="giphy.webp" /></p>
<pre><code class="lang-bash">pip install fastapi python-dotenv sendlk uvicorn
</code></pre>
<p>to keep a track of necessary packages let's create a <code>requirements.txt</code> file too.</p>
<pre><code class="lang-bash">pip freeze &gt; requirements.txt
</code></pre>
<h2 id="heading-start-the-project-coding">Start the project coding</h2>
<p>Inside the <code>main.py</code> file let's import the FastAPI and create the app.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI

<span class="hljs-comment"># Create the app</span>
app: FastAPI = FastAPI(
    title=<span class="hljs-string">"FastAPI Mobile Verification"</span>,
    version=<span class="hljs-string">"0.1.0"</span>,
)

<span class="hljs-comment"># App Root</span>
<span class="hljs-meta">@app.get("/", name="root")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">f"Welcome to Mobile Verification API <span class="hljs-subst">{app.version}</span>"</span>}
</code></pre>
<p>Done, Now we can start the debug server for the real-time preview of the API. Run the below command inside the venv activated terminal in the project root.</p>
<pre><code class="lang-bash">uvicorn --port 8000 --reload main:app
</code></pre>
<p>you will see that debug server started in port 8000, we use <code>--reload</code> flag to reload the server on file changes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639921448025/XUbVG9wjt.png" alt="fastapi-03.PNG" /></p>
<p>once again if you are using vs-code you can create the debug configuration by creating a <code>launch.json</code> file inside the <code>project-root/.vscode</code> folder with the following content.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.2.0"</span>,
    <span class="hljs-attr">"configurations"</span>: [
        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Python: FastAPI"</span>,
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"python"</span>,
            <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
            <span class="hljs-attr">"module"</span>: <span class="hljs-string">"uvicorn"</span>,
            <span class="hljs-attr">"args"</span>: [
                <span class="hljs-string">"--port"</span>,
                <span class="hljs-string">"8000"</span>,
                <span class="hljs-string">"--reload"</span>,
                <span class="hljs-string">"main:app"</span>
            ],
            <span class="hljs-attr">"jinja"</span>: <span class="hljs-literal">true</span>
        }
    ]
}
</code></pre>
<p>After stating the debug server if you go to http://localhost:8000 you will see that the API is working and we are getting the content of the root path.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639922281020/NuDu6NoVk.webp" alt="tada.webp" /></p>
<p><a target="_blank" href="http://127.0.0.1:8080/">localhost:8000</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639922424395/pda8zK-jA.png" alt="fastapi-04.PNG" /></p>
<p>hope you are not getting any errors or problems running the API 🤞</p>
<h2 id="heading-add-environment-variables">Add Environment variables</h2>
<p>Ok, let's add the Sendlk token and sender id to the <code>.env</code> file so that we can keep it private if we ever push this to a public repository. also, we will add a secret string to encrypt the OTP token.</p>
<pre><code><span class="hljs-comment"># Send lk token</span>
<span class="hljs-attr">SENDLK_TOKEN</span>=<span class="hljs-string">"sendlk-token"</span>
<span class="hljs-attr">SENDER_ID</span>=<span class="hljs-string">"sender-id"</span>
<span class="hljs-attr">SECRET</span>=<span class="hljs-string">"my-super-secret"</span>
</code></pre><p>and add these two lines to the top of the <code>main.py</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-comment"># Load the .env file</span>
load_dotenv(<span class="hljs-string">".env"</span>)
</code></pre>
<p>also, add the <code>.gitignore</code> file to the project root with the following content</p>
<pre><code><span class="hljs-comment">### dotenv ###</span>
.env
venv
.idea

<span class="hljs-comment">### Python ###</span>
<span class="hljs-comment"># Byte-compiled / optimized / DLL files</span>
__pycache__/
*.py[cod]
*$py.<span class="hljs-keyword">class</span>
</code></pre><p><em>restart the server</em></p>
<p>I know the article is getting long but I'm going to finish this with one article. stay with me,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639925221305/cZTI_H1ew.webp" alt="you-got-this.webp" /></p>
<p>to implement the feature first we will create folder <code>src</code>, inside that create three files <code>controller.py</code>, <code>schema.py</code> and <code>route.py</code>. like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639937166509/Mtrvh5k-9.png" alt="fastapi-05.PNG" /></p>
<p>before using the sendlk package we need to initialize it first after doing it <code>main.py</code> file will look like this</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> sendlk
<span class="hljs-keyword">import</span> os

<span class="hljs-comment"># Load the .env file</span>
load_dotenv(<span class="hljs-string">".env"</span>)

SENDLK_TOKEN = os.environ.get(<span class="hljs-string">"SENDLK_TOKEN"</span>)
SECRET = os.environ.get(<span class="hljs-string">"SECRET"</span>)

sendlk.initialize(SENDLK_TOKEN, SECRET)

<span class="hljs-comment"># Create the app</span>
app: FastAPI = FastAPI(
    title=<span class="hljs-string">"FastAPI Mobile Verification"</span>,
    version=<span class="hljs-string">"0.1.0"</span>,
)

<span class="hljs-comment"># App Root</span>
<span class="hljs-meta">@app.get("/", name="root")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">f"Welcome to Mobile Verification API <span class="hljs-subst">{app.version}</span>"</span>}
</code></pre>
<p><em>restart the server</em></p>
<h2 id="heading-creating-controllers">Creating Controllers</h2>
<p>inside the controller file, we are going to create some functions. first import those modules.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> sendlk.engine <span class="hljs-keyword">import</span> SMS
<span class="hljs-keyword">from</span> sendlk.responses <span class="hljs-keyword">import</span> SmsResponse
<span class="hljs-keyword">from</span> sendlk.exceptions <span class="hljs-keyword">import</span> SendLKException
<span class="hljs-keyword">from</span> sendlk.options <span class="hljs-keyword">import</span> SendLKVerifyOption, SendLKCodeTemplet
<span class="hljs-keyword">from</span> fastapi.exceptions <span class="hljs-keyword">import</span> HTTPException
<span class="hljs-keyword">from</span> starlette.responses <span class="hljs-keyword">import</span> JSONResponse
<span class="hljs-keyword">import</span> os
</code></pre>
<p>Ok, let's create <code>send_verify_code</code> function, it will accept the user phone number as an argument and will return the token.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_verify_code</span>(<span class="hljs-params">phone_number: str</span>) -&gt; str:</span>
    <span class="hljs-keyword">pass</span>
</code></pre>
<p>Almost forgot, add this line before the function and after the import lines</p>
<pre><code class="lang-python">SENDER_ID = os.environ.get(<span class="hljs-string">"SENDER_ID"</span>)
</code></pre>
<p>then we will create <code>SendLKVerifyOption</code> object to call the actual function</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_verify_code</span>(<span class="hljs-params">phone_number: str</span>) -&gt; str:</span>

    <span class="hljs-comment"># Create the SMS option object</span>
    options: SendLKVerifyOption = SendLKVerifyOption(
        code_length=<span class="hljs-number">4</span>,
        expires_in=<span class="hljs-number">3</span>,
        sender_id=SENDER_ID,
        code_templet=CustomCodeTemplet()
    )
</code></pre>
<blockquote>
<p><code>code_length</code> is the OTP code length</p>
<p><code>expires_in</code> means how much time that users have to submit the code, the token will be expired after this time. here I use 3 minutes.</p>
<p><code>sender_id</code> is the sender id that wants to use with the text message.</p>
</blockquote>
<p>but... wait...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639929229831/WM_EUXR1_.webp" alt="wait.webp" /></p>
<p>what the fudge nugget is the <code>CustomCodeTemplet()</code></p>
<p>here is the deal, in the sendlk package it has a default text body to send code to the phone number which is something like <code>0000 is your verification code.</code> ugly right? 
so we can override that and provide our own text body. like adding our company name to it like this -&gt; <code>*0000 is the verification code for foo service.*</code> Nice.</p>
<p>So let's do it. if you want you can create a separate file but I'm going to use the same file here. before the send function add this class.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomCodeTemplet</span>(<span class="hljs-params">SendLKCodeTemplet</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">text</span>(<span class="hljs-params">self, code: str</span>) -&gt; str:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{code}</span> is the varification code for foo serveice."</span>
</code></pre>
<p>I don't think I have to explain the above code it's quite self explainable, right? Ok moving on, we are going to use <code>try except</code> for the sendlk call. add this to the function.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_verify_code</span>(<span class="hljs-params">phone_number: str</span>) -&gt; str:</span>

    <span class="hljs-keyword">try</span>:
        <span class="hljs-comment"># Create the SMS option object</span>
        options: SendLKVerifyOption = SendLKVerifyOption(
            code_length=<span class="hljs-number">4</span>,
            expires_in=<span class="hljs-number">3</span>,
            sender_id=SENDER_ID,
            code_templet=CustomCodeTemplet()
        )

        response = SMS.send_verify_code(number=phone_number, verify_option=options)
        token = response.data.get(<span class="hljs-string">"token"</span>, <span class="hljs-literal">None</span>)
        <span class="hljs-keyword">return</span> token
    <span class="hljs-keyword">except</span> SendLKException <span class="hljs-keyword">as</span> e:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail=e.message)
</code></pre>
<blockquote>
<p><code>response = SMS.send_verify_code(number=phone_number, verify_option=options)</code> this is the line we acthuly send the code it will return the <code>SmsResponse</code> object with the token inside it.</p>
</blockquote>
<p>ok, what the fudge is this token? let me explain it to you. </p>
<p>Assume we providing phone number verification on our own mobile application. after we get the phone number from the user we make a request to the API with the number. then API will generate a code and send it to the user's phone number and API will send a response back to our app with the token which indicates the code. we don't even know what code the client received (obviously you can view it from the gateway dashboard). with the token app will wait for the client code submit. after that app will make another request to the API with code and token. API will verify the code and finish the process. Here is how it's work...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639935424192/LS02dXHj6.webp" alt="true-story.webp" /></p>
<p>back to the code.</p>
<p>if everything goes right we return the token otherwise will raise an exception with status code 400.</p>
<p>let's move on to the next function which is <code>validate_code</code>.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">validate_code</span>(<span class="hljs-params">token: str, code: str</span>) -&gt; str:</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-comment"># Validate the code</span>
        response = SMS.validate_verify_code(code=code, token=token)
        <span class="hljs-keyword">return</span> response.message
    <span class="hljs-keyword">except</span> SendLKException <span class="hljs-keyword">as</span> e:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail=e.message)
</code></pre>
<p>I'm sure the code is pretty self explainable right. anyway here we take token and code as arguments we will give it to the validate function on SMS class if we received success response we just return the message otherwise again we raise an exception.</p>
<p>ok, end of the file here is the full code</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> sendlk.engine <span class="hljs-keyword">import</span> SMS
<span class="hljs-keyword">from</span> sendlk.responses <span class="hljs-keyword">import</span> SmsResponse
<span class="hljs-keyword">from</span> sendlk.exceptions <span class="hljs-keyword">import</span> SendLKException
<span class="hljs-keyword">from</span> sendlk.options <span class="hljs-keyword">import</span> SendLKVerifyOption, SendLKCodeTemplet
<span class="hljs-keyword">from</span> fastapi.exceptions <span class="hljs-keyword">import</span> HTTPException
<span class="hljs-keyword">from</span> starlette.responses <span class="hljs-keyword">import</span> JSONResponse
<span class="hljs-keyword">import</span> os

SENDER_ID = os.environ.get(<span class="hljs-string">"SENDER_ID"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomCodeTemplet</span>(<span class="hljs-params">SendLKCodeTemplet</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">text</span>(<span class="hljs-params">self, code: str</span>) -&gt; str:</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{code}</span> is the varification code for foo serveice."</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_verify_code</span>(<span class="hljs-params">phone_number: str</span>) -&gt; str:</span>

    <span class="hljs-keyword">try</span>:
        <span class="hljs-comment"># Create the SMS option object</span>
        options: SendLKVerifyOption = SendLKVerifyOption(
            code_length=<span class="hljs-number">4</span>,
            expires_in=<span class="hljs-number">3</span>,
            sender_id=SENDER_ID,
            code_templet=CustomCodeTemplet()
        )

        response = SMS.send_verify_code(number=phone_number, verify_option=options)
        token = response.data.get(<span class="hljs-string">"token"</span>, <span class="hljs-literal">None</span>)
        <span class="hljs-keyword">return</span> token
    <span class="hljs-keyword">except</span> SendLKException <span class="hljs-keyword">as</span> e:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail=e.message)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">validate_code</span>(<span class="hljs-params">token: str, code: str</span>) -&gt; str:</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-comment"># Validate the code</span>
        response = SMS.validate_verify_code(code=code, token=token)
        <span class="hljs-keyword">return</span> response.message
    <span class="hljs-keyword">except</span> SendLKException <span class="hljs-keyword">as</span> e:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail=e.message)
</code></pre>
<h2 id="heading-creating-schemas">Creating Schemas</h2>
<p>moving on to the <code>schema.py</code> let's create some schemas.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Token</span>(<span class="hljs-params">BaseModel</span>):</span>
    token: str

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PhoneNumber</span>(<span class="hljs-params">BaseModel</span>):</span>
    phone_number: str

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidateCode</span>(<span class="hljs-params">Token</span>):</span>
    code: str

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Message</span>(<span class="hljs-params">BaseModel</span>):</span>
    message: str
</code></pre>
<p>Alright, I know this is not necessary to do. but come on let's do it right.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639937785688/rHTda32yB.webp" alt="come-on.webp" /></p>
<p>if you don't know what is schemas it is like blueprints for the API, we predefined the request and response models so there will be no mistakes when calling the API.</p>
<h2 id="heading-creating-routes">Creating Routes</h2>
<p>okay, finally we are going to create the routes</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter
<span class="hljs-keyword">from</span> src.controller <span class="hljs-keyword">import</span> send_verify_code, validate_code
<span class="hljs-keyword">from</span> src.schema <span class="hljs-keyword">import</span> PhoneNumber, ValidateCode, Token, Message

router = APIRouter(prefix=<span class="hljs-string">"/code"</span>)

<span class="hljs-meta">@router.post("/send", response_model=Token)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_verify_code_handler</span>(<span class="hljs-params">phone_number: PhoneNumber</span>):</span>
    token = send_verify_code(phone_number.phone_number)
    <span class="hljs-keyword">return</span> Token(token=token)

<span class="hljs-meta">@router.post("/validate", response_model=Message)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">validate_code_handler</span>(<span class="hljs-params">validate: ValidateCode</span>):</span>
    message = validate_code(validate.token, validate.code)
    <span class="hljs-keyword">return</span> Message(message=message)
</code></pre>
<p>you can clearly see what is going on, again I'm not going to explain line by line this is not entirely a FastAPI tutorial after all. anyway, we create sub route <code>/code</code> and add two <code>post</code> requests to it. <code>send_verify_code_handler</code> and <code>validate_code_handler</code> which will accept requests according to the schemas and return the response appropriately. </p>
<h2 id="heading-finishing-the-project">Finishing the Project</h2>
<p>as the final step back to the <code>main.py</code>,</p>
<p>import the router after the <strong>sendlk initialize line</strong> then add the route to the main app, that's it we done.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> sendlk
<span class="hljs-keyword">import</span> os

<span class="hljs-comment"># Load the .env file</span>
load_dotenv(<span class="hljs-string">".env"</span>)

SENDLK_TOKEN = os.environ.get(<span class="hljs-string">"SENDLK_TOKEN"</span>)
SECRET = os.environ.get(<span class="hljs-string">"SECRET"</span>)

sendlk.initialize(SENDLK_TOKEN, SECRET)

<span class="hljs-comment"># Imports routes</span>
<span class="hljs-keyword">from</span> src.route <span class="hljs-keyword">import</span> router

<span class="hljs-comment"># Create the app</span>
app: FastAPI = FastAPI(
    title=<span class="hljs-string">"FastAPI Mobile Verification"</span>,
    version=<span class="hljs-string">"0.1.0"</span>,
)

<span class="hljs-comment"># App Root</span>
<span class="hljs-meta">@app.get("/", name="root")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">f"Welcome to Mobile Verification API <span class="hljs-subst">{app.version}</span>"</span>}

app.include_router(router, prefix=<span class="hljs-string">"/api"</span>)
</code></pre>
<p>Okay, It's QA time. Run the server, reload if it's already running.</p>
<p>and go to this URL </p>
<blockquote>
<p><a target="_blank" href="http://127.0.0.1:8080/docs">localhost:8000/docs</a></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639940429462/3da6QSXhj.webp" alt="wow.webp" /></p>
<p>you got your own auto-generated Swagger UI that's the beauty of <a target="_blank" href="https://fastapi.tiangolo.com">FastAPI</a> and that's why we create all the schemas too. Cool right?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639941514665/TxSCaR3EI.png" alt="fastapi-06.PNG" /></p>
<p>try it, play it, do whatever you want with it. and <a target="_blank" href="https://github.com/ishangavidusha/fastapi-otp-sendlk">here</a> is the repo if you want to take a look at the code.</p>
<p>Peace out!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1639941406625/OYlQ_v5Pb.webp" alt="mic-drop.webp" /></p>
]]></content:encoded></item></channel></rss>