<?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>Forem: Kingkor Roy Tirtho</title>
    <description>The latest articles on Forem by Kingkor Roy Tirtho (@krtirtho).</description>
    <link>https://forem.com/krtirtho</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%2F512998%2F05e4dc19-521a-428c-8722-c4094d7287db.jpg</url>
      <title>Forem: Kingkor Roy Tirtho</title>
      <link>https://forem.com/krtirtho</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/krtirtho"/>
    <language>en</language>
    <item>
      <title>Flutter Desktop Mastery 3: Windows Inno Installer, Packaging, and Publishing to Chocolatey</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Fri, 15 May 2026 16:05:17 +0000</pubDate>
      <link>https://forem.com/krtirtho/flutter-desktop-mastery-3-windows-inno-installer-packaging-and-publishing-to-chocolatey-o9c</link>
      <guid>https://forem.com/krtirtho/flutter-desktop-mastery-3-windows-inno-installer-packaging-and-publishing-to-chocolatey-o9c</guid>
      <description>&lt;p&gt;Shipping a Windows desktop app is not just about producing an &lt;code&gt;.exe&lt;/code&gt;. Users expect a proper installer, clean uninstall, and an easy way to update. For &lt;a href="https://spotube.krtirtho.dev/" rel="noopener noreferrer"&gt;Spotube&lt;/a&gt;, we solved that by pairing &lt;strong&gt;fastforge&lt;/strong&gt; with an &lt;strong&gt;Inno Setup&lt;/strong&gt; template and &lt;strong&gt;Chocolatey&lt;/strong&gt; package manager for distribution to power users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Big Picture
&lt;/h2&gt;

&lt;p&gt;We want a pipeline that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Produces a Windows installer (&lt;code&gt;.exe&lt;/code&gt;) with a modern wizard UI&lt;/li&gt;
&lt;li&gt;Injects version metadata and app branding automatically&lt;/li&gt;
&lt;li&gt;Installs required runtime dependencies (VC++ runtime)&lt;/li&gt;
&lt;li&gt;Generates a Chocolatey &lt;code&gt;.nupkg&lt;/code&gt; for distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spotube achieves this with four main building blocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://fastforge.dev/" rel="noopener noreferrer"&gt;fastforge&lt;/a&gt;&lt;/strong&gt; for orchestrating Flutter desktop packaging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://jrsoftware.org/isinfo.php" rel="noopener noreferrer"&gt;Inno Setup&lt;/a&gt;&lt;/strong&gt; as the installer engine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/DomGries/InnoDependencyInstaller" rel="noopener noreferrer"&gt;InnoDependencyInstaller&lt;/a&gt;&lt;/strong&gt; for bundling prerequisites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://chocolatey.org/" rel="noopener noreferrer"&gt;Chocolatey&lt;/a&gt;&lt;/strong&gt; for package distribution&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;We also distribute our app through WinGet, but we will cover that in a future article. The Inno installer is the common denominator for both channels.&lt;/p&gt;

&lt;p&gt;WinGet part is more involved with CI/CD then build steps as Microsoft requires a public repository and a manifest PR for each release, so we will cover that separately.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Packaging Configuration (fastforge)
&lt;/h2&gt;

&lt;p&gt;Spotube uses &lt;code&gt;fastforge&lt;/code&gt; as the packaging driver and stores its Windows packaging config under:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;./windows/packaging/exe/make_config.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The config is intentionally small because the heavy lifting happens in the Inno script template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;app_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;RANDOM GUID&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;publisher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR NAME OR COMPANY&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;publisher_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://yourwebsite.com&lt;/span&gt;
&lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YOUR APP NAME&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;create_desktop_icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;install_dir_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;YourAppNameWithoutSpaces&lt;/span&gt;
&lt;span class="na"&gt;script_template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inno_setup.iss&lt;/span&gt;
&lt;span class="na"&gt;locales&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;en&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config injects identity details, publisher URLs, and icon behavior into the installer template. You can expand &lt;code&gt;locales&lt;/code&gt; to ship translated installer UIs later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Inno Setup Template
&lt;/h2&gt;

&lt;p&gt;The real power is in the installer template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;spotube/windows/packaging/exe/inno_setup.iss&lt;/code&gt; contributed by the community (Thanks to &lt;a href="https://github.com/olivier2" rel="noopener noreferrer"&gt;@olivier2&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fastforge takes a &lt;strong&gt;templated Inno script&lt;/strong&gt; (using Jinja-like tags) so it can fill in build-time values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Setup Metadata
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;To learn in-depth about InnoScript and how it works, check out the &lt;a href="https://jrsoftware.org/ishelp/" rel="noopener noreferrer"&gt;Inno Setup documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Chouzz.vscode-innosetup" rel="noopener noreferrer"&gt;InnoSetup VSCode Extension&lt;/a&gt; for syntax highlighting and IntelliSense in VSCode or similar IDEs/code-editor.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Setup]&lt;/span&gt;
&lt;span class="py"&gt;AppId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{APP_ID}}&lt;/span&gt;
&lt;span class="py"&gt;AppVersion&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{APP_VERSION}}&lt;/span&gt;
&lt;span class="py"&gt;AppName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{DISPLAY_NAME}}&lt;/span&gt;
&lt;span class="py"&gt;AppPublisher&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{PUBLISHER_NAME}}&lt;/span&gt;
&lt;span class="py"&gt;AppPublisherURL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{PUBLISHER_URL}}&lt;/span&gt;
&lt;span class="py"&gt;DefaultDirName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{autopf}&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s"&gt;{DISPLAY_NAME}}&lt;/span&gt;
&lt;span class="py"&gt;OutputBaseFilename&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{OUTPUT_BASE_FILENAME}}&lt;/span&gt;
&lt;span class="py"&gt;SetupIconFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{SETUP_ICON_FILE}}&lt;/span&gt;
&lt;span class="py"&gt;WizardStyle&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;modern&lt;/span&gt;
&lt;span class="py"&gt;WizardSmallImageFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"..&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;..&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;assets&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;branding&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s"&gt;logo.bmp"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script keeps branding consistent and ensures Windows “Programs &amp;amp; Features” metadata is correct.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installer Tasks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Tasks]&lt;/span&gt;
&lt;span class="err"&gt;Name:&lt;/span&gt; &lt;span class="err"&gt;"desktopicon"&lt;/span&gt;&lt;span class="c"&gt;; Description: "{cm:CreateDesktopIcon}"; Flags: checkedonce
&lt;/span&gt;&lt;span class="err"&gt;Name:&lt;/span&gt; &lt;span class="err"&gt;"launchAtStartup"&lt;/span&gt;&lt;span class="c"&gt;; Description: "{cm:AutoStartProgram,{{DISPLAY_NAME}}}"; Flags: unchecked
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users get a desktop icon option and an optional “launch on startup” task. The defaults are driven by fastforge variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Files &amp;amp; Shortcuts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Files]&lt;/span&gt;
&lt;span class="err"&gt;Source:&lt;/span&gt; &lt;span class="err"&gt;"{{SOURCE_DIR}}\\*"&lt;/span&gt;&lt;span class="c"&gt;; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
&lt;/span&gt;
&lt;span class="nn"&gt;[Icons]&lt;/span&gt;
&lt;span class="err"&gt;Name:&lt;/span&gt; &lt;span class="err"&gt;"{autoprograms}\\{{DISPLAY_NAME}}"&lt;/span&gt;&lt;span class="c"&gt;; Filename: "{app}\\{{EXECUTABLE_NAME}}"
&lt;/span&gt;&lt;span class="err"&gt;Name:&lt;/span&gt; &lt;span class="err"&gt;"{autodesktop}\\{{DISPLAY_NAME}}"&lt;/span&gt;&lt;span class="c"&gt;; Filename: "{app}\\{{EXECUTABLE_NAME}}"; Tasks: desktopicon
&lt;/span&gt;&lt;span class="err"&gt;Name:&lt;/span&gt; &lt;span class="err"&gt;"{userstartup}\\{{DISPLAY_NAME}}"&lt;/span&gt;&lt;span class="c"&gt;; Filename: "{app}\\{{EXECUTABLE_NAME}}"; Tasks: launchAtStartup
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All app files are copied into &lt;code&gt;{app}&lt;/code&gt;, with shortcuts created for Start Menu, Desktop, and Startup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtime Dependencies
&lt;/h3&gt;

&lt;p&gt;We include the &lt;strong&gt;&lt;a href="https://github.com/DomGries/InnoDependencyInstaller" rel="noopener noreferrer"&gt;InnoDependencyInstaller&lt;/a&gt;&lt;/strong&gt; helper so prerequisites install automatically:&lt;br&gt;
It supports a wide range of dependencies, but for Flutter Windows, the most common one is the &lt;strong&gt;VC++ 2015-2022 runtime&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;#define public Dependency_Path_NetCoreCheck "..\\..\\build\\inno-depend\\dependencies\\"
#include "..\\..\\build\\inno-depend\\CodeDependencies.iss"
&lt;/span&gt;
&lt;span class="nn"&gt;[Code]&lt;/span&gt;
&lt;span class="err"&gt;function&lt;/span&gt; &lt;span class="err"&gt;InitializeSetup:&lt;/span&gt; &lt;span class="err"&gt;Boolean&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;&lt;span class="err"&gt;begin&lt;/span&gt;
  &lt;span class="err"&gt;Dependency_AddVC2015To2022&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;  &lt;span class="err"&gt;Result&lt;/span&gt; &lt;span class="err"&gt;:=&lt;/span&gt; &lt;span class="err"&gt;True&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;&lt;span class="err"&gt;end&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pulls in the &lt;strong&gt;VC++ 2015-2022 runtime&lt;/strong&gt; at install time, which is required for most Flutter Windows builds.&lt;/p&gt;

&lt;p&gt;The entire &lt;a href="https://github.com/KRTirtho/spotube/blob/412f3dd81cd02cb97d7e8c24505774340f89c5cf/windows/packaging/exe/inno_setup.iss" rel="noopener noreferrer"&gt;inno_setup.iss&lt;/a&gt; is quite huge. We suggest you give it a look at or even copy it &lt;a href="https://github.com/KRTirtho/spotube/blob/412f3dd81cd02cb97d7e8c24505774340f89c5cf/windows/packaging/exe/inno_setup.iss" rel="noopener noreferrer"&gt;from our repository&lt;/a&gt; as it'll work for 90% of Flutter desktop apps without modification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the app and packaging the installer
&lt;/h3&gt;

&lt;p&gt;Pre-requisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Inno Setup on your Windows machine&lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;iscc&lt;/code&gt; (Inno Setup Compiler) is in your PATH&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;git clone https://github.com/DomGries/InnoDependencyInstaller build/inno-depend&lt;/code&gt; to clone the InnoDependencyInstaller into the expected location&lt;/li&gt;
&lt;li&gt;Install fastforge globally via &lt;code&gt;dart pub global activate fastforge&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, from the root of the Spotube repo, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fastforge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--targets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--skip-clean&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build the Flutter Windows app, generate the Inno installer, and place it in &lt;code&gt;dist/&amp;lt;version from pubspec.yaml&amp;gt;/&amp;lt;your-app-name&amp;gt;-windows-setup.exe&lt;/code&gt;. You can distribute this &lt;code&gt;.exe&lt;/code&gt; directly, and users can actually run it to install the app. Most developers stop here and share the installer as-is, which is perfectly fine for small projects or internal distribution.&lt;/p&gt;

&lt;p&gt;But for a more polished distribution, we wrap it in a Chocolatey package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chocolatey Packaging Structure
&lt;/h2&gt;

&lt;p&gt;Once the installer exists, Spotube wraps it as a Chocolatey package from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;./choco-struct/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;choco-struct/
  spotube.nuspec
  tools/
    chocolateyinstall.ps1
    chocolateyuninstall.ps1
    VERIFICATION.txt
    LICENSE.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Nuspec Manifest
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;your app&amp;gt;.nuspec&lt;/code&gt; defines package metadata that will be displayed on Chocolatey and used for versioning. Spotube’s &lt;code&gt;spotube.nuspec&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter
enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;package&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;metadata&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- == PACKAGE SPECIFIC SECTION == --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;spotube&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;packageSourceUrl&amp;gt;&lt;/span&gt;https://github.com/KRTirtho/spotube/tree/master/choco-struct&lt;span class="nt"&gt;&amp;lt;/packageSourceUrl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;owners&amp;gt;&lt;/span&gt;Kingkor Roy Tirtho&lt;span class="nt"&gt;&amp;lt;/owners&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- ============================== --&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- == SOFTWARE SPECIFIC SECTION == --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;spotube (Install)&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;authors&amp;gt;&lt;/span&gt;Kingkor Roy Tirtho&lt;span class="nt"&gt;&amp;lt;/authors&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;projectUrl&amp;gt;&lt;/span&gt;https://spotube.krtirtho.dev&lt;span class="nt"&gt;&amp;lt;/projectUrl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;iconUrl&amp;gt;&lt;/span&gt;
      https://rawcdn.githack.com/KRTirtho/spotube/7edb0bb834eb18c05551e30a891720a6abf53dbe/assets/branding/spotube-logo.png&lt;span class="nt"&gt;&amp;lt;/iconUrl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;copyright&amp;gt;&lt;/span&gt;2022 Spotube&lt;span class="nt"&gt;&amp;lt;/copyright&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- If there is a license Url available, it is required for the community feed --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;licenseUrl&amp;gt;&lt;/span&gt;https://github.com/KRTirtho/spotube/blob/master/LICENSE&lt;span class="nt"&gt;&amp;lt;/licenseUrl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;requireLicenseAcceptance&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/requireLicenseAcceptance&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;projectSourceUrl&amp;gt;&lt;/span&gt;https://github.com/KRTirtho/spotube&lt;span class="nt"&gt;&amp;lt;/projectSourceUrl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;docsUrl&amp;gt;&lt;/span&gt;https://spotube.krtirtho.dev&lt;span class="nt"&gt;&amp;lt;/docsUrl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;bugTrackerUrl&amp;gt;&lt;/span&gt;https://github.com/KRTirtho/spotube/issues/new&lt;span class="nt"&gt;&amp;lt;/bugTrackerUrl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tags&amp;gt;&lt;/span&gt;spotube music audio youtube flutter&lt;span class="nt"&gt;&amp;lt;/tags&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;summary&amp;gt;&lt;/span&gt;🎧 Open source music client that doesn't require Premium nor uses Electron! Available
      for both desktop &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; mobile! &lt;span class="nt"&gt;&amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;
      Spotube is a Flutter based lightweight music client. It utilizes the power
      of music metadata providers &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; Youtube's public API &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; creates a hazardless, performant
      &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; resource
      friendly User Experience

      # Features
      - Open source/libre software
      - Anonymous/guest login
      - Cross platform support
      - No telemetry, diagnostics or user data collection
      - Lightweight &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; resource-friendly
      - Native performance (Thanks to Flutter+Skia)
      - Playback control is done locally instead of on the server
      - Small size &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; less data usage
      - No ads since it uses all public &lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt; free APIs (It is still recommended
      to support the creators by watching/liking/subscribing to the artists' YouTube channels or
      liking their tracks on different music platforms.)
      - Time synced lyrics
      - Downloadable tracks
    &lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;releaseNotes&amp;gt;&lt;/span&gt;https://github.com/KRTirtho/spotube/releases/tag/v1.0.0&lt;span class="nt"&gt;&amp;lt;/releaseNotes&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/metadata&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;files&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;file&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"tools\**"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"tools"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/files&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/package&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The version placeholder is replaced during the Windows build process (see the automation section below).&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Script (Inno Setup Silent)
&lt;/h3&gt;

&lt;p&gt;Chocolatey runs &lt;code&gt;tools/chocolateyinstall.ps1&lt;/code&gt;, which uses the Inno Setup silent install flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$fileLocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Join-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$toolsDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;your-app-name&amp;gt;-windows-setup.exe'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Inno Setup silent install arguments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$packageArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;fileType&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'exe'&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$fileLocation&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;silentArgs&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-'&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;validExitCodes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# This is a Chocolatey helper function that runs the installer with the specified arguments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Install-ChocolateyInstallPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;packageArgs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;To learn more about Chocolatey packaging and the available helper functions, check out the &lt;a href="https://docs.chocolatey.org/en-us/create/create-packages" rel="noopener noreferrer"&gt;Chocolatey Packaging Tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Uninstall Script
&lt;/h3&gt;

&lt;p&gt;The uninstall uses the registry to discover the Inno uninstall entry and runs it silently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-UninstallRegistryKey&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-SoftwareName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;your-app-name&amp;gt;*'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nv"&gt;$packageArgs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'file'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UninstallString&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Uninstall-ChocolateyPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;packageArgs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you can see we're quite literally embedding the app binary (installer) inside the Chocolatey package. Because our application size is quite small &amp;lt; 30MB, so we embed it. But it is &lt;strong&gt;highly recommended&lt;/strong&gt; that you host the installer on a CDN or your own server and use the &lt;code&gt;url&lt;/code&gt; parameter instead of &lt;code&gt;file&lt;/code&gt; in &lt;code&gt;Install-ChocolateyInstallPackage&lt;/code&gt; to avoid hitting Chocolatey’s package size limits and to allow for faster updates without needing to republish the entire package.&lt;/p&gt;

&lt;p&gt;Following is the example of using &lt;code&gt;url&lt;/code&gt; instead of &lt;code&gt;file&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$packageName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'windirstat'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$fileType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'exe'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Using url instead of embedding the installer in the package&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'http://prdownloads.sourceforge.net/windirstat/windirstat1_1_2_setup.exe'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$silentArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/S'&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Install-ChocolateyPackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$packageName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$fileType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$silentArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verification File
&lt;/h3&gt;

&lt;p&gt;Chocolatey moderators require a hash check for embedded binaries. Spotube injects SHA256 into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tools/VERIFICATION.txt&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;SHA256     0a05b56727f5c72a6d608dbd320c8fd119eb5030f20253127889c7306d797cf4  tools\Spotube-windows-x86_64-setup.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That hash is generated after the &lt;code&gt;.exe&lt;/code&gt; is built. You can generate it manually with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-FileHash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist\&amp;lt;version&amp;gt;\&amp;lt;your-app-name&amp;gt;-windows-setup.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Algorithm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SHA256&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Automated Build Flow
&lt;/h2&gt;

&lt;p&gt;At Spotube, we created a CLI that has a dedicated Windows build command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spotube/cli/commands/build/windows.dart&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This command:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replaces &lt;code&gt;%{{SPOTUBE_VERSION}}%&lt;/code&gt; placeholders in nuspec and verification files&lt;/li&gt;
&lt;li&gt;Builds the installer via fastforge&lt;/li&gt;
&lt;li&gt;Renames the output installer to &lt;code&gt;Spotube-windows-x86_64-setup.exe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Calculates SHA256 and injects it into &lt;code&gt;VERIFICATION.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Packs the Chocolatey &lt;code&gt;.nupkg&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The core fastforge invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fastforge package &lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;windows &lt;span class="nt"&gt;--targets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exe &lt;span class="nt"&gt;--skip-clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the Chocolatey packaging step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;choco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;choco-struct/spotube.nuspec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--outputdirectory&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Publishing to Chocolatey
&lt;/h2&gt;

&lt;p&gt;Once you have the &lt;code&gt;.nupkg&lt;/code&gt;, publishing is just the standard Chocolatey flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;choco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dist/&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;your-app-name&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;-windows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nupkg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https://push.chocolatey.org/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure the package version matches your app’s version. Spotube derives that from &lt;code&gt;pubspec.yaml&lt;/code&gt; (&lt;code&gt;version: 5.1.1+44&lt;/code&gt;) and strips the build number for Chocolatey compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ensure &lt;code&gt;pubspec.yaml&lt;/code&gt; has the correct semantic version&lt;/li&gt;
&lt;li&gt;Confirm &lt;code&gt;inno_setup.iss&lt;/code&gt; has your branding, icons, and app ID&lt;/li&gt;
&lt;li&gt;Build with &lt;code&gt;fastforge package --platform=windows --targets=exe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify &lt;code&gt;Spotube-windows-x86_64-setup.exe&lt;/code&gt; exists in &lt;code&gt;dist/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;tools/VERIFICATION.txt&lt;/code&gt; for updated SHA256&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;choco pack&lt;/code&gt; and test install locally&lt;/li&gt;
&lt;li&gt;Push the &lt;code&gt;.nupkg&lt;/code&gt; to Chocolatey&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Flutter desktop apps feel native only when distribution is native too. A clean Inno installer paired with Chocolatey gives your users a Windows-first experience: a proper installer, silent updates, and easy uninstalls.&lt;/p&gt;

&lt;p&gt;Keep an eye out for the next article where we’ll cover WinGet packaging and distribution, which is another major channel for Windows apps.&lt;/p&gt;




&lt;p&gt;Follow the full 'Flutter Desktop Mastery' series on the official blog: &lt;a href="https://spotube.krtirtho.dev/blog/flutter-desktop-mastery/part-3-windows-inno-installer-packaging-and-publishing-to-chocolatey/" rel="noopener noreferrer"&gt;https://spotube.krtirtho.dev/blog/flutter-desktop-mastery/part-3-windows-inno-installer-packaging-and-publishing-to-chocolatey/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>windows</category>
      <category>chocolatey</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Flutter Desktop Mastery 2: System Tray &amp; Local Notifications</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Fri, 15 May 2026 16:01:42 +0000</pubDate>
      <link>https://forem.com/krtirtho/flutter-desktop-mastery-2-system-tray-local-notifications-4edg</link>
      <guid>https://forem.com/krtirtho/flutter-desktop-mastery-2-system-tray-local-notifications-4edg</guid>
      <description>&lt;p&gt;The system tray is essential for a lot of desktop apps that needs to run in the background. But due to Flutter’s limited desktop support, again the Flutter Desktop Community has saved us with another much needed plugin. &lt;/p&gt;

&lt;p&gt;At &lt;a href="https://spotube.krtirtho.dev/" rel="noopener noreferrer"&gt;Spotube&lt;/a&gt;, users expect to control playback from the tray, minimize the app while keeping it running, and have quick access to essential features without opening the main window.&lt;/p&gt;

&lt;p&gt;So, we implement a &lt;strong&gt;reactive system tray&lt;/strong&gt; that stays in sync with the app's state. We use the &lt;a href="https://pub.dev/packages/tray_manager" rel="noopener noreferrer"&gt;&lt;code&gt;tray_manager&lt;/code&gt;&lt;/a&gt; package to accomplish that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing the Dependencies
&lt;/h3&gt;

&lt;p&gt;In Spotube's &lt;strong&gt;pubspec.yaml&lt;/strong&gt;, we include:&lt;br&gt;
&lt;/p&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;tray_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;latest&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;local_notifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;latest&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;# we use it for state management&lt;/span&gt;
  &lt;span class="na"&gt;hooks_riverpod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;latest&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;window_manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;latest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;tray_manager&lt;/code&gt; package provides cross-platform system tray integration, while &lt;code&gt;local_notifier&lt;/code&gt; handles desktop notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Tray Menu
&lt;/h3&gt;

&lt;p&gt;Our tray menu is built reactively using Riverpod providers.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/KRTirtho/spotube/blob/2cc8d2620d2d6ea9fa0ee9e3ff2fdc6fbd87f5e1/lib/provider/tray_manager/tray_menu.dart" rel="noopener noreferrer"&gt;&lt;strong&gt;lib/provider/tray_manager/tray_menu.dart&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:hooks_riverpod/hooks_riverpod.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:tray_manager/tray_manager.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:window_manager/window_manager.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;trayMenuProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;//..... internal logic&lt;/span&gt;
&lt;span class="c1"&gt;// ref.watch() to other providers that update this&lt;/span&gt;
&lt;span class="c1"&gt;// provider on change&lt;/span&gt;

&lt;span class="c1"&gt;// This is the part that you need to understand.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Menu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Show/Hide Window"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&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="c1"&gt;// We use window_manager plugin to show or hide the window&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isVisible&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&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;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;separator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// A special type of menu item that creates a divider between other menu items&lt;/span&gt;
      &lt;span class="c1"&gt;// Play/Pause button that adapts to current state&lt;/span&gt;
      &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="n"&gt;isPlaying&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Pause"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Play"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// You can disable (gray out) an option in the menu&lt;/span&gt;
        &lt;span class="nl"&gt;disabled:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isPlaybackPlaying&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&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="c1"&gt;// .. we are handling play/pause logic here&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;disabled:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isPlaybackPlaying&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// ... next handle logic&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Previous"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;disabled:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isPlaybackPlaying&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// ... previous handle logic&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="c1"&gt;// Submenu for advanced playback controls&lt;/span&gt;
      &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Playback"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;submenu:&lt;/span&gt; &lt;span class="n"&gt;Menu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="c1"&gt;// Repeat mode toggle&lt;/span&gt;
            &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Repeat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="c1"&gt;// Checked is a special boolean that can be used&lt;/span&gt;
              &lt;span class="c1"&gt;// to show a check next to an item in the menu&lt;/span&gt;
              &lt;span class="nl"&gt;checked:&lt;/span&gt; &lt;span class="n"&gt;isLoopOne&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Logic for repeat&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c1"&gt;// Shuffle toggle&lt;/span&gt;
            &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Shuffle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;checked:&lt;/span&gt; &lt;span class="n"&gt;isShuffled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Logic for shuffle&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;separator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Logic for stop&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;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;separator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="n"&gt;MenuItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;"Quit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;onClick:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Closes the program&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;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features of our tray menu:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reactive state&lt;/strong&gt;: Menu items update when playback state changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-aware labels&lt;/strong&gt;: "Play" vs "Pause" adapts to current state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disabled items&lt;/strong&gt;: Playback controls are disabled when no track is loaded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checked items&lt;/strong&gt;: Repeat and Shuffle show their current state with checkmarks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organized submenu&lt;/strong&gt;: Advanced controls grouped under "Playback"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Platform-Aware Tray Manager
&lt;/h3&gt;

&lt;p&gt;The system tray behaves differently on Windows vs macOS/Linux. &lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/KRTirtho/spotube/blob/2cc8d2620d2d6ea9fa0ee9e3ff2fdc6fbd87f5e1/lib/provider/tray_manager/tray_manager.dart" rel="noopener noreferrer"&gt;&lt;strong&gt;lib/provider/tray_manager/tray_manager.dart&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemTrayManager&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;TrayListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Ref&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;SystemTrayManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enabled&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;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&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;initialize&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsDesktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&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;enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Set platform-specific tray icon&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;trayManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;kIsWindows&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;'assets/branding/spotube-logo.ico'&lt;/span&gt;  &lt;span class="c1"&gt;// .ico for Windows&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kIsFlatpak&lt;/span&gt;
                &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;'com.github.KRTirtho.Spotube'&lt;/span&gt;  &lt;span class="c1"&gt;// App ID for Flatpak&lt;/span&gt;
                &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'assets/branding/spotube-logo.png'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// PNG for others&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Register this object as a listener for tray events&lt;/span&gt;
      &lt;span class="n"&gt;trayManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Clean up when tray is disabled&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;trayManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;destroy&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="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;trayManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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 left-click on tray icon&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;onTrayIconMouseDown&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;kIsWindows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Windows convention: left-click shows the window&lt;/span&gt;
      &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// macOS/Linux convention: left-click shows context menu&lt;/span&gt;
      &lt;span class="n"&gt;trayManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;popUpContextMenu&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="c1"&gt;// Handle right-click on tray icon&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;onTrayIconRightMouseDown&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsWindows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// macOS/Linux: right-click shows the window&lt;/span&gt;
      &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Windows: right-click shows context menu&lt;/span&gt;
      &lt;span class="n"&gt;trayManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;popUpContextMenu&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;&lt;strong&gt;Platform differences we handle:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Icon format&lt;/strong&gt;: Windows requires &lt;code&gt;.ico&lt;/code&gt;, Linux/Flatpak use PNG or app ID strings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click behavior&lt;/strong&gt;: Windows expects left-click to open, macOS/Linux expects right-click&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listener pattern&lt;/strong&gt;: We implement the &lt;code&gt;TrayListener&lt;/code&gt; interface to receive tray events&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Tray Manager Provider
&lt;/h3&gt;

&lt;p&gt;Our Riverpod provider ties everything together:&lt;br&gt;
&lt;/p&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;trayManagerProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Watch user preference for showing tray icon&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;userPreferencesProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showSystemTrayIcon&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update tray menu reactively whenever app state changes&lt;/span&gt;
    &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trayMenuProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;menu&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsDesktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;trayManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContextMenu&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Create and manage the tray manager instance&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SystemTrayManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;enabled:&lt;/span&gt; &lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Clean up when provider is disposed&lt;/span&gt;
    &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onDispose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dispose&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;manager&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;&lt;strong&gt;Why this pattern is powerful:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reactive updates&lt;/strong&gt;: When some state (for Spotube, audio) changes (e.g. play/pause), the menu updates automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User control&lt;/strong&gt;: Users can toggle tray icon from settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle management&lt;/strong&gt;: Proper cleanup with &lt;code&gt;ref.onDispose&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single source of truth&lt;/strong&gt;: Audio player state is the source, tray menu is computed from it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Initializing the Tray in Your App
&lt;/h3&gt;

&lt;p&gt;In your root app widget, initialize the tray manager early:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Spotube&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;HookConsumerWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize tray manager&lt;/span&gt;
    &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trayManagerProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="c1"&gt;// ... rest of your app&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;
  
  
  Desktop Notifications 🔔
&lt;/h2&gt;

&lt;p&gt;Desktop notifications inform users about important events even when the app window is hidden or minimized. At Spotube, we use the &lt;a href="https://pub.dev/packages/local_notifier" rel="noopener noreferrer"&gt;&lt;code&gt;local_notifier&lt;/code&gt;&lt;/a&gt; package to show native desktop notifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initializing Local Notifications
&lt;/h3&gt;

&lt;p&gt;In your &lt;strong&gt;lib/main.dart&lt;/strong&gt;, initialize the notification system for desktop:&lt;br&gt;
&lt;/p&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="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rawArgs&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;widgetsBinding&lt;/span&gt; &lt;span class="o"&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;kIsDesktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize local notifier with app name&lt;/span&gt;
    &lt;span class="c1"&gt;// This name appears in system notification settings&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;localNotifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;appName:&lt;/span&gt; &lt;span class="s"&gt;"Spotube"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Initialize other desktop services&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;WindowManagerTools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// ... rest of initialization&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Minimize to Tray Notification
&lt;/h3&gt;

&lt;p&gt;When users minimize Spotube to the system tray, we show a notification to confirm the app is still running. This is handled in &lt;a href="https://github.com/KRTirtho/spotube/blob/3fb003ea60d90a8f8da7eb177bc747d4eb219199/lib/hooks/configurators/use_close_behavior.dart" rel="noopener noreferrer"&gt;&lt;strong&gt;lib/hooks/configurators/use_close_behavior.dart&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:local_notifier/local_notifier.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This notification is created once and reused multiple times&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;closeNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsDesktop&lt;/span&gt;
    &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LocalNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"'Spotube',"&lt;/span&gt;
        &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="s"&gt;'Running in background. Minimized to System Tray'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;actions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="n"&gt;LocalNotificationAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;text:&lt;/span&gt; &lt;span class="s"&gt;'Close The App'&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;onClickAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// User clicked "Close The App" action&lt;/span&gt;
        &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;&lt;strong&gt;Breaking this down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Desktop-only&lt;/strong&gt;: The notification is only created on desktop platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nullable&lt;/strong&gt;: On mobile, it's &lt;code&gt;null&lt;/code&gt;, preventing crashes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Callback&lt;/strong&gt;: &lt;code&gt;onClickAction&lt;/code&gt; is invoked when the user clicks the notification action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable&lt;/strong&gt;: We create it once and call &lt;code&gt;show()&lt;/code&gt; multiple times&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Showing the Notification
&lt;/h3&gt;

&lt;p&gt;When the user closes the window and has "minimize to tray" enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;useCloseBehavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WidgetRef&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;useWindowListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;onWindowClose:&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;preferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userPreferencesProvider&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;preferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closeBehavior&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;CloseBehavior&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minimizeToTray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Hide the window&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Show notification that app is still running&lt;/span&gt;
        &lt;span class="n"&gt;closeNotification&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Close completely&lt;/span&gt;
        &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This notification gives users confidence that their music will keep playing and shows them how to close the app if needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are you curious about this weird way of writing logical function? These look similar to react-hook, aren’t they? Actually, at Spotube, to reduce boilerplate and easier life-cycle handling of widgets, we use &lt;code&gt;flutter_hooks&lt;/code&gt; package that is basically react-hooks but for Flutter. You can learn about the custom &lt;code&gt;useWindowListener&lt;/code&gt; hook &lt;a href="https://dev.to/blog/flutter-desktop-mastery/part-1-window-management"&gt;on part 1 of this series&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Building Custom Notifications
&lt;/h3&gt;

&lt;p&gt;You can create notifications for other scenarios. Here's a pattern for doing it safely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showTrackChangeNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Track&lt;/span&gt; &lt;span class="n"&gt;track&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsDesktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Only on desktop&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"'Now Playing',"&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;${track.name}&lt;/span&gt;&lt;span class="s"&gt; • &lt;/span&gt;&lt;span class="si"&gt;${track.artist}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;silent:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Play system sound&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showDownloadCompleteNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fileName&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsDesktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"'Download Complete',"&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;actions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="n"&gt;LocalNotificationAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;text:&lt;/span&gt; &lt;span class="s"&gt;'Open Folder'&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;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onClickAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Open the downloads folder&lt;/span&gt;
    &lt;span class="n"&gt;openFileExplorer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;downloadPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&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;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always check &lt;code&gt;kIsDesktop&lt;/code&gt;&lt;/strong&gt;: Prevents crashes on mobile&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide meaningful titles and bodies&lt;/strong&gt;: Users should understand what happened&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use actions sparingly&lt;/strong&gt;: Notification actions are powerful but can be confusing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid notification spam&lt;/strong&gt;: Don't show notifications for every minor event&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Putting It All Together: The Complete Desktop Experience
&lt;/h2&gt;

&lt;p&gt;Here's how window management, system tray, and notifications work together in Spotube's initialization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;HookConsumerWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Make sure run this in the root app to Setup system tray with reactive menu&lt;/span&gt;
    &lt;span class="c1"&gt;// Riverpod providers are lazy, so we need to listen to it here to make sure it's initialized&lt;/span&gt;
    &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trayManagerProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__&lt;/span&gt;&lt;span class="p"&gt;)&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;ShadcnApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// ... app configuration&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;&lt;strong&gt;The user flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User clicks close button&lt;/strong&gt; → &lt;code&gt;useCloseBehavior&lt;/code&gt; intercepts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close preference checked&lt;/strong&gt; → If "minimize to tray" is enabled...&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window hides&lt;/strong&gt; → &lt;code&gt;windowManager.hide()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notification shows&lt;/strong&gt; → User sees "Running in background"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User interacts with tray&lt;/strong&gt; → Platform-specific behavior triggers

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: Left-click shows window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS/Linux&lt;/strong&gt;: Right-click shows window&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tray menu displays&lt;/strong&gt; → Reactive menu with current playback state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User controls playback&lt;/strong&gt; → Menu items trigger audio player actions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Menu updates&lt;/strong&gt; → State changes reflect in next menu open&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This seamless integration is what makes Spotube feel like a native desktop application.&lt;/p&gt;

&lt;p&gt;Happy building! 🚀&lt;/p&gt;




&lt;p&gt;Follow the full 'Flutter Desktop Mastery' series on the official blog: &lt;a href="https://spotube.krtirtho.dev/blog/flutter-desktop-mastery/part-2-system-tray-local-notifications/" rel="noopener noreferrer"&gt;https://spotube.krtirtho.dev/blog/flutter-desktop-mastery/part-2-system-tray-local-notifications/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>desktop</category>
      <category>windows</category>
    </item>
    <item>
      <title>Flutter Desktop Mastery 1: Window Management</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Fri, 15 May 2026 15:56:18 +0000</pubDate>
      <link>https://forem.com/krtirtho/flutter-desktop-mastery-1-window-management-2mb5</link>
      <guid>https://forem.com/krtirtho/flutter-desktop-mastery-1-window-management-2mb5</guid>
      <description>&lt;p&gt;There’s no debate that Flutter’s desktop support is very limited. And the Flutter community actually did an amazing job creating an ecosystem of packages to make Flutter Desktop production ready. &lt;/p&gt;

&lt;p&gt;Building a professional-grade desktop application with Flutter requires more than just writing Dart code. Here at &lt;a href="https://spotube.krtirtho.dev/" rel="noopener noreferrer"&gt;Spotube&lt;/a&gt;, we've spent years perfecting the craft of cross-platform desktop development, tackling everything from window management to system integration to multi-platform packaging. This series of article breaks down the technical decisions and implementation patterns that make Spotube a production-ready desktop application. We’ll focus on desktop specific features like Window Management, System Tray support and Desktop Notifications&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Window Management &amp;amp; Lifecycle 🪟
&lt;/h2&gt;

&lt;p&gt;At Spotube, window management is one of the most critical aspects of the desktop experience. We use the &lt;a href="https://pub.dev/packages/window_manager" rel="noopener noreferrer"&gt;&lt;code&gt;window_manager&lt;/code&gt;&lt;/a&gt; package to give us fine-grained control over window behavior across platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialization &amp;amp; Configuration
&lt;/h3&gt;

&lt;p&gt;At Spotube, we always create a wrapper for external libraries for easier refactoring and very easy mocking for unit testing.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/KRTirtho/spotube/blob/v5.1.1/lib/services/wm_tools/wm_tools.dart" rel="noopener noreferrer"&gt;&lt;strong&gt;lib/services/wm_tools/wm_tools.dart&lt;/strong&gt;&lt;/a&gt;, we initialize the window manager with platform-aware settings. Because &lt;code&gt;windowManager&lt;/code&gt; cannot be called in Android, iOS or Web. This will cause exceptions that might crash the app.&lt;/p&gt;

&lt;p&gt;We use Singletons Design Pattern for the services and &lt;code&gt;freezed&lt;/code&gt; for immutable data classes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/foundation.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:freezed_annotation/freezed_annotation.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:shared_preferences/shared_preferences.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;part&lt;/span&gt; &lt;span class="s"&gt;'wm_tools.freezed.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;part&lt;/span&gt; &lt;span class="s"&gt;'wm_tools.g.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Some helper constants. Give emphasis on the kIsWeb constant, it&lt;/span&gt;
&lt;span class="c1"&gt;// must be checked before doing anything with [Platform] object.&lt;/span&gt;
&lt;span class="c1"&gt;// Platform (or more specifically dart:io) is not available on flutter web&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;kIsAndroid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kIsWeb&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAndroid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;kIsIOS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kIsWeb&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isIOS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;kIsMobile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kIsAndroid&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;kIsIOS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@freezed&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WindowSize&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;_$WindowSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;WindowSize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;maximized&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="n"&gt;_WindowSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;WindowSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;_$WindowSizeFromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&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;Above is our Freezed data class &lt;code&gt;WindowSize&lt;/code&gt; which we use to store our Window Size Information. And we must do that to ensure when user resizes the window our app remembers to restore the screen to that size on next startup.&lt;/p&gt;

&lt;p&gt;Below is the &lt;code&gt;KVStoreService&lt;/code&gt; which we use to save stuff to &lt;code&gt;shared_preferences&lt;/code&gt; aka local key-value storage. This is also a Singleton and we use a separate class so we have single source of truth for all our key-value data entry and we don’t make mess using only key strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KVStoreService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;SharedPreferences&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_sharedPreferences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;SharedPreferences&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;sharedPreferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_sharedPreferences&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&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;initialize&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;_sharedPreferences&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;SharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;WindowSize&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;windowSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'windowSize'&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;raw&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&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;WindowSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&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;setWindowSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WindowSize&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'windowSize'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;jsonEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJson&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;Now finally, this is our actual &lt;code&gt;WindowManagerTools&lt;/code&gt; singleton service. &lt;code&gt;windowManager&lt;/code&gt; is also a singleton from the &lt;code&gt;window_manager&lt;/code&gt; package because currently Flutter only has a single window support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WindowManagerTools&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;WidgetsBindingObserver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Classic/idiomatic Dart way of implementing singletons&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;WindowManagerTools&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;WindowManagerTools&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_instance&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// We make the constructor private&lt;/span&gt;
  &lt;span class="n"&gt;WindowManagerTools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// This function must be called in the main.dart's main() function.&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&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;initialize&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&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="n"&gt;_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WindowManagerTools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Adding this WindowManagerTools singleton as a WidgetsBinding observer&lt;/span&gt;
    &lt;span class="c1"&gt;// to recieve life-cycle updates when window is resized or moved&lt;/span&gt;
    &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;waitUntilReadyToShow&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;WindowOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;Spotube&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;,"&lt;/span&gt;
        &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// minimum size must be set otherwise users can shrink the windows into a&lt;/span&gt;
        &lt;span class="c1"&gt;// single pixel. I don't think anyone wants add responsiveness down to&lt;/span&gt;
        &lt;span class="c1"&gt;// 1 pixel lmao.&lt;/span&gt;
        &lt;span class="nl"&gt;minimumSize:&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;titleBarStyle:&lt;/span&gt; &lt;span class="n"&gt;TitleBarStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// This hides the titlebar&lt;/span&gt;
        &lt;span class="c1"&gt;// Opens the app in Center of the Monitor/screen&lt;/span&gt;
        &lt;span class="nl"&gt;center:&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="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Restoring window size&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;savedSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KVStoreService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;windowSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setResizable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;savedSize&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;maximized&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isMaximized&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maximize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;savedSize&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savedSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&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;span class="c1"&gt;// To ignore same size events and avoid unnecessary resizing operations&lt;/span&gt;
  &lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_prevSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// This is why we extended from WidgetsBindingObserver which gives us &lt;/span&gt;
    &lt;span class="c1"&gt;// these life-cycle methods that we can listen to&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;didChangeMetrics&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="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;didChangeMetrics&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// We are ignoring mobile and web platforms&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;kIsMobile&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;kIsWeb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// windowManager.getSize gets the current size of the Window&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;size&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;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;windowSameDimension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;_prevSize&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_prevSize&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&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;windowSameDimension&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;_prevSize&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_prevSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;isMaximized&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;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isMaximized&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;KVStoreService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setWindowSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;WindowSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;maximized:&lt;/span&gt; &lt;span class="n"&gt;isMaximized&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;_prevSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;size&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;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;TitleBarStyle.hidden&lt;/code&gt;&lt;/strong&gt;: We hide the native title bar because we implement a custom one in Dart (more on this later)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window size persistence&lt;/strong&gt;: We save and restore window dimensions, including maximized state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimum size constraint&lt;/strong&gt;: Prevents UI breakdown on very small windows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observer pattern&lt;/strong&gt;: By extending &lt;code&gt;WidgetsBindingObserver&lt;/code&gt;, we listen to window resize events&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Multi-window can be implemented through &lt;a href="https://pub.dev/packages/desktop_multi_window" rel="noopener noreferrer"&gt;&lt;code&gt;desktop_multi_window&lt;/code&gt;&lt;/a&gt; package but that is not the focus of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Handling Close Behavior
&lt;/h3&gt;

&lt;p&gt;Here's where Spotube excels, we give users control over what happens when they click the &lt;em&gt;close button&lt;/em&gt;. In &lt;a href="https://github.com/KRTirtho/spotube/blob/3fb003ea60d90a8f8da7eb177bc747d4eb219199/lib/hooks/configurators/use_close_behavior.dart" rel="noopener noreferrer"&gt;&lt;strong&gt;lib/hooks/configurators/use_close_behavior.dart&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;useCloseBehavior&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WidgetRef&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;useWindowListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;onWindowClose:&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;preferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userPreferencesProvider&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;preferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closeBehavior&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;CloseBehavior&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minimizeToTray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;closeNotification&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows users to either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Close completely&lt;/strong&gt; - Exit the app immediately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimize to system tray&lt;/strong&gt; - Keep audio playing in the background&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is set in &lt;strong&gt;lib/main.dart&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kIsDesktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPreventClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ... other initialization&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;setPreventClose(true)&lt;/code&gt;, we intercept the close event before it propagates to the OS, giving us control over the behavior.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Now if you know, you might be thinking the above code looks like React as it is clearly react-hooks, right?? You got exactly right. Spotube uses &lt;a href="https://pub.dev/packages/flutter_hooks" rel="noopener noreferrer"&gt;flutter_hooks&lt;/a&gt; package in combination with riverpod’s &lt;a href="https://pub.dev/packages/hooks_riverpod" rel="noopener noreferrer"&gt;hooks_riverpod&lt;/a&gt; package. This reduces insane Flutter boilerplate code and helps focus on things that matter more. The &lt;code&gt;useWindowListener&lt;/code&gt; is also a custom hook that implement &lt;code&gt;window_manager&lt;/code&gt;’s &lt;code&gt;WindowListener&lt;/code&gt; interface and converts it into a simple callback based hook that can be used anywhere.&lt;br&gt;
Take a look at: &lt;a href="https://github.com/KRTirtho/spotube/blob/a9c78b786343bdc744745d884e59601a5834fd91/lib/hooks/configurators/use_window_listener.dart" rel="noopener noreferrer"&gt;lib\hooks\configurators\use_window_listener.dart&lt;/a&gt; for understanding what we’re doing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Platform-Specific Window Fixes
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://github.com/KRTirtho/spotube/blob/6cf4ca91793b95f0ec3393f3e7c4a7d0e2197f57/lib/hooks/configurators/use_fix_window_stretching.dart" rel="noopener noreferrer"&gt;&lt;strong&gt;lib/hooks/configurators/use_fix_window_stretching.dart&lt;/strong&gt;&lt;/a&gt; handles a quirky Windows issue where the Window content of the initial page stretches and causes weird layout that is not something a user should see.&lt;/p&gt;

&lt;p&gt;The bug is still not fixed to this day and is actually caused by the Flutter Window embedder in Windows that does not provide proper size information on the first frame&lt;/p&gt;

&lt;p&gt;The GitHub issue: &lt;a href="https://github.com/leanflutter/window_manager/issues/464" rel="noopener noreferrer"&gt;leanflutter/window_manager#464&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;To fix this, a simple fix was to resize the window by 1 pixel and that fixed the issue, a workaround.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;useFixWindowStretching&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;useEffect&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsWindows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&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;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;milliseconds:&lt;/span&gt; &lt;span class="mi"&gt;100&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;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSize&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;Size&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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;This must be called in all the route pages that can be an initial page. We call it in our root App instance. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Custom Title Bar Implementation 🎨
&lt;/h2&gt;

&lt;p&gt;Since we hide the native title bar, we implement a fully custom one. This gives us complete control over appearance while maintaining platform-specific behaviors.&lt;/p&gt;

&lt;h3&gt;
  
  
  The TitleBar Widget
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;TitleBar&lt;/code&gt; is an &lt;code&gt;AppBar&lt;/code&gt; replacement from Flutter Material. This widget integrates &lt;code&gt;window_manager&lt;/code&gt; and necessary paddings, gesutres and window buttons when the platform is desktop.&lt;br&gt;
&lt;/p&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;kTitlebarVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kIsWindows&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;kIsLinux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;/// We're using [HookConsumerWidget] which is a widget from hooks_riverpod package&lt;/span&gt;
&lt;span class="c1"&gt;/// that allows us to use hooks and also consume providers.&lt;/span&gt;
&lt;span class="c1"&gt;///&lt;/span&gt;
&lt;span class="c1"&gt;/// The [PreferredSizeWidget] interface as it is required by the Scaffold's appBar property,&lt;/span&gt;
&lt;span class="c1"&gt;/// which allows us to specify the preferred size of the title bar.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TitleBar&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;HookConsumerWidget&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;PreferredSizeWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;automaticallyImplyLeading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trailing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;leading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// small widget placed on top of title&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// small widget placed below title&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
      &lt;span class="n"&gt;trailingExpanded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// expand the trailing instead of the main content&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;AlignmentGeometry&lt;/span&gt; &lt;span class="n"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;leadingGap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;trailingGap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;EdgeInsetsGeometry&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;useSafeArea&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;surfaceBlur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;surfaceOpacity&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;TitleBar&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;automaticallyImplyLeading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trailing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;leading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trailingExpanded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;alignment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Alignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;leadingGap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trailingGap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;surfaceBlur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;surfaceOpacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;useSafeArea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Enabling dragging the window by dragging the titlebar support&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onDrag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WidgetRef&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// At Spotube, we even allow users to use a customer titlebar or native titlebar&lt;/span&gt;
    &lt;span class="c1"&gt;// Not every app needs to do this, but our philosophy is to give users the freedom&lt;/span&gt;
    &lt;span class="c1"&gt;// to customize their experience as much as possible&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;systemTitleBar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userPreferencesProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;systemTitleBar&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;kIsDesktop&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;systemTitleBar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startDragging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// a simple API to start dragging the window when the user drags the titlebar&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;hasLeadingOrCanPop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isNotEmpty&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;canPop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// We need to use useRef here to store the last clicked &lt;/span&gt;
    &lt;span class="c1"&gt;// time for double click detection. Because drag gesture&lt;/span&gt;
    &lt;span class="c1"&gt;// disables double click.&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;lastClicked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;millisecondsSinceEpoch&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;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scaling&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;LayoutBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;hasFullscreen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
              &lt;span class="n"&gt;MediaQuery&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sizeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;canPop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;leading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
              &lt;span class="n"&gt;automaticallyImplyLeading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;canPop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watchRouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;canPop&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;GestureDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;onHorizontalDragStart:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onDrag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nl"&gt;onVerticalDragStart:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onDrag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nl"&gt;onTapDown:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&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="c1"&gt;// If the user has enabled system title bar, we don't need to handle double click to maximize the window&lt;/span&gt;
              &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;systemTitlebar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="n"&gt;userPreferencesProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;systemTitleBar&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kIsDesktop&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;systemTitlebar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

              &lt;span class="c1"&gt;// Detect double click to maximize/unmaximize the window&lt;/span&gt;
              &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;currMills&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;millisecondsSinceEpoch&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;currMills&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lastClicked&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isMaximized&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unmaximize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maximize&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;lastClicked&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currMills&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="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;leading:&lt;/span&gt; &lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;mainAxisSize:&lt;/span&gt; &lt;span class="n"&gt;MainAxisSize&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="n"&gt;canPop&lt;/span&gt; &lt;span class="o"&gt;?&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;BackButton&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;leading&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;actions:&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;trailing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;// These are the custom titlebar buttons&lt;/span&gt;
                &lt;span class="n"&gt;Align&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;alignment:&lt;/span&gt; &lt;span class="n"&gt;Alignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;topRight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="nl"&gt;child:&lt;/span&gt;
                      &lt;span class="n"&gt;WindowTitleBarButtons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;foregroundColor:&lt;/span&gt; &lt;span class="n"&gt;foregroundColor&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="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"title,"&lt;/span&gt;
              &lt;span class="nl"&gt;header:&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;subtitle:&lt;/span&gt; &lt;span class="s"&gt;"subtitle,"&lt;/span&gt;
              &lt;span class="nl"&gt;trailingExpanded:&lt;/span&gt; &lt;span class="n"&gt;trailingExpanded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;alignment:&lt;/span&gt; &lt;span class="n"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="n"&gt;EdgeInsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;backgroundColor:&lt;/span&gt; &lt;span class="n"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;leadingGap:&lt;/span&gt; &lt;span class="n"&gt;leadingGap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;trailingGap:&lt;/span&gt; &lt;span class="n"&gt;trailingGap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scaling&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;surfaceBlur:&lt;/span&gt; &lt;span class="n"&gt;surfaceBlur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;surfaceOpacity:&lt;/span&gt; &lt;span class="n"&gt;surfaceOpacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;useSafeArea:&lt;/span&gt; &lt;span class="n"&gt;useSafeArea&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;child&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;withPadding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="c1"&gt;// This is where we handle macOS's titlebar buttons&lt;/span&gt;
                &lt;span class="c1"&gt;// On macOS, we can't have custom titlebar buttons, so we need to make&lt;/span&gt;
                &lt;span class="c1"&gt;// sure there is space for the platform buttons&lt;/span&gt;
                &lt;span class="nl"&gt;left:&lt;/span&gt; &lt;span class="n"&gt;kIsMacOS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hasFullscreen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hasLeadingOrCanPop&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// The preferred size of the title bar is 48 by default, but it can be customized by passing a height parameter.  &lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;preferredSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;48&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;&lt;strong&gt;Key features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drag to move&lt;/strong&gt;: &lt;code&gt;windowManager.startDragging()&lt;/code&gt; lets users drag the title bar to move the window&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Double-click to maximize&lt;/strong&gt;: Just like native title bars, double-clicking toggles maximize state&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Platform-aware macOS padding&lt;/strong&gt;: On macOS with fullscreen, we add extra padding for the traffic light buttons:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withPadding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;left:&lt;/span&gt; &lt;span class="n"&gt;kIsMacOS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hasFullscreen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hasLeadingOrCanPop&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;h3&gt;
  
  
  TitleBar Buttons
&lt;/h3&gt;

&lt;p&gt;You can see the &lt;code&gt;WindowTitleBarButtons&lt;/code&gt; buttons in our repository at: &lt;a href="https://github.com/KRTirtho/spotube/blob/9d2ad1c626d92bb6a054b75c859ae6d12c5c29e8/lib/components/titlebar" rel="noopener noreferrer"&gt;**lib\components\titlebar&lt;/a&gt;.** It uses &lt;code&gt;windowManager&lt;/code&gt; ’s different methods to show/hide, maximize/restore or minimize the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:shadcn_flutter/shadcn_flutter.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_hooks/flutter_hooks.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:hooks_riverpod/hooks_riverpod.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:shadcn_flutter/shadcn_flutter_extension.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:spotube/components/hover_builder.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:spotube/components/titlebar/titlebar.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:spotube/components/titlebar/titlebar_icon_buttons.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:spotube/hooks/configurators/use_window_listener.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:spotube/provider/user_preferences/user_preferences_provider.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:spotube/utils/platform.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:titlebar_buttons/titlebar_buttons.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:window_manager/window_manager.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WindowTitleBarButtons&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;HookConsumerWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;foregroundColor&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;WindowTitleBarButtons&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;preferences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userPreferencesProvider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;isMaximized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ThemeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&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;onClose&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// The useWindowListener we talked about earlier came in handy&lt;/span&gt;
    &lt;span class="n"&gt;useWindowListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;onWindowMaximize:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&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="nl"&gt;onWindowUnmaximize:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;useEffect&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;kIsDesktop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isMaximized&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;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;kTitlebarVisible&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;preferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;systemTitleBar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;SizedBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shrink&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Windows specific buttons&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;kIsWindows&lt;/span&gt;&lt;span class="p"&gt;)&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;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;crossAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;CrossAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="n"&gt;ShadcnWindowButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;MinimizeIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;colorScheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minimize&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;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;ShadcnWindowButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;MaximizeIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;colorScheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maximize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="n"&gt;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;ShadcnWindowButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;RestoreIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;colorScheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unmaximize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="n"&gt;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;HoverBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isHovered&lt;/span&gt;&lt;span class="p"&gt;)&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;ShadcnWindowButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="n"&gt;CloseIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;isHovered&lt;/span&gt;
                    &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;white&lt;/span&gt;
                    &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;colorScheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;foreground&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="n"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nl"&gt;hoverBackgroundColor:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xFFD32F2F&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;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// These are linux specific buttons&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;crossAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;CrossAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;DecoratedMinimizeButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;type:&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minimize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;DecoratedMaximizeButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;type:&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isMaximized&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unmaximize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
              &lt;span class="n"&gt;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;windowManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maximize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
              &lt;span class="n"&gt;isMaximized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;DecoratedCloseButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;type:&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="n"&gt;onClose&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;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;Windows uses custom in house components that we had to create. And Linux actually uses a package &lt;code&gt;titlebar_buttons&lt;/code&gt; which gives accurate titlebar buttons based on dbus config in Linux for different desktop environment, like GTK, Gnome, Cinnamon, KDE etc flavors.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wondering about &lt;em&gt;shadcn&lt;/em&gt;? (Smirk) we don’t use material anymore. Spotube uses &lt;code&gt;shadcn_flutter&lt;/code&gt; which is a shadncn implementation in Flutter based on material.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion: Bringing It All Together
&lt;/h2&gt;

&lt;p&gt;Building professional desktop experiences in Flutter isn't about individual features in isolation, it's about how they work together seamlessly. At Spotube, window management, custom title bars, and platform-specific implementations form the foundation of a polished desktop application.&lt;/p&gt;

&lt;p&gt;The key insight we've learned over years of development is this: &lt;strong&gt;respect the platform conventions while maintaining a unified codebase&lt;/strong&gt;. Windows users expect native window buttons and specific click behaviors. macOS users expect traffic light buttons in the right positions. Linux users expect their desktop environment's native styling. By handling these details thoughtfully, we create an application that feels native on each platform, not just a "web app in a window."&lt;/p&gt;

&lt;h3&gt;
  
  
  What We've Covered
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Window Management&lt;/strong&gt;: Persisting state, handling lifecycle events, and working around platform-specific quirks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Title Bars&lt;/strong&gt;: Implementing native interactions (drag to move, double-click to maximize) with platform-aware positioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform Integration&lt;/strong&gt;: Using the right tools and patterns for Windows, macOS, and Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Moving Forward
&lt;/h3&gt;

&lt;p&gt;The Flutter community has done remarkable work filling the gaps in Flutter's desktop support. Packages like &lt;code&gt;window_manager&lt;/code&gt;, &lt;code&gt;titlebar_buttons&lt;/code&gt;, and &lt;code&gt;shadcn_flutter&lt;/code&gt; make it possible to build truly professional desktop applications. The patterns we've shared at Spotube, singletons for services, custom hooks for state management, platform-aware configuration, are battle-tested and ready for your own projects.&lt;/p&gt;

&lt;p&gt;If you're building a desktop app with Flutter, remember: the small details matter. Users notice when windows restore their previous size, when title bars respond naturally to interactions, and when your app feels like it belongs on their platform.&lt;/p&gt;

&lt;p&gt;Happy building! 🚀&lt;/p&gt;




&lt;p&gt;Follow the full 'Flutter Desktop Mastery' series on the official blog: &lt;a href="https://spotube.krtirtho.dev/blog/flutter-desktop-mastery/part-1-window-management/" rel="noopener noreferrer"&gt;https://spotube.krtirtho.dev/blog/flutter-desktop-mastery/part-1-window-management/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>desktop</category>
      <category>windowmanagement</category>
    </item>
    <item>
      <title>It's okay to not to code in your free time in your personal projects</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Tue, 26 Dec 2023 15:25:21 +0000</pubDate>
      <link>https://forem.com/krtirtho/you-should-stop-writing-code-right-now-2ap7</link>
      <guid>https://forem.com/krtirtho/you-should-stop-writing-code-right-now-2ap7</guid>
      <description>&lt;p&gt;Hello! Merry Christmas!&lt;/p&gt;

&lt;p&gt;If you’re reading this, you’re probably a developer. There’s a chance you love to code. You love coding so much that even in holidays, you work on your personal projects that you’ll definitely not going to abandon in the next month.&lt;/p&gt;

&lt;p&gt;For most of the developers, programming is passion. It’s often a hobby for one that the person can also turn into a profession. &lt;strong&gt;70.42%&lt;/strong&gt; of developers say they code as a hobby, according to &lt;a href="https://survey.stackoverflow.co/2023/#:~:text=Hobby-,70.42%25,-Professional%20development%20or" rel="noopener noreferrer"&gt;stackoverflow survey&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fp14190380708eqp25mcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fp14190380708eqp25mcr.png" alt="Stackoverflow survey" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But this hobby + passion + carrier, just like any other profession/leisure work, has its own toll on you. You may think you’re enjoying your personal projects, but it is possible you’re overworking, especially if you’re also a professional programmer. We’ll all like working on new projects that we envision to succeed. But unsurprisingly, many of them are mirages. But at least we can from our mistakes.&lt;/p&gt;

&lt;p&gt;Often times, developers create personal projects that solve a problem that the individual and many people are facing in the community. It’s like making a missing piece of a puzzle. These projects most of the time turns into a big open source project. And now all of a sudden you’re a maintainer of an open source project. But this is good. This is something we (excluding you) all want. But do you want it? &lt;/p&gt;

&lt;p&gt;Maintaining an Open Source project, doing a job, studying (if you’re a student), facing + implementing + closing feature requests and bug requests, these are tedious work. Also, at this point we become so obsessed with our project that we often neglect health, close ones and even the carrier/studies. So the best way to avoid this is not doing it alone. Ask for help from others. Create a community and a moderation group and share your project with them.  It’ll make everything better for everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take weekly breaks
&lt;/h2&gt;

&lt;p&gt;Personal health is above anything. No passion, profession is not worth more than your life. It’s normal, when we’re passionate about something, we want to do that all the time. But this is when we burned out. Stress and over excitement steers us to worse health. So take a break from this. Separate yourself from your personal projects and if possible your job. Go on vacation or hiking or cycling. Chat with friends in a BBQ party or just enjoy your life. &lt;/p&gt;

&lt;h2&gt;
  
  
  Take daily breaks
&lt;/h2&gt;

&lt;p&gt;A job, personal projects, studying/parenting altogether makes anyone’s life a hell. So no matter what, make sure to have personal time every day. After a long period of work, don’t just straight go and work on your personal projects. Take some rest, eat and enjoy for a while and then in the remaining 1–2 hours work on those projects. Or if you’re too passionate about your projects, do a part-time job and work on your personal projects in the remaining hours. But make sure your projects are profitable so you won’t be broke.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I know it’s a pretty shitty article. Although I wrote it myself, It's still worse than an AI. But I recently faced burn-outs, stress, exam stress, result stress and a total breakdown basically. The only thing helped me recover was touching the grass and forgetting about this side of my life for a while. Cycling, playing football (soccer) helped me a lot too. And, obviously, a group of good friends.&lt;/p&gt;

&lt;p&gt;So go out, forget about programming. And enjoy the remaining days of this terrible year of 2023. Hopefully, 2024 will be better (it usually gets worse when you hope for better next year, so this time I’m hopeless for 2024 tbh)&lt;/p&gt;

&lt;h2&gt;
  
  
  Support Me
&lt;/h2&gt;

&lt;p&gt;You don’t have to support me, but in case you’re Santa Claus,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/krtirtho" rel="noopener noreferrer"&gt;&lt;img alt="Buy me a Coffee" height="56" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.jsdelivr.net%2Fnpm%2F%40intergrav%2Fdevins-badges%403%2Fassets%2Fcozy%2Fdonate%2Fbuymeacoffee-singular_vector.svg" width="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://patreon.com/krtirtho" rel="noopener noreferrer"&gt;&lt;img alt="Support me on Patron" height="56" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.jsdelivr.net%2Fnpm%2F%40intergrav%2Fdevins-badges%403%2Fassets%2Fcozy%2Fdonate%2Fpatreon-singular_vector.svg" width="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Find Me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://krtirtho.dev" rel="noopener noreferrer"&gt;krtirtho.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/KRTirtho" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/krtirtho" rel="noopener noreferrer"&gt;Twitter | X&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>JavaScript Intl API: The hidden gem of web internationalization</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Sun, 13 Aug 2023 06:22:53 +0000</pubDate>
      <link>https://forem.com/krtirtho/javascript-intl-api-the-hidden-gem-of-web-internationalization-2cd7</link>
      <guid>https://forem.com/krtirtho/javascript-intl-api-the-hidden-gem-of-web-internationalization-2cd7</guid>
      <description>&lt;p&gt;Lots of time us web devs try to explore multiple libraries/frameworks for solution to the biggest reason of debt "Internationalization". But we often overlook the default browser APIs and how capable these are. JavaScript's &lt;code&gt;Intl&lt;/code&gt; API is one of those hidden gems. If you use &lt;code&gt;Intl&lt;/code&gt; half of the time you can avoid those date, currency etc. formatting &amp;amp; parsing libraries which are huge impact on the bundle size sometimes. The advantage is that the API is designed to be locale agnostic so with internationalization we're also getting localization features&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" rel="noopener noreferrer"&gt;Learn the difference between internationalization &amp;amp; localization&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Formatting Dates
&lt;/h3&gt;

&lt;p&gt;Who doesn't remember the days of moment.js. Thank God date-fns and dayjs like libs helped us getting of that bundle of latency, Phew! But dates can be tricky to format consistently across different locales and require heavy libraries to do so&lt;br&gt;
So the &lt;code&gt;Intl.DateTimeFormat&lt;/code&gt; object saves the day. By specifying locale-specific options, you can even present dates in a format that's both readable and familiar to users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: August 13, 2023&lt;/span&gt;

&lt;span class="c1"&gt;// or if you give bengali locale&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedBnDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bn-BD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedBnDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: ১৩ আগস্ট, ২০২৩&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Relative Time formatting
&lt;/h3&gt;

&lt;p&gt;Relative times are a better UX. It lets the user know the time in an instant. But formatting relative time is a trouble &amp;amp; datetime libraries provides us with an easy way to do so. But did you know &lt;code&gt;Intl&lt;/code&gt; already has an API that does the exact same job &amp;amp; also respects locale. Crazy, right! See this official example from MDN&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rtf1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RelativeTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;always&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rtf1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;quarter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Expected output: "in 3 qtrs."&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rtf1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Expected output: "1 day ago"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rtf2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RelativeTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rtf2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;day&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Expected output: "pasado mañana"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Number Formatting across Locales
&lt;/h3&gt;

&lt;p&gt;Displaying numbers in a way that matches each user's language and cultural norms is crucial. The &lt;code&gt;Intl.NumberFormat&lt;/code&gt; object allows you to achieve this effortlessly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;12345.6789&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bn-BD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: ১২,৩৪৫.৬৭৯&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ar-SA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: ١٢٬٣٤٥٫٦٧٩&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Currency Formatting
&lt;/h3&gt;

&lt;p&gt;There's libraries that can handle currency formatting. But the &lt;code&gt;Intl.NumberFormat&lt;/code&gt; API steps up by providing built-in currency formatting eliminating the need for third party libs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1234.56&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedCurrency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;currency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedCurrency&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: $1,234.56&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pluralization
&lt;/h3&gt;

&lt;p&gt;The rules for pluralization vary across languages, and the &lt;code&gt;Intl.PluralRules&lt;/code&gt; object helps you manage this intricacy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;enOrdinalRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PluralRules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ordinal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;suffixes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;one&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;st&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;two&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;few&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;other&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;th&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formatOrdinals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;enOrdinalRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;suffixes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '0th'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '1st'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '2nd'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '3rd'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '4th'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '11th'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '21st'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '42nd'&lt;/span&gt;
&lt;span class="nf"&gt;formatOrdinals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;103&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '103rd'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Believe me before &lt;code&gt;Intl.PluralRules&lt;/code&gt;, we needed to utilize a algorithm to stuff like this&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Sorting Strings Globally
&lt;/h3&gt;

&lt;p&gt;JavaScript alphanumeric sorting is a jock which everyone knows. The &lt;code&gt;Array.sort&lt;/code&gt; works for mostly numbers. For alphanumeric strings (strings that include both numbers &amp;amp; letters) we need to do some manual works before so it acts correctly &lt;/p&gt;

&lt;p&gt;For example with sort:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;50&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hundred&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;localeCompare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 200, 50, good, hundred&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix this we need &lt;code&gt;Intl.Collator&lt;/code&gt; with &lt;code&gt;numeric: true&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt;  &lt;span class="nx"&gt;collator&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt;  &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt;  &lt;span class="nx"&gt;strings&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;50&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hundred&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="nx"&gt;collator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 50, 200, good, hundred&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But i doesn't stop there. Sorting strings can be surprisingly tricky due to varying language rules also. The &lt;code&gt;Intl.Collator&lt;/code&gt; object comes to the rescue by ensuring your sorting stays true to the user's locale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;banana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cherry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es-ES&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sortedStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sortedStrings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: [ 'banana', 'cherry', 'apple' ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Easy Time Zones
&lt;/h3&gt;

&lt;p&gt;Dealing with time zones can be complex, but the &lt;code&gt;Intl.DateTimeFormat&lt;/code&gt; API simplifies it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;long&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;timeZoneName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: August 13, 2023, GMT+2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Embracing User Locale
&lt;/h3&gt;

&lt;p&gt;Understanding your users' preferred language and formatting preferences is key. The &lt;code&gt;Intl.Locale&lt;/code&gt; object streamlines this process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userLocale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userLocale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: en&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userLocale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// Output: US&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By mastering the &lt;code&gt;Intl&lt;/code&gt; API, you can create applications that bridge cultural gaps and resonate with a diverse global audience &amp;amp; fundamentally increases your revenue. I hope this guide provides you with insights that can transform your internationalization efforts into a seamless and user-centric experience.&lt;br&gt;
But for full understanding of the API and mastering it visit the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl" rel="noopener noreferrer"&gt;official MDN docs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My socials:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://x.com/@KrTirtho" rel="noopener noreferrer"&gt;X&lt;/a&gt; (Formerly Twitter)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/KRTirtho" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/KRTirtho"&gt;DEVCommunity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Support me &amp;amp; my work
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://patreon.com/krtirtho" rel="noopener noreferrer"&gt;&lt;img alt="Support me on Patron" height="56" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.jsdelivr.net%2Fnpm%2F%40intergrav%2Fdevins-badges%403%2Fassets%2Fcozy%2Fdonate%2Fpatreon-singular_vector.svg" width="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/krtirtho" rel="noopener noreferrer"&gt;&lt;img alt="Buy me a Coffee" height="56" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.jsdelivr.net%2Fnpm%2F%40intergrav%2Fdevins-badges%403%2Fassets%2Fcozy%2Fdonate%2Fbuymeacoffee-singular_vector.svg" width="209"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Rust based alternatives for Unix &amp; GNU Terminal Tools (Quick Table)</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Thu, 20 Oct 2022 16:13:23 +0000</pubDate>
      <link>https://forem.com/krtirtho/rust-based-alternatives-for-unix-gnu-terminal-tools-quick-table-jpf</link>
      <guid>https://forem.com/krtirtho/rust-based-alternatives-for-unix-gnu-terminal-tools-quick-table-jpf</guid>
      <description>&lt;p&gt;This is the short (Quick Table) article version of &lt;a href="https://dev.to/krtirtho/rust-ify-linuxgnumacos-terminal-tools-40c6"&gt;https://dev.to/krtirtho/rust-ify-linuxgnumacos-terminal-tools-40c6&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we’re developers, and we barely have time, so I’m starting this new kind of Article format where instead of traditionally showing tricks and tips or anything in a List order with stupid unneeded description, everything will be inside a Table or be carefully structured&lt;/p&gt;

&lt;p&gt;I figured this way, most readers will spend way less time compared to my previous Articles. So I’m starting a never ending series of &lt;strong&gt;“Quick Table”&lt;/strong&gt; articles&lt;/p&gt;

&lt;p&gt;Behold the 2-minutes table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Unix/GNU tools&lt;/th&gt;
&lt;th&gt;Rust tools&lt;/th&gt;
&lt;th&gt;Short description (Biggest features)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cd&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ajeetdsouza/zoxide" rel="noopener noreferrer"&gt;zoxide&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;remembers which directories you use most frequently, so you can "jump" to them in just a few keystrokes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ls&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ogham/exa" rel="noopener noreferrer"&gt;exa&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;uses colors to distinguish file types and metadata. It knows about symlinks, extended attributes, and Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;grep&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;ripgrep&lt;/a&gt; (Dude got humor XD)&lt;/td&gt;
&lt;td&gt;line-oriented search tool that recursively searches the current directory for a regex pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cat&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;bat&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;with syntax highlighting and Git integration and line numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;top&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/ClementTsang/bottom" rel="noopener noreferrer"&gt;bottom🙃&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;customizable cross-platform graphical process/system monitor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sed&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/chmln/sd" rel="noopener noreferrer"&gt;sd&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;uses regex syntax that you already know from JavaScript and Python. Forget about dealing with quirks of sed  or awk   - get productive immediately. Find &amp;amp; replace expressions are split up, so easy to read and write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cloc&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/XAMPPRocky/tokei" rel="noopener noreferrer"&gt;tokei&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;shows the number of files, total lines within those files and code, comments, and blanks grouped by language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;find&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;fd&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;has an intuitive syntax and supports Regular expressions (default) and glob-based patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;du&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/bootandy/dust" rel="noopener noreferrer"&gt;dust&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;shows the disk usage more efficiently, fast and in a more organized way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ps&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/dalance/procs" rel="noopener noreferrer"&gt;procs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;colored and human-readable outputs. Supports Multi-column keyword search. Additionally, it shows TCP/UDP port, read/write throughput, docker container name, more memory information too&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bash/zsh/powershell&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/nushell/nushell" rel="noopener noreferrer"&gt;nushell&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;thinks files and data as raw streams of text and looks at each input as something with structure and many more&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you like this new format, let me know, so I’ll keep on making more in this way&lt;/p&gt;

&lt;p&gt;You can always support me for more 😊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/krtirtho" rel="noopener noreferrer"&gt;&lt;img src="https://media2.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%2F74avki0qfk5462ju6gbn.png" alt="Buy me a coffe" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Social
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://twitter.com/@KrTirtho" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/kingkor-roy-tirtho-810b951b4/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/KRTirtho" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.to/KRTirtho"&gt;DEVCommunity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://medium.com/@krtirtho" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>rust</category>
      <category>beginners</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Rust-ify Linux/GNU/macOS terminal tools</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Mon, 17 Oct 2022 17:00:31 +0000</pubDate>
      <link>https://forem.com/krtirtho/rust-ify-linuxgnumacos-terminal-tools-40c6</link>
      <guid>https://forem.com/krtirtho/rust-ify-linuxgnumacos-terminal-tools-40c6</guid>
      <description>&lt;p&gt;Unix tools, generally written in C, are an exceptionally well-designed and maintained tool set for regular day-to-day terminal usage. Although these traditional GNU and Unix amazing, they are nowadays not enough and over 40-50 years old. So those were not designed keeping the modern computers &amp;amp; software in their mind. We need more cool fancier and feature rich tools that save time and also make us look savage&lt;/p&gt;

&lt;h2&gt;
  
  
  Rust to the saving
&lt;/h2&gt;

&lt;p&gt;Rust, slowly taking place into the realm of modern computing and crazy terminal space, actually has some cool and brilliant solutions for the old GNU and Unix based CLI problems. Although it might sound weird and out of the world, but surprisingly there’s a Rust based equivalent (mostly upgrade) for every single basic CLI (Command Line Interface) tools in Linux/macOS. This kind of thing unseen in the days of computing&lt;/p&gt;

&lt;p&gt;Enough goof and trash talk (I hate myself for this), let’s see all gems now&lt;/p&gt;

&lt;p&gt;We’ll replace all the native terminal commands with their Rust equivalent using the &lt;code&gt;alias&lt;/code&gt; property of the shell&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;cd&lt;/code&gt; replacement - &lt;a href="https://github.com/ajeetdsouza/zoxide" rel="noopener noreferrer"&gt;Zoxide&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A smarter cd command. Supports all major shells. &lt;/p&gt;

&lt;p&gt;It remembers which directories you use most frequently, so you can "jump" to them in just a few keystrokes.&lt;/p&gt;

&lt;p&gt;So if you go to a directory e.g &lt;code&gt;/home/batman/03842034sakdsuperrmanbadasd21/secret-identity&lt;/code&gt; once, the second you can just type &lt;code&gt;z secret-identity&lt;/code&gt; it’ll directly navigate to that same directory&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fdt8j9m1b6epp29xj275m.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fdt8j9m1b6epp29xj275m.webp" alt="https://github.com/ajeetdsouza/zoxide/raw/main/contrib/tutorial.webp" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let’s bury &lt;code&gt;cd&lt;/code&gt; with &lt;code&gt;zoxide&lt;/code&gt;. Just do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias cd=z&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;cd ./&amp;lt;any-directory&amp;gt;&lt;/code&gt; and live happily every after&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ls&lt;/code&gt; replacement - &lt;a href="https://github.com/ogham/exa" rel="noopener noreferrer"&gt;Exa&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;exa is a modern replacement for the venerable file-listing command-line program &lt;code&gt;ls&lt;/code&gt; that ships with Unix and Linux operating systems, giving it more features and better defaults. It uses colors to distinguish file types and metadata. It knows about symlinks, extended attributes, and Git&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F30p3nevt63lsppp07337.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F30p3nevt63lsppp07337.png" alt="https://github.com/ogham/exa/raw/master/screenshots.png" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s &lt;em&gt;unlist&lt;/em&gt; &lt;code&gt;ls&lt;/code&gt; with &lt;code&gt;exa&lt;/code&gt;. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias ls=exa --icons --colors=always&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;ls &amp;lt;any-directory&amp;gt;&lt;/code&gt; and live a colorful life&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;grep&lt;/code&gt; replacement - &lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;Rip grep&lt;/a&gt; (Dude got humor XD)
&lt;/h3&gt;

&lt;p&gt;ripgrep is a line-oriented search tool that recursively searches the current directory for a regex pattern. By default, ripgrep will respect gitignore rules and automatically skip hidden files/directories and binary files. In other word, RIP grep&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6x01vusxjsy128hzt9n7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6x01vusxjsy128hzt9n7.png" alt="https://burntsushi.net/stuff/ripgrep1.png" width="769" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s execute (kill)  &lt;code&gt;grep&lt;/code&gt; and repalce with &lt;code&gt;ripgrep&lt;/code&gt;. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias grep=rg&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;grep Life &amp;lt;any-file&amp;gt;&lt;/code&gt; and … (are you even a human?)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;cat&lt;/code&gt; replacement - &lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;Bat&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A cat replacement with syntax highlighting and Git integration and line numbers&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cat has nine lives.&lt;/em&gt; The &lt;code&gt;cat&lt;/code&gt; command has exactly 10 replacement and &lt;code&gt;bat&lt;/code&gt; is the 10th attempt at making a better &lt;code&gt;cat&lt;/code&gt; (character at)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fp3n0pc4uvesj6q5g0ei4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fp3n0pc4uvesj6q5g0ei4.png" alt=" " width="656" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's kick off the ungrateful &lt;code&gt;cat&lt;/code&gt; and replace it with &lt;code&gt;bat&lt;/code&gt;. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias cat=bat&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;cat ./&amp;lt;any-file&amp;gt;&lt;/code&gt; and fly out in the night sky and sleep in the day&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;top&lt;/code&gt; repalcement - &lt;a href="https://github.com/ClementTsang/bottom" rel="noopener noreferrer"&gt;bottom🙃&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Yet another cross-platform graphical process/system monitor. It’s a customizable cross-platform graphical process/system monitor for the terminal. No matter what system stats/monitor tool anyone makes/writes, it’d still be better than &lt;code&gt;top&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F6wug26xgb8cnnxjw3qnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6wug26xgb8cnnxjw3qnf.png" alt=" " width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's remove &lt;code&gt;top&lt;/code&gt; from the top and put &lt;code&gt;bottom&lt;/code&gt; there&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias top=bottom&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;bottom&lt;/code&gt; and find out why Chromes taking the 99% of RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;See kids, anyone(tool) can go from bottom to top&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;sed&lt;/code&gt; replacement - &lt;a href="https://github.com/chmln/sd" rel="noopener noreferrer"&gt;Sd&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;sd&lt;/code&gt; is an intuitive find &amp;amp; replace CLI and the &lt;code&gt;sed&lt;/code&gt; butcher. &lt;code&gt;sd&lt;/code&gt; uses regex syntax that you already know from JavaScript and Python. Forget about dealing with quirks of &lt;code&gt;sed&lt;/code&gt; or &lt;code&gt;awk&lt;/code&gt;  - get productive immediately. It’s find &amp;amp; replace expressions are split up, which makes them easy to read and write. No more messing with unclosed and escaped slashes&lt;/p&gt;

&lt;p&gt;Now, let's replace &lt;code&gt;sed&lt;/code&gt; with &lt;code&gt;sd&lt;/code&gt;. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias sed=sd&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Example: type &lt;code&gt;echo 'lots((([]))) of special chars' | sed -s '((([])))' ''&lt;/code&gt; and you’ll get the output as &lt;code&gt;lots of special chars&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;cloc&lt;/code&gt; repalcement - &lt;a href="https://github.com/XAMPPRocky/tokei" rel="noopener noreferrer"&gt;Tokei&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Tokei is a program that displays statistics about your code. Tokei will show the number of files, total lines within those files and code, comments, and blanks grouped by language. It’s a great replacement for cloc (count lines of code)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F9qzlvfmuit49cnyxrk1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F9qzlvfmuit49cnyxrk1g.png" alt="https://user-images.githubusercontent.com/61944859/196231304-06031dbb-9a29-4160-b7cc-c5f67f493b70.png" width="663" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's count &lt;code&gt;cloc&lt;/code&gt; off and use tokei instead. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias cloc=tokei&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;tokei ./&lt;/code&gt; and it’ll output 0 lines of code ‘cause you didn’t write any&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;find&lt;/code&gt; replacement - &lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;Fd&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;fd&lt;/code&gt;  is a program to find entries in your file system. It is a simple, fast and user-friendly alternative to &lt;code&gt;find&lt;/code&gt;. While it does not aim to support all of &lt;code&gt;find&lt;/code&gt;'s powerful functionality, it provides sensible (opinionated) defaults for a majority of use cases.&lt;/p&gt;

&lt;p&gt;It has an intuitive syntax: &lt;code&gt;fd PATTERN&lt;/code&gt; instead of &lt;code&gt;find -iname '*PATTERN*'&lt;/code&gt;. Supports Regular expression (default) and glob-based patterns. Superfast due to parallelized directory traversal. Uses colors to highlight different file types (same as &lt;code&gt;ls&lt;/code&gt;). Ignores hidden directories, files &amp;amp; patterns from your&lt;code&gt;.gitignore&lt;/code&gt;, by default. AND The command name is &lt;em&gt;50%&lt;/em&gt; shorter&lt;a href="https://github.com/ggreer/the_silver_searcher" rel="noopener noreferrer"&gt;&lt;/a&gt; than &lt;code&gt;find&lt;/code&gt; :-)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F01r3j3u6pw7w6w06zeqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F01r3j3u6pw7w6w06zeqf.png" alt=" " width="620" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s forget, &lt;code&gt;find&lt;/code&gt; and replace it with &lt;code&gt;fd&lt;/code&gt;. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias find=fd&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;find useful-files&lt;/code&gt; and forget&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;du&lt;/code&gt; repalcement - &lt;a href="https://github.com/bootandy/dust" rel="noopener noreferrer"&gt;Dust&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;du + rust = dust. Like du but more intuitive. &lt;code&gt;dust&lt;/code&gt; shows the disk usage more efficiently, fast and in a more organized way&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F2zou6mfb406lbn8iz5yz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F2zou6mfb406lbn8iz5yz.png" alt="https://github.com/bootandy/dust/raw/master/media/snap.png" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's throw &lt;code&gt;du&lt;/code&gt; to dust and replace it with &lt;code&gt;dust&lt;/code&gt;. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias du=dust&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;du&lt;/code&gt; and see the reason why your disk space at 69% (I know it’s all Pokémon movies)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ps&lt;/code&gt; replacement - &lt;a href="https://github.com/dalance/procs" rel="noopener noreferrer"&gt;Procs&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;procs is a modern replacement for ps. It has colored and human-readable outputs. Supports Multi-column keyword search. Additionally, it shows TCP/UDP port, read/write throughput, docker container name, more memory information too which &lt;code&gt;ps&lt;/code&gt; can’t show&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fw10z3s4s33wuy99zz185.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fw10z3s4s33wuy99zz185.png" alt="https://user-images.githubusercontent.com/4331004/55446625-5e5fce00-55fb-11e9-8914-69e8640d89d7.png" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, lets SIGKILL &lt;code&gt;ps&lt;/code&gt; and replace it with &lt;code&gt;procs&lt;/code&gt;. Just do following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;/&lt;code&gt;~/.zshrc&lt;/code&gt;/&lt;code&gt;~/. config/fish/config.fish&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add &lt;code&gt;alias ps=procs&lt;/code&gt; at the end of the file and save the file&lt;/li&gt;
&lt;li&gt;close and open the terminal or run &lt;code&gt;source &amp;lt;config-file-path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;type &lt;code&gt;ps&lt;/code&gt; and to exit, press &lt;code&gt;q&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;bash&lt;/code&gt; repalcement (more like powershell) - &lt;a href="https://github.com/nushell/nushell" rel="noopener noreferrer"&gt;Nushell&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A new type of shell. It’s an alternative shell, quite different compared to bash, zsh or fish like terminals. Instead of thinking of files and data as raw streams of text, Nu looks at each input as something with structure. For example, when you list the contents of a directory what you get back is a table of rows, where each row represents an item in that directory. These values can be piped through a series of steps, in a series of commands called a 'pipeline'. (~ copied from the official doc)&lt;/p&gt;

&lt;p&gt;It supports plugins which interacts with Nushell using JSON-IPC. Plugins are basically binaries available in the path and follows the &lt;code&gt;nu_plugin_*&lt;/code&gt; naming convention&lt;/p&gt;

&lt;p&gt;Nushell is extremely powerful and flexible and brings organization to the outputs. It structures the output in an intuitive and human-readable way. Nushell is basically the non-corporate open-source terminal, but keeping the UNIX spirit. It’s more like a PowerShell replacement. But in Linux/macOS it shines bright too. It needs more adaptation and updates and let's keep our finger crossed hoping it’ll one day become the top shell of all time&lt;/p&gt;

&lt;p&gt;Nushell supports all major platforms (Linux, Windows, macOS). Now install it and then run &lt;code&gt;chsh -s $(which nu)&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;sudo&lt;/code&gt; it should work. If you use &lt;code&gt;sudo&lt;/code&gt; it will change the shell not for your working user but &lt;strong&gt;for root.&lt;/strong&gt; Finally, log out of your computer and log back in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fnushell%2Fnushell%2Fraw%2Fmain%2Fimages%2Fnushell-autocomplete6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fnushell%2Fnushell%2Fraw%2Fmain%2Fimages%2Fnushell-autocomplete6.gif" alt="https://github.com/nushell/nushell/raw/main/images/nushell-autocomplete6.gif" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What? You want more?
&lt;/h2&gt;

&lt;p&gt;All the configurations at once for replacing the GNU/Unix tools. Put this on &lt;code&gt;~/.bashrc&lt;/code&gt; / &lt;code&gt;~/.zshrc&lt;/code&gt; etc shell configuration files and restart your Terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias cd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;z
&lt;span class="nb"&gt;alias ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exa &lt;span class="nt"&gt;--icons&lt;/span&gt; &lt;span class="nt"&gt;--colors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always
&lt;span class="nb"&gt;alias cat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bat
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;find&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fd
&lt;span class="nb"&gt;alias grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rg
&lt;span class="nb"&gt;alias du&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dust
&lt;span class="nb"&gt;alias sed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sd
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;cloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tokei
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;procs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, you can always support me for more 😊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/krtirtho" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee" width="545" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Social
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://twitter.com/@KrTirtho" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/kingkor-roy-tirtho-810b951b4/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/KRTirtho" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.to/KRTirtho"&gt;DEVCommunity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://medium.com/@krtirtho" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>terminal</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Fl-Query⚡ for Flutter | Managing async data &amp; mutations in Flutter is like a breeze now🌬️</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Wed, 13 Jul 2022 09:32:06 +0000</pubDate>
      <link>https://forem.com/krtirtho/fl-query-for-flutter-managing-async-data-mutations-in-flutter-is-like-a-breeze-now-2c04</link>
      <guid>https://forem.com/krtirtho/fl-query-for-flutter-managing-async-data-mutations-in-flutter-is-like-a-breeze-now-2c04</guid>
      <description>&lt;p&gt;If you’re familiar with &lt;a href="http://flutter.dev" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;, you’re definitely familiar with &lt;code&gt;FutureBuilder&lt;/code&gt;. After all this is the only way you can fetch data in Flutter with proper state, or is it? But you might’ve noticed every time the Widget gets rebuild the Future is run along too unless you declare your &lt;code&gt;Future&lt;/code&gt; in &lt;code&gt;initState&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But even if you declare your future in &lt;code&gt;initState&lt;/code&gt; to avoid reruns across rebuilds it will still rerun when the Widget gets disposed &amp;amp; mounted again. So what’s the solution? Ans: &lt;code&gt;FutureProvider&lt;/code&gt; from &lt;a href="http://riverpod.dev" rel="noopener noreferrer"&gt;riverpod&lt;/a&gt; or &lt;a href="https://pub.dev/packages/provider" rel="noopener noreferrer"&gt;provider&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hold on a sec, but it’s not another riverpod/provider article? No, it's not.&lt;/p&gt;

&lt;p&gt;Yeah, to cache your future results or server response in you can use &lt;code&gt;FutureProvider&lt;/code&gt;. It can store &amp;amp; distribute the result all across the application without multiple reruns of the same operation. But what happens when the Data becomes expired or stale? What if your app won’t need that data after some time but the data still wasting RAM?&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Fl-Query&lt;/strong&gt; comes to play. It’s an &lt;em&gt;Async Data + Mutation Manager for Flutter that caches, fetches, automatically refetches stale data.&lt;/em&gt; Similar to &lt;a href="http://tanstack.com/query" rel="noopener noreferrer"&gt;Tanstack Query&lt;/a&gt; in the World of web development, but only the concept is implemented &amp;amp; The API is very similar to what Flutter Developers are used to &amp;amp; so it makes everyone feel like home&lt;/p&gt;

&lt;p&gt;Enough talk let’s jump to the big part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it offer?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Async data caching &amp;amp; invalidation&lt;/li&gt;
&lt;li&gt;Smart &amp;amp; highly configurable refetch mechanism that smartly updates stale/expired data in the background when needed&lt;/li&gt;
&lt;li&gt;Declarative way to define asynchronous operations&lt;/li&gt;
&lt;li&gt;Garbage Collects Query &amp;amp; Mutation. That means unused queries sitting in the Cache for long time gets automatically removed&lt;/li&gt;
&lt;li&gt;Code &amp;amp; &lt;strong&gt;data&lt;/strong&gt; reusability because of persisted data &amp;amp; Query/Mutation &lt;strong&gt;Job&lt;/strong&gt; API&lt;/li&gt;
&lt;li&gt;Optimistic updates&lt;/li&gt;
&lt;li&gt;Lazy Loading Queries.  Run you defined asynchronous task or operation when needed&lt;/li&gt;
&lt;li&gt;Zero Configuration out of the box Global Store that no one ever have to touch&lt;/li&gt;
&lt;li&gt;Supports both Vanilla Flutter &amp;amp; &lt;a href="https://pub.dev/packages/flutter_hooks" rel="noopener noreferrer"&gt;Flutter Hooks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let’s see some Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A QueryJob is where the Logic of how the data should be &lt;/span&gt;
&lt;span class="c1"&gt;// fetched can defined. The task callback is a PURE Function &lt;/span&gt;
&lt;span class="c1"&gt;// &amp;amp; have access to external resources through the second &lt;/span&gt;
&lt;span class="c1"&gt;// parameter where the first parameter is the queryKey&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;successJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QueryJob&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&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="nl"&gt;queryKey:&lt;/span&gt; &lt;span class="s"&gt;"query-example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;task:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;externalData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delayed&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;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seconds:&lt;/span&gt; &lt;span class="mi"&gt;2&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="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="s"&gt;"The work successfully executed. Data: key=(&lt;/span&gt;&lt;span class="si"&gt;$queryKey&lt;/span&gt;&lt;span class="s"&gt;) value=&lt;/span&gt;&lt;span class="si"&gt;${
          Random.secure().nextInt(100)
        }&lt;/span&gt;&lt;span class="s"&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;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// QueryBowlScope creates a Bowl (metaphor for Collection/Store)&lt;/span&gt;
    &lt;span class="c1"&gt;// for all the Queries &amp;amp; Mutations&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;QueryBowlScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Fl-Query Quick Start'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;theme:&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;useMaterial3:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;primarySwatch:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyHomePage&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasicExample&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&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;BasicExample&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&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;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;crossAxisAlignment:&lt;/span&gt; &lt;span class="n"&gt;CrossAxisAlignment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s"&gt;"# Basic Query Example"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;Theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headline5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="c1"&gt;// QueryBuilder Widget provides the expected query &lt;/span&gt;
        &lt;span class="c1"&gt;// instances through the builder callback based on &lt;/span&gt;
        &lt;span class="c1"&gt;// the passed job &amp;amp; externalData argument&lt;/span&gt;
        &lt;span class="n"&gt;QueryBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&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="nl"&gt;job:&lt;/span&gt; &lt;span class="n"&gt;successJob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;externalData:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasData&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isRefetching&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;();&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;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Refetch"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                  &lt;span class="nl"&gt;onPressed:&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refetch&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="c1"&gt;// Text&lt;/span&gt;
              &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Row&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// QueryBuilder&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;Here’s three key important part in the code&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://fl-query.vercel.app/docs/basics/QueryBowlScope" rel="noopener noreferrer"&gt;&lt;code&gt;QueryBowlScope&lt;/code&gt;&lt;/a&gt;: It's the store where all queries &amp;amp; mutations are stored and distributed throughout the application&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fl-query.vercel.app/docs/basics/QueryJob" rel="noopener noreferrer"&gt;&lt;code&gt;QueryJob&lt;/code&gt;&lt;/a&gt;: This what one can use to declare and define the asynchronous operations that retrieves data from server or any other place&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fl-query.vercel.app/docs/basics/Queries#querybuilder" rel="noopener noreferrer"&gt;&lt;code&gt;QueryBuilder&lt;/code&gt;&lt;/a&gt;: It creates appropriate &lt;code&gt;Query&lt;/code&gt; by using the provided &lt;code&gt;job&lt;/code&gt; &amp;amp; provides the &lt;code&gt;Query&lt;/code&gt; in the builder method. It is the way the data &amp;amp; the UI connects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;QueryBowlScope&lt;/code&gt; &amp;amp; &lt;code&gt;QueryJob&lt;/code&gt; has many properties that can be configured &amp;amp; changed in a way that best suits your application. Most of their properties are some. But properties defined in &lt;code&gt;QueryBowlScope&lt;/code&gt; are global configuration where &lt;code&gt;QueryJob&lt;/code&gt; is specific to that very &lt;code&gt;Query&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Query External Data
&lt;/h2&gt;

&lt;p&gt;One thing you might’ve notice or have been thinking that the task function is separated from the Widget so how would you use other services, classes, methods or data in it. Simply, how will give inputs to a Query Task or the asynchronous operation? This is where the second parameter of &lt;code&gt;QueryJob&lt;/code&gt;’s task callback comes to play. You can use the &lt;code&gt;QueryBuilder&lt;/code&gt;’s &lt;code&gt;externalData&lt;/code&gt; named argument to pass data to the task callback. You can literally use anything as externalData. Just pass the Type as Type Parameter to &lt;code&gt;QueryJob&lt;/code&gt; &amp;amp; &lt;code&gt;QueryBuilder&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;QueryJob with externalData example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This job requires a pre-configured HTTP Client from the http package&lt;/span&gt;
&lt;span class="c1"&gt;// as externalData&lt;/span&gt;
&lt;span class="c1"&gt;// The first Type Parameter is the Type of returned Data &amp;amp; 2nd one is the Type&lt;/span&gt;
&lt;span class="c1"&gt;// of externalData&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;anotherJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QueryJob&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;queryKey:&lt;/span&gt; &lt;span class="s"&gt;"another-unique-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;task:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httpClient&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;httpClient&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://jsonplaceholder.typicode.com/todos/1"&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;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&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;Now let’s use this job inside a Widget&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// getting the instance of Client provided by the [provider] package&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;QueryBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&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="nl"&gt;job:&lt;/span&gt; &lt;span class="n"&gt;anotherJob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// passing the client as externalData&lt;/span&gt;
      &lt;span class="nl"&gt;externalData:&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasData&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// remember to always show a fallback widget/screen for errors too. &lt;/span&gt;
        &lt;span class="c1"&gt;// It keeps the user aware of status of the application their using&lt;/span&gt;
        &lt;span class="c1"&gt;// &amp;amp; saves their time&lt;/span&gt;
        &lt;span class="k"&gt;else&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;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasError&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isError&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"My disappointment is immeasurable &amp;amp; my day is ruined for this stupid error: &lt;/span&gt;&lt;span class="si"&gt;$error&lt;/span&gt;&lt;span class="s"&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                        &lt;span class="c1"&gt;// accessing the returned data &amp;amp; showing it&lt;/span&gt;
            &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
            &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Refetch"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refetch&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;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;That’s it. It is that easy to provide &lt;code&gt;externalData&lt;/code&gt; to a Query task&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, when &lt;code&gt;externalData&lt;/code&gt; changes the Query is not refetched. But you can change this behavior if you want the query to refetch everytime &lt;code&gt;externalData&lt;/code&gt; changes. Just set &lt;code&gt;refetchOnExternalDataChange&lt;/code&gt; to true in &lt;code&gt;QueryJob&lt;/code&gt; for that specific Query or in &lt;code&gt;QueryBowlScope&lt;/code&gt; for all Queries&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Query Refetch &amp;amp; Stale Time
&lt;/h2&gt;

&lt;p&gt;Every query is updated when needed. But you can trigger a refetch for a Query or multiple queries manually. This can be useful after a mutation or the application data has changed for sure. The &lt;code&gt;Query.refetch&lt;/code&gt; allows to refetch a single query where &lt;code&gt;QueryBowl.of(context).refetchQueries&lt;/code&gt; allows refetching multiple queries at the same time&lt;/p&gt;

&lt;p&gt;Here’s an example of single query refetch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="nl"&gt;onPressed:&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Refetch"&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;Now for refetching multiple queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;controller:&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;onSubmitted:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="n"&gt;QueryBowl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refetchQueries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;exampleJob1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exampleJob2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryKey&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;blockquote&gt;
&lt;p&gt;The &lt;code&gt;QueryBowl.of&lt;/code&gt; method gives access to many methods used internally in Fl-Query. Its just an imperative way to do some things when it's necessary. It provides access to many useful methods &amp;amp; properties e.g. &lt;code&gt;refetchQueries&lt;/code&gt;, &lt;code&gt;invalidateQueries&lt;/code&gt;, &lt;code&gt;resetQueries&lt;/code&gt;, &lt;code&gt;isFetching&lt;/code&gt; etc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stale Time means the amount of time when after a Query or multiple Query’s data should be considered as outdated&lt;/p&gt;

&lt;p&gt;By default, Queries become stale as soon as fetch/refetch completes. But this can be configured using the &lt;code&gt;staleTime&lt;/code&gt; property of &lt;code&gt;QueryBowlScope&lt;/code&gt; for global configuration or per Query basis using the &lt;code&gt;QueryJob&lt;/code&gt;'s property&lt;br&gt;
&lt;/p&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;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QueryJob&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;queryKey:&lt;/span&gt; &lt;span class="s"&gt;"another-unique-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// now the data of the query will become stale after 30 seconds when the&lt;/span&gt;
  &lt;span class="c1"&gt;// fetch/refetch executes&lt;/span&gt;
    &lt;span class="nl"&gt;staleTime:&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seconds:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;task:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;httpClient&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;httpClient&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://jsonplaceholder.typicode.com/todos/1"&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;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&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;h2&gt;
  
  
  Query status
&lt;/h2&gt;

&lt;p&gt;A Query can be in following statuses&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;isSuccess&lt;/code&gt;: When the task function returned data successfully&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isError&lt;/code&gt;: When the task function returned an error&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isLoading&lt;/code&gt;: When the task function is running&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isRefetching&lt;/code&gt;: When new data is being fetched or simply the &lt;code&gt;refetch&lt;/code&gt; method is executing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isIdle&lt;/code&gt;: When there's no data &amp;amp; &lt;code&gt;Query&lt;/code&gt;'s task has not been yet run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are Query Progress Status there’s another type of status of Query too. They’re called Data availability status. Following are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hasData&lt;/code&gt;: When query contains data no matters what's happening Basically, &lt;code&gt;query.data != null&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hasError&lt;/code&gt;: When the query contains error. Basically, &lt;code&gt;query.error != null&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember, don’t use &lt;code&gt;isLoading&lt;/code&gt; only for Loading Indicators as even if the Query &lt;code&gt;isSucessful&lt;/code&gt; the &lt;code&gt;data&lt;/code&gt; can still be null. So use &lt;code&gt;!query.hasData&lt;/code&gt; || &lt;code&gt;query.isLoading&lt;/code&gt; to ensure there’s no null exceptions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is all about queries. It has so much more useful functionalities &amp;amp; features that it can’t be covered in a single article. Please visit the &lt;a href="https://fl-query.vercel.app/" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for more info (&lt;a href="https://fl-query.vercel.app/" rel="noopener noreferrer"&gt;https://fl-query.vercel.app&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Mutations
&lt;/h2&gt;

&lt;p&gt;Queries are used for retrieving Data or for GET requests where Mutation are, Unlike queries, typically used to create/update/delete data or perform server side effects&lt;/p&gt;

&lt;p&gt;Basically, a mutation is a type of asynchronous operation that modifies already available data or adds data in a store or remote server&lt;/p&gt;

&lt;p&gt;Just like QueryJob Mutation has &lt;code&gt;MutationJob&lt;/code&gt; that can be used to define the mutation operation or configuring different stuff&lt;/p&gt;

&lt;p&gt;Here’s a MutationJob example:&lt;br&gt;
&lt;/p&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;basicMutationJob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutationJob&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
  &lt;span class="c1"&gt;// instead of queryKey mutation has mutationKey&lt;/span&gt;
  &lt;span class="nl"&gt;mutationKey:&lt;/span&gt; &lt;span class="s"&gt;"unique-mutation-key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;task:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&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;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// to simulate a failing response environment&lt;/span&gt;
        &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nextBool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"https://jsonplaceholder.typicode.com/posts"&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://google.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;headers:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'Content-type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'application/json; charset=UTF-8'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;jsonEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&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;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&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;The &lt;code&gt;task&lt;/code&gt; callback of MutationJob has a variable parameter. It might seem like externalData from QueryJob, but it's a little different. Instead of passing data through externalData argument of QueryBuider, you’ve to pass it through &lt;code&gt;mutate&lt;/code&gt; or &lt;code&gt;mutateAsync&lt;/code&gt; method of Mutation&lt;/p&gt;

&lt;p&gt;Now let’s use this &lt;code&gt;MutationJob&lt;/code&gt; with our &lt;code&gt;MutationBuilder&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;MutationBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;
     &lt;span class="nl"&gt;job:&lt;/span&gt; &lt;span class="n"&gt;basicMutationJob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;)&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;Padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;EdgeInsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c1"&gt;// Its just basic Form&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
               &lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                 &lt;span class="nl"&gt;controller:&lt;/span&gt; &lt;span class="n"&gt;titleController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="nl"&gt;decoration:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputDecoration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;labelText:&lt;/span&gt; &lt;span class="s"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
               &lt;span class="p"&gt;),&lt;/span&gt;
               &lt;span class="n"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="nl"&gt;controller:&lt;/span&gt; &lt;span class="n"&gt;bodyController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="nl"&gt;decoration:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputDecoration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;labelText:&lt;/span&gt; &lt;span class="s"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;),&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;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
               &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                 &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;titleController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                   &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bodyController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&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;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="c1"&gt;// calling the mutate of the mutation&lt;/span&gt;
                                        &lt;span class="n"&gt;mutation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                       &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="s"&gt;"body"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42069&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the holy number as ID&lt;/span&gt;
                      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nl"&gt;onData:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="c1"&gt;// resetting the form&lt;/span&gt;
                        &lt;span class="n"&gt;titleController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="n"&gt;bodyController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&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;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Post"&lt;/span&gt;&lt;span class="p"&gt;),&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;SizedBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;height:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="c1"&gt;// accessing the mutation result&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;mutation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;${mutation.data}&lt;/span&gt;&lt;span class="s"&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;mutation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mutation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&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="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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, there are 2 text fields that provide title and body of a Post &amp;amp; we’re running the mutation whenever the submit button is pressed. The &lt;code&gt;mutate&lt;/code&gt; method accepts 3 arguments&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1st parameter/Variables&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onData&lt;/code&gt;: A callback that runs when result is available &amp;amp; Mutation is successful&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onError&lt;/code&gt;: A callback that runs when result is available &amp;amp; Mutation is successful&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Optimistic Updates
&lt;/h3&gt;

&lt;p&gt;The most interesting part of &lt;code&gt;Mutation&lt;/code&gt;s are the &lt;code&gt;onMutate&lt;/code&gt; callback of &lt;code&gt;MutationBuilder&lt;/code&gt;. It’s a callback that runs just before executing the &lt;code&gt;MutationJob.task&lt;/code&gt;. Here you can do all sort of crazy stuff. Such as adding predicted data to different queries before data being returned from the server or deleting an entire Query or anything you want. But in combination with &lt;code&gt;onData&lt;/code&gt; you can update your application’s data Optimistically, so the user doesn’t have to wait&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Optimistic update means, updating Query data with predictable data before actually getting back any result. Then, when the real data arrives, replace the predicted data with the real data without letting the user even know&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s a simple example of Optimistic Updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;MutationBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;job:&lt;/span&gt; &lt;span class="n"&gt;newUsernameMutation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;onMutate:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// getting the query that needs to be updated optimistically&lt;/span&gt;
      &lt;span class="n"&gt;QueryBowl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setQueryData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserData&lt;/span&gt;&lt;span class="p"&gt;,&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;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;oldData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// you've to return a new instance of the oldData else fl-query&lt;/span&gt;
      &lt;span class="c1"&gt;// will assume data hasn't been updated thus won't trigger any changes&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;UserData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldData&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="nl"&gt;onData:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="c1"&gt;// replacing the predicted fake data with real data&lt;/span&gt;
        &lt;span class="n"&gt;QueryBowl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setQueryData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserData&lt;/span&gt;&lt;span class="p"&gt;,&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;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;oldData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="c1"&gt;// you've to return a new instance of the oldData else fl-query&lt;/span&gt;
      &lt;span class="c1"&gt;// will assume data hasn't been updated thus won't trigger any changes&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;UserData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oldData&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="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mutation&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;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;This is all for Mutations. To learn more about Mutation &lt;a href="https://fl-query.vercel.app/docs/basics/MutationJob" rel="noopener noreferrer"&gt;read the docs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks
&lt;/h2&gt;

&lt;p&gt;Fl-Query supports both Vanilla Flutter &amp;amp; flutter_hooks. Both aren’t much different everything is same but in fl_query_hooks you additionally get 2 hooks &lt;code&gt;useQuery&lt;/code&gt; &amp;amp; &lt;code&gt;useMutation&lt;/code&gt; which you can use in place of &lt;code&gt;QueryBuilder&lt;/code&gt; &amp;amp; &lt;code&gt;MutationBuilder&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  useQuery
&lt;/h3&gt;

&lt;p&gt;It is basically &lt;code&gt;QueryBuilder&lt;/code&gt; without all the typical Builder boilerplate. So when I write the 1st example with hooks it’ll look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BasicHookExample&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;HookWidget&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;BasicHookExample&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;job:&lt;/span&gt; &lt;span class="n"&gt;successJob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;externalData:&lt;/span&gt; &lt;span class="kc"&gt;null&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasData&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isRefetching&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;CircularProgressIndicator&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;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;children:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Refetch"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="nl"&gt;onPressed:&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refetch&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="c1"&gt;// Text&lt;/span&gt;
       &lt;span class="p"&gt;],&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Row&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;h3&gt;
  
  
  useMutation
&lt;/h3&gt;

&lt;p&gt;I guess, you know what &lt;code&gt;useMutation&lt;/code&gt; does. It’s a replacement for &lt;code&gt;MutationBuilder&lt;/code&gt; for flutter_hooks which makes code more clean &amp;amp; easier to read&lt;/p&gt;

&lt;p&gt;Here’s a &lt;code&gt;useMutation&lt;/code&gt; example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;// the mutation object is the same passed as &lt;/span&gt;
  &lt;span class="c1"&gt;// parameter in the builder method of MutationBuilder&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;mutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;useMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="cm"&gt;/* .... (Imaginary Form) */&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;This article only covers simple &amp;amp; most used features of Fl-Query. There are tons of more things that we can do with Fl-Query. Everything is available in the &lt;a href="https://fl-query.vercel.app/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; which is a WIP. So you can contribute to that if you want. It’ll really help the project to move forward&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fl-Query is still under heavy development &amp;amp; it is expected to have bugs + unintended behavior. So if you find any please &lt;a href="https://github.com/KRTirtho/fl-query/issues" rel="noopener noreferrer"&gt;create an Issue&lt;/a&gt; with proper details. Also, we’re open for any suggestions. Suggest what you like or not or want Fl-Query to have. Probably contribute to the project with your own code &amp;amp; feature. It will be much appreciated&lt;/p&gt;

&lt;p&gt;Since the project is in a very early stage it needs appropriate tests &amp;amp; I’m the worst excuse for Tester so Fl-Query needs some good testers who’re willing to contribute to the project with tests. If you want to contribute with tests. Please &lt;a href="https://github.com/KRTirtho/fl-query/discussions/categories/testing" rel="noopener noreferrer"&gt;join the discussion&lt;/a&gt; by creating one&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Give Fl-Query a ⭐Star⭐ in &lt;a href="https://github.com/KRTirtho/fl-query" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Social
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://twitter.com/@KrTirtho" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/kingkor-roy-tirtho-810b951b4/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/KRTirtho" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.to/KRTirtho"&gt;DEVCommunity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://medium.com/@krtirtho" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dart-lang | Features you didn’t know exists</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Fri, 03 Jun 2022 12:04:20 +0000</pubDate>
      <link>https://forem.com/krtirtho/dart-lang-features-you-didnt-know-exists-3j2b</link>
      <guid>https://forem.com/krtirtho/dart-lang-features-you-didnt-know-exists-3j2b</guid>
      <description>&lt;p&gt;Dart a single threaded, both AOT (Ahead of time) &amp;amp; JIT (Just in time) compiled, UI programming language that also provides HMR (Hot Reload) out of the box, is one of the growing programming languages nowadays. Its the base language of the famous cross platform UI toolkit &lt;strong&gt;Flutter.&lt;/strong&gt; Dart is known for its extreme syntactical similarities with JavaScript &amp;amp; also being strongly typed “compiled” language. And Yes, it can compile to JavaScript too&lt;/p&gt;

&lt;p&gt;Its used by many devs everyday for various Flutter related apps/tools. Chances are you’re one of them. But Flutter being one of the great UI toolkit, Dart keeps giving pain in a Developer’s life. But recently Dart improved so much it is actually making good progress on being one of the easiest &amp;amp; flexible programming language ever next to JavaScript. But unluckily lot of us still don’t know those awesome new features that exist &amp;amp; can be used in the programming language right now if you’re using the latest version&lt;/p&gt;

&lt;h2&gt;
  
  
  Operator Overloading/Overriding
&lt;/h2&gt;

&lt;p&gt;Have you ever compared two &lt;code&gt;Duration&lt;/code&gt;s in Dart? You probably used the equality operator &lt;code&gt;==&lt;/code&gt; to do that, right? But if you’re coming from other languages like JS/Java/PHP, you’d see something like &lt;code&gt;Duration.isEqual&lt;/code&gt;. But in Dart, like most other languages, supports operator overloading&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="kd"&gt;operator&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hola"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//result is true&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//result is false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can overload every single arithmetic &amp;amp; comparison operator (&lt;code&gt;+, -, *, ≠, ==, &amp;gt;, ≥, &amp;lt;, ≤, [], call()&lt;/code&gt;) in Dart using above syntax. &lt;a href="https://dart.dev/guides/language/language-tour#overridable-operators" rel="noopener noreferrer"&gt;More on operator overloading/overriding&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional Unnamed Parameters
&lt;/h2&gt;

&lt;p&gt;You’re probably familiar with &lt;em&gt;named &amp;amp; unnamed parameters&lt;/em&gt; in Dart/Flutter. Named parameters are optional by default but you can make them required by using the &lt;code&gt;required&lt;/code&gt; keyword. But did you know you can also make unnamed parameters optional?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;example&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$name&lt;/span&gt;&lt;span class="s"&gt;, it's just an owl!"&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;You can also mix ‘n match with default value optional unnamed parameters &amp;amp; unnamed parameters but you can’t use both named parameters &amp;amp; optional unnamed parameters at the same time&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bustin"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// works fine&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;what&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Jeiber"&lt;/span&gt;&lt;span class="p"&gt;]){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Mr. &lt;/span&gt;&lt;span class="si"&gt;$lastName&lt;/span&gt;&lt;span class="s"&gt;, you're not &lt;/span&gt;&lt;span class="si"&gt;$firstName&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// doesn't work&lt;/span&gt;
&lt;span class="c1"&gt;// syntax error. Expected ) after&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;noway&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;isIt&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
    &lt;span class="c1"&gt;// .....&lt;/span&gt;
    &lt;span class="c1"&gt;// .....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// doesn't work&lt;/span&gt;
&lt;span class="c1"&gt;// syntax error. Expected ) after&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;impossible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;isIt&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;]){&lt;/span&gt;
    &lt;span class="c1"&gt;// .....&lt;/span&gt;
    &lt;span class="c1"&gt;// .....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔥Super Parameters🔥
&lt;/h2&gt;

&lt;p&gt;Remember this?😵‍💫&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;MaterialButton&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;CustomButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;VoidCallback&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;onPressed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;VoidCallback&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;onLongPress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ValueChanged&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;onHighlightChanged&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MouseCursor&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;mouseCursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ButtonTextTheme&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;textTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;disabledTextColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;disabledColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;focusColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;hoverColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;highlightColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;splashColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Brightness&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;colorBrightness&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;elevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;focusElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;hoverElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;highlightElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;disabledElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;EdgeInsetsGeometry&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;VisualDensity&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;visualDensity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ShapeBorder&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Clip&lt;/span&gt; &lt;span class="n"&gt;clipBehavior&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Clip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FocusNode&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;focusNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;autofocus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaterialTapTargetSize&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;materialTapTargetSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;animationDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Widget&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;child&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="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;autofocus&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elevation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;elevation&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;focusElevation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;focusElevation&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hoverElevation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;hoverElevation&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;highlightElevation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;highlightElevation&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;disabledElevation&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;disabledElevation&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clipBehavior&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="n"&gt;onPressed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;onLongPress:&lt;/span&gt; &lt;span class="n"&gt;onLongPress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;onHighlightChanged:&lt;/span&gt; &lt;span class="n"&gt;onHighlightChanged&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;mouseCursor:&lt;/span&gt; &lt;span class="n"&gt;mouseCursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;textTheme:&lt;/span&gt; &lt;span class="n"&gt;textTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;textColor:&lt;/span&gt; &lt;span class="n"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;disabledTextColor:&lt;/span&gt; &lt;span class="n"&gt;disabledTextColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;disabledColor:&lt;/span&gt; &lt;span class="n"&gt;disabledColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;focusColor:&lt;/span&gt; &lt;span class="n"&gt;focusColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;hoverColor:&lt;/span&gt; &lt;span class="n"&gt;hoverColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;highlightColor:&lt;/span&gt; &lt;span class="n"&gt;highlightColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;splashColor:&lt;/span&gt; &lt;span class="n"&gt;splashColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;colorBrightness:&lt;/span&gt; &lt;span class="n"&gt;colorBrightness&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;elevation:&lt;/span&gt; &lt;span class="n"&gt;elevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;focusElevation:&lt;/span&gt; &lt;span class="n"&gt;focusElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;hoverElevation:&lt;/span&gt; &lt;span class="n"&gt;hoverElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;highlightElevation:&lt;/span&gt; &lt;span class="n"&gt;highlightElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;disabledElevation:&lt;/span&gt; &lt;span class="n"&gt;disabledElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;padding:&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;visualDensity:&lt;/span&gt; &lt;span class="n"&gt;visualDensity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;shape:&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;clipBehavior:&lt;/span&gt; &lt;span class="n"&gt;clipBehavior&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;focusNode:&lt;/span&gt; &lt;span class="n"&gt;focusNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;autofocus:&lt;/span&gt; &lt;span class="n"&gt;autofocus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;materialTapTargetSize:&lt;/span&gt; &lt;span class="n"&gt;materialTapTargetSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;animationDuration:&lt;/span&gt; &lt;span class="n"&gt;animationDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ..... other stuff .... //&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not much long ago we had to specify every single constructor parameter in a child class if we wanted/the parent class required to pass them to the &lt;code&gt;super&lt;/code&gt; constructor call. It was bloaty &amp;amp; was kind of a shame for the Dart Programming Language&lt;/p&gt;

&lt;p&gt;Starting from Dart 2.17 stable we got &lt;em&gt;super parameters.&lt;/em&gt; Now we can pass super constructor parameters directly from the child constructor&lt;/p&gt;

&lt;p&gt;The above code now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;MaterialButton&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CustomButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onPressed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onLongPress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onHighlightChanged&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mouseCursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disabledTextColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disabledColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;focusColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hoverColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;highlightColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;splashColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;colorBrightness&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;elevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;focusElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hoverElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;highlightElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disabledElevation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visualDensity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;focusNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;materialTapTargetSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;animationDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// ..... other stuff .... //&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally my 2.5MB HTTP response models can be squeezed down to 200KB&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fseovupi76xrnot2zgdhw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fseovupi76xrnot2zgdhw.gif" alt="https://c.tenor.com/IrPvKoyAfmsAAAAC/master-oogway-kung-fu-panda.gif" width="498" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though the Dart programming language still have a lot of flaws &amp;amp; lot features not yet implemented but its evolving. It was super hard to work with but now has become a lot more flexible. I hope a lot of new features will be added time by time making the Language more mature + modern&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://twitter.com/@KrTirtho" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.to/KRTirtho"&gt;dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@krtirtho" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dart</category>
      <category>flutter</category>
      <category>mobile</category>
      <category>programming</category>
    </item>
    <item>
      <title>Choosing Open Source License Wisely🤔</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Sun, 16 Jan 2022 16:13:23 +0000</pubDate>
      <link>https://forem.com/krtirtho/choosing-open-source-license-wisely-1m3p</link>
      <guid>https://forem.com/krtirtho/choosing-open-source-license-wisely-1m3p</guid>
      <description>&lt;p&gt;Licenses are really important part of a project. It’s one of those protector that’ll make sure no creation of one is used by another for self-profit without permission&lt;/p&gt;

&lt;p&gt;Even though Licenses simple stuff but they can get overwhelming to choose while owning a growing/expanding project. And it’s more important when you’re an one man show &amp;amp; you project is open source. Of course, you can create your own licenses. But to save time you can use a free pre-created one&lt;/p&gt;

&lt;p&gt;Here’s a List of popular Free Licenses, their basic breakdown &amp;amp; compatibility:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. MIT
&lt;/h3&gt;

&lt;p&gt;It’s basically like saying take my piece of code/project, do whatever you want (commercially/non-commercially) but don’t blame me if anything goes wrong&lt;/p&gt;

&lt;p&gt;It’s the most favorite Open Source Software license &amp;amp; being used by many popular libraries, frameworks, organizations &amp;amp; applications&lt;/p&gt;

&lt;p&gt;Here’s the license&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fmdq3k1o0agp6igysthdn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fmdq3k1o0agp6igysthdn.png" alt=" " width="603" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.gnu.org/licenses/gpl-3.0.en.html" rel="noopener noreferrer"&gt;2. GNU General Public License (GPL) v3.0&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;It’s mostly like MIT but instead of saying “do whatever you want” it says “do whatever you want but keep license term unchanged”. GPL is one of the famous license for most of the open source project built on the convention &amp;amp; tradition of Unix &amp;amp; GNU. It can be seen mostly in Linux/Unix related projects&lt;/p&gt;

&lt;p&gt;It’s a great license but it’s also called an “infection” as once you publish something under this license, other projects copying/forking this projects have to use this license or other licenses compatible with this license. Making every fork based of this license, it makes the license contagious. But it’s a great way to make the world more open &amp;amp; that’s why it’s OpenSourceFoundation’s favorite license&lt;/p&gt;

&lt;p&gt;You can view it &lt;a href="https://www.gnu.org/licenses/gpl-3.0.en.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.apache.org/licenses/LICENSE-2.0" rel="noopener noreferrer"&gt;3. Apache License 2.0&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Created by Apache Software Foundation in 2004 for the community &amp;amp; their own services. It allows doing all the things with the software including commercial use but it strictly prohibits the use of the software’s name/trademark/logo&lt;/p&gt;

&lt;p&gt;It’s a great choice for organizations as it secures the trademark &amp;amp; name/logo usage without affecting the flexibility of use of the product/software/library&lt;/p&gt;

&lt;p&gt;View it &lt;a href="https://www.apache.org/licenses/LICENSE-2.0" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://opensource.org/licenses/BSD-2-Clause" rel="noopener noreferrer"&gt;4.  FreeBSD / BSD 2-Clause License&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;FreeBSD License or Simplified BSD License or BSD 2-Clause License is one of the popular licenses for OpenSource projects. It’s super simple &amp;amp; quite similar to MIT. It just says, “do whatever you want but don’t blame me if anything goes wrong!”&lt;/p&gt;

&lt;p&gt;Here’s the license&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ffjuljgac1n28cy62fipm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ffjuljgac1n28cy62fipm.png" alt=" " width="600" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://opensource.org/licenses/BSD-3-Clause" rel="noopener noreferrer"&gt;5. New BSD License / BSD 3-Clause&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;It’s similar to BSD 2-Clause License, but with a 3rd clause that prohibits others from using the name of the project or its contributors to promote derived products without written consent&lt;/p&gt;

&lt;p&gt;Here’s the license&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzdy62ueq9ey3h3t69its.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzdy62ueq9ey3h3t69its.png" alt=" " width="600" height="652"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://spdx.org/licenses/BSD-4-Clause.html" rel="noopener noreferrer"&gt;6. Original/Old BSD License / BSD 4-Clause License* (not accepted in OSF)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The original BSD License. It was later updated as the BSD 2-Clause. It wasn’t accepted by the the OpenSourceFoundation because of it’s advertisement related clause (Clause-3). It’s like saying, “Do whatever you want but keep License unchanged/compatible &amp;amp; have to give me my credits”&lt;/p&gt;

&lt;p&gt;Even though its outdated, it has quite a lot meaningful things to it. It can be potentially helpful for small or one man army projects as it secures the “credit” of the developer which is often neglected by most of the Open Source Licenses&lt;/p&gt;

&lt;p&gt;Here’s the license&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fgxkhrczjutzxv087wx61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fgxkhrczjutzxv087wx61.png" alt=" " width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.mozilla.org/en-US/MPL/2.0/" rel="noopener noreferrer"&gt;7. Mozilla Public License&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Created by the Mozilla Foundation for the community. It’s a great license balancing both the open source &amp;amp; the proprietary perspective of a project. As such, it allows the integration of MPL-licensed code into proprietary codebases, as long as the MPL-licensed components remain accessible under the terms of the MPL. It’s a middle ground between the permissive licenses &amp;amp; GPL licenses&lt;/p&gt;

&lt;p&gt;Great choice for organizations/company which want to be as transparent as possible also keeping other super secret stuff veiled&lt;/p&gt;

&lt;p&gt;View the license &lt;a href="https://www.mozilla.org/en-US/MPL/2.0/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What to choose?
&lt;/h2&gt;

&lt;p&gt;It’s complicated. But one have to be careful while choosing licenses. If you’re fine to expose “everything” you can. Or you can keep your trademark/logo/patent/credit restricted too&lt;/p&gt;

&lt;p&gt;Here are some conditions to help differentiate &amp;amp; choose the correct license:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you’ve a healthy job but want to serve the community in free time, I’d encourage to choose the permissive licenses (MIT, BSD 2-Clause, Creative Commons)&lt;/li&gt;
&lt;li&gt;If you’re running an organization &amp;amp; need a bridge between OSS &amp;amp; proprietary, use MPL&lt;/li&gt;
&lt;li&gt;If it’s a non-profit OSS organization, use Apache-2.0/BSD 3-Clause&lt;/li&gt;
&lt;li&gt;If you’re an one man show &amp;amp; don’t have a healthy job or a student, I highly encourage to use BSD 4-Clause or a similar one to keep your credit secure &amp;amp; make it easier to get a job. Also good luck &amp;amp; don’t give up😉&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, I’m nobody to tell what to choose or not. But Licenses play a really important role in the long run. Yeah, you can change it later (like me) but keeping the ground solid from the beginning is always better&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one’s my favorite? (Opinion)
&lt;/h2&gt;

&lt;p&gt;It’s the &lt;strong&gt;Original BSD/BSD 4-Clause License.&lt;/strong&gt; But why?&lt;/p&gt;

&lt;p&gt;If you’re an opensource contributor/developer you might be aware of the &lt;strong&gt;faker.js story&lt;/strong&gt;. In case you’re don’t know, here’s a video explanation from my bud Jeff aka &lt;a href="https://www.youtube.com/c/Fireship" rel="noopener noreferrer"&gt;Fireship&lt;/a&gt;,&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/R6S-b_k-ZKY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Most of the time OSS developer works in free-time. But there are many scenarios where that person remain jobless just to maintain the OpenSource Project. Sometimes it’s profitable (sponsorship) &amp;amp; sometimes it’s not&lt;/p&gt;

&lt;p&gt;Everyone just uses those libraries where a very few contributes to the project. That’s not a problem, this is how open-source works, mostly. But the problem is when you use too much of that product &amp;amp; make profits but never payback nor give the credits&lt;/p&gt;

&lt;p&gt;I’m not saying, the user has to pay with $ money initially or at all but there are other ways to payback. Such as, contributing back, sharing the project or give credit in their own projects. It doesn’t cost anything. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Its only a few lines for others but can be a life changing opportunity for the project owner/maintainer&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After all, someone’s helping you for nothing. At least, be grateful to that person &amp;amp; try help him/her back in his/her problems if you’ve the ability&lt;/p&gt;

&lt;p&gt;BSD 4-Clause makes it compulsory to give credits to the developer which is most of the time neglected. OpenSource doesn’t mean only open. It means sharing &amp;amp; helping others too. But people are selfish (that included me too) &amp;amp; we can’t change that. But if we try we can do anything &amp;amp; I guess it’s high time we give the proper credits to the proper credit-holder. And there is no shame in giving others their legal credits&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Mega corporation, startup &amp;amp; consulting firms, all make tons of money on the shoulders of open source software” ~ Jeff | Fireship&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;IMO, BSD 4-Clause License is now a great solution to these open source developer burn out problems. If it’s not maybe we should make Licenses that actually cares about developer credits similar to BSD 4-Clause but even better&lt;/p&gt;

&lt;p&gt;Clause-3 of original BSD License:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F5dcy0dqk0gm7lei7re5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5dcy0dqk0gm7lei7re5v.png" alt=" " width="771" height="50"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the end, this is all my opinion &amp;amp; personal thinking. Of course I can’t be right always. Maybe there are way better solutions to this problem&lt;/p&gt;

&lt;p&gt;Do more OSS projects and&lt;br&gt;
DO NOT BURN OUT &amp;amp; TAKE BREAKS&lt;/p&gt;

&lt;h2&gt;
  
  
  Social
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/KrTirtho" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/KRTirtho/" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/krtirtho"&gt;dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@krtirtho" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/kingkor-roy-tirtho-810b951b4/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>motivation</category>
      <category>career</category>
    </item>
    <item>
      <title>Facebook Sign Up Form Tutorial | React Binden💪👑 x Tailwindcss ️</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Tue, 02 Nov 2021 18:29:31 +0000</pubDate>
      <link>https://forem.com/krtirtho/facebook-sign-up-form-tutorial-react-binden-x-tailwindcss-25cn</link>
      <guid>https://forem.com/krtirtho/facebook-sign-up-form-tutorial-react-binden-x-tailwindcss-25cn</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/facebook/react" rel="noopener noreferrer"&gt;React&lt;/a&gt; is an awesome FrontEnd UI library created by Facebook. But forms in React always been a little hard. This is what the library &lt;a href="https://react-binden.netlify.app/" rel="noopener noreferrer"&gt;react-binden&lt;/a&gt; solves. It's a fairly new form handling library for React. It's extremely easy to learn &amp;amp; use&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get a deep dive on &lt;a href="https://dev.to/krtirtho/react-forms-made-easy-with-react-binden-1aeb"&gt;React Binden&lt;/a&gt; or visit the &lt;a href="https://react-binden.netlify.app/" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwindcss&lt;/a&gt; is my most favorite css framework❤️ &amp;amp; by far the most awesome library I've ever found&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article only shows how to build a Signup form using React, react-binden &amp;amp; tailwindcss. I'm assuming one will be familiar with above tools &amp;amp; technologies&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What are we Building?
&lt;/h2&gt;

&lt;p&gt;We're making a simple, regular &amp;amp; boring old Sign Up Form inspired by Facebook's Sign Up Form with React, react-binden &amp;amp; tailwindcss. But there's a twist. The form will still be a Sign Up Form but &lt;strong&gt;we'll be honest&lt;/strong&gt; for the placeholders, labels &amp;amp; license agreement etc.. texts🙃😆&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the project
&lt;/h2&gt;

&lt;p&gt;For bootstraping the project, we'll use &lt;a href="https://vitejs.dev/" rel="noopener noreferrer"&gt;vite&lt;/a&gt;. An extraordinary frontend build tool that is super fast &amp;amp; also supports various frontend frameworks&lt;/p&gt;

&lt;p&gt;Initiating the project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm init vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It'll ask a few questions, including project name &amp;amp; which frontend framework to use. Write the name of your choice &amp;amp; select the &lt;code&gt;react&lt;/code&gt; option&lt;/p&gt;

&lt;p&gt;Now open the project into VSCode/your favorite code editor. Then in the terminal, inside the project root run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then remove all the non required files e.g &lt;code&gt;src/App.css&lt;/code&gt;, &lt;code&gt;src/logo.svg&lt;/code&gt;. Remove the all the boilerplate code inside &lt;code&gt;src/App.jsx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now install the following dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-binden tailwindcss postcss autoprefixer nanoid clsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the following command to initiate TailwindCSS inside your project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx tailwindcss init &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the following files &lt;code&gt;tailwind.config.js&lt;/code&gt;, &lt;code&gt;postcss.config.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now add the following to &lt;code&gt;src/index.css&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's enable &lt;a href="https://tailwindcss.com/docs/just-in-time-mode" rel="noopener noreferrer"&gt;JIT&lt;/a&gt; (Just in Time) mode for the tailwindcss compiler. Add &lt;code&gt;mode: "jit"&lt;/code&gt; inside the code &lt;code&gt;tailwind.config.js&lt;/code&gt;'s export config object. Then the file should look like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// added jit mode&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// purge Array&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.{js,ts,jsx,tsx}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'media' for automatic dark mode detection&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&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="na"&gt;plugins&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;Now run following command to start the vite dev server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Custom Themed Input
&lt;/h2&gt;

&lt;p&gt;Now that we've done initializing the project, it's time to create a awesome &amp;amp; beautiful &lt;code&gt;Input&lt;/code&gt; component with our favorite tailwindcss&lt;/p&gt;

&lt;p&gt;Create a file as &lt;code&gt;src/ModInput.jsx&lt;/code&gt; then do the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-binden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;clsx&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nanoid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nanoid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ModInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nf"&gt;nanoid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ModInput&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;May be you're thinking &lt;em&gt;why did I put the label &amp;amp; the error hint in the wrong order&lt;/em&gt;. Well, there's a reason. But now, let's style these components. I'll be using &lt;a href="https://github.com/lukeed/clsx" rel="noopener noreferrer"&gt;clsx&lt;/a&gt; for handling multiple &amp;amp; conditional classes efficiently&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-binden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;clsx&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clsx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nanoid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nanoid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ModInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;peer transition-all p-1 border-2 border-solid rounded outline-none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// conditional classes&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border-red-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touched&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border-gray-500 focus:border-blue-400&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nf"&gt;nanoid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// radio &amp;amp; checkboxes are different than text fields thus they need&lt;/span&gt;
  &lt;span class="c1"&gt;// a bit differently adjusted styles&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rowTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;radio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secondDivStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inline-flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// corrects the wrong order of label &amp;amp; error-hint&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;rowTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex-col-reverse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex-row items-center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;labelStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transition-all select-none peer-focus:text-blue-500 font-semibold&lt;/span&gt;&lt;span class="dl"&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;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;font-normal peer-focus:text-black ml-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;rowTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;peer-focus:text-red-500&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touched&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;secondDivStyles&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-red-500 text-sm ml-2 group-focus"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                   &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;
      &lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inputStyle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;labelStyles&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ModInput&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's answer why the order of error-hint &amp;amp; label are in reverse in the JSX. This is because of tailwind's &lt;code&gt;peer&lt;/code&gt; class &amp;amp; &lt;code&gt;peer-focus:&lt;/code&gt; prefix/variant. TailwindCSS provides an awesome way to handle css's styles based on sibling's state. &lt;code&gt;peer&lt;/code&gt; prefix works as the CSS's &lt;code&gt;+&lt;/code&gt; operator for selectors. But &lt;code&gt;peer&lt;/code&gt; only works when the upper most element/sibling has the &lt;code&gt;peer&lt;/code&gt; class. Downwards siblings can use upward siblings states but not vice-versa&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Learn more about &lt;a href="https://blog.tailwindcss.com/tailwindcss-2-2" rel="noopener noreferrer"&gt;TailwindCSS Sibling Select Variant&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Basic Form
&lt;/h2&gt;

&lt;p&gt;Let's use the newly created &lt;code&gt;ModInput&lt;/code&gt;. Now in &lt;code&gt;src/App.jsx&lt;/code&gt; we've to create our basic form using react-binden's &lt;code&gt;Form&lt;/code&gt;, &lt;code&gt;useModel&lt;/code&gt; &amp;amp; &lt;code&gt;regex&lt;/code&gt;. We'll style the form later. Now only focus on Logic&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useModel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-binden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ModInput&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ModInput&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// models of each field&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;birthday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// since we're using radio-group a common name for all the&lt;/span&gt;
  &lt;span class="c1"&gt;// radio-button is required to make it function&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gender&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_states&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setSubmitting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resetForm&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// resetting the form&lt;/span&gt;
            &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nf"&gt;resetForm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
              &lt;span class="nf"&gt;setSubmitting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Honest Facebook Sign Up&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Disclaimer!:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; This is just a parody of Facebook. Nothing related to actual Facebook corp. Made just for fun &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; entertainment&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;
          &lt;span class="c1"&gt;// only allows lowercase letters&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;only lower case name is allowed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should be a valid email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moderatePassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write a stronger password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;confirmPassword&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;imprint-model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Confirm Password"&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"datetime"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Birthday"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;birthday&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date_dd_MM_yyyy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should follow the `ddmmyy` format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Gender&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
              &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"male"&lt;/span&gt;
              &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Male"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
              &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"female"&lt;/span&gt;
              &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Female"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
              &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other"&lt;/span&gt;
              &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Other"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Get Ruined&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you feel overwhelmed with all of the code above you can learn about react-binden &lt;a href="https://react-binden.netlify.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, that we've all the fields Facebook requires to Signup, let's style &amp;amp; structure them as following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... import stuffs&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ... other stuff (models, handlers etc..)&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col items-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"m-2 text-3xl text-center font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Honest Facebook Sign Up
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Disclaimer!:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; This is just a parody of Facebook. Nothing related
        actual Facebook corp. Made just for fun &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; entertainment
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"inline-flex flex-col p-5 space-y-2 max-w-xl"&lt;/span&gt;
        &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl text-gray-900 font-semibold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign Up&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xs text-gray-600"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            It's quick &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; easy
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;only lower case name is allowed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should be a valid email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex space-x-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
            &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moderatePassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write a stronger password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;confirmPassword&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;imprint-model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Confirm Password"&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"datetime"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;birthday&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date_dd_MM_yyyy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should follow the `ddmmyy` format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Gender&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center justify-between w-1/2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"male"&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Male"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
              &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"female"&lt;/span&gt;
              &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Female"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other"&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Other"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-600 text-xs pb-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          By clicking Sign Up, you agree to our Terms, Data Policy and Cookie Policy. You may receive SMS notifications from us and can opt out at any time.
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-[#00a400] py-2 px-10 text-white font-bold rounded"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Get Ruined
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Fun Part
&lt;/h2&gt;

&lt;p&gt;I hope it'd now look visually appealing but that's boring. Nothing fun &amp;amp; interesting. Of course, I could add awesome animations, weirdest scroll effect or various CSS animations. But we're developers &amp;amp; we do hard work "occasionally"🤥. So let's use our "joke power" (which I obviously don't have but still trying) with texts. Let's just pretend we're actual Facebook developer &amp;amp; we, for some reason have to be slightly honest with what we build🙃&lt;/p&gt;

&lt;p&gt;FUN GENERATING&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useModel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-binden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ModInput&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ModInput&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;birthday&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gender&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setSubmitting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resetForm&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;resetForm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setSubmitting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col items-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"m-2 text-3xl text-center font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Honest Facebook Sign Up
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Disclaimer!:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;b&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; This is just a parody of Facebook. Nothing related
        actual Facebook corp. Made just for fun &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; entertainment
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"inline-flex flex-col p-5 space-y-2 max-w-xl"&lt;/span&gt;
        &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl text-gray-900 font-semibold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign Up&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xs text-gray-600"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            It's quick &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; easy (profit for us)
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Credit Card Pin. Oops, Username"&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;only lower case name is allowed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should be a valid email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password. Oh sorry, Email"&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex space-x-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
            &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moderatePassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write a stronger password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Why not use, Hail Zuckerberg?"&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;confirmPassword&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;imprint-model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Confirm Password"&lt;/span&gt;
            &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Isn't it, Hail Zuckerberg?"&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"datetime"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Birthday (Makes it easier for your friends to beg treats from you)"&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;birthday&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date_dd_MM_yyyy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should follow the `ddmmyy` format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Gender&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center justify-between w-1/2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"male"&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Male"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;
              &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"female"&lt;/span&gt;
              &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Female"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ModInput&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;gender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"other"&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Other"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-600 text-xs pb-5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          By clicking Get Ruined, you agree that you're our product, we can do
          whatever we want with &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; we own you (for free). You may receive SMS
          notifications from us and can opt out at any time (not actually).
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-[#00a400] py-2 px-10 text-white font-bold rounded"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Get Ruined
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Glad that it's finished. For a moment, it was feeling like it'd never end. But don't go way. There's a catch in the project. I created the entire website &lt;em&gt;without taking care of responsiveness&lt;/em&gt;.  So you can now make it responsive by yourself. Do this as a home-work&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;After writing 2 million lines (200 actually) of code we're finally done. Let's see what have we built so far &amp;amp; let's hope there's no bug&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/facebook-signup-parody-5l21l"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Source Code: &lt;a href="https://github.com/KRTirtho/fb-parody-signup" rel="noopener noreferrer"&gt;https://github.com/KRTirtho/fb-parody-signup&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Social
&lt;/h2&gt;

&lt;p&gt;Follow me on &lt;a href="https://twitter.com/KrTirtho" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.reddit.com/user/krtirtho" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give &lt;strong&gt;react-binden&lt;/strong&gt; a &lt;a href="https://github.com/KRTirtho/react-binden" rel="noopener noreferrer"&gt;⭐ on Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>react</category>
    </item>
    <item>
      <title>React Forms Made Easy with react-binden🚀</title>
      <dc:creator>Kingkor Roy Tirtho</dc:creator>
      <pubDate>Sun, 31 Oct 2021 09:10:25 +0000</pubDate>
      <link>https://forem.com/krtirtho/react-forms-made-easy-with-react-binden-1aeb</link>
      <guid>https://forem.com/krtirtho/react-forms-made-easy-with-react-binden-1aeb</guid>
      <description>&lt;p&gt;Introducing &lt;strong&gt;React Binden&lt;/strong&gt; 🎉🚀&lt;/p&gt;

&lt;p&gt;A react form handling library which is actually easy&lt;/p&gt;

&lt;p&gt;React Binden is inspired from Vue's &lt;code&gt;v-bind&lt;/code&gt; directive &amp;amp; is extremely lightweight yet fast&lt;/p&gt;

&lt;p&gt;Its,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightweight, Fast &amp;amp; Tree-shakable&lt;/li&gt;
&lt;li&gt;has Out of the Box &lt;strong&gt;validation&lt;/strong&gt; without any 3rd party library&lt;/li&gt;
&lt;li&gt;optimizes both &lt;strong&gt;Hooks &amp;amp; Components&lt;/strong&gt; to get the best of both worlds&lt;/li&gt;
&lt;li&gt;offers custom curated collection of useful &lt;strong&gt;Regex&lt;/strong&gt; for validation purposes&lt;/li&gt;
&lt;li&gt;UI component library &lt;strong&gt;proof&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;can be integrated with any UI component library/framework &lt;strong&gt;without any 3rd party library&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;zero&lt;/strong&gt; dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why created this?
&lt;/h2&gt;

&lt;p&gt;React forms, no doubt can be a nightmare for any developer. It can be messy to create a simple Login form in React&lt;/p&gt;

&lt;p&gt;One day, while using Vuejs for creating a &lt;a href="http://medium.com" rel="noopener noreferrer"&gt;medium.com&lt;/a&gt; clone, I encountered with Vue's &lt;code&gt;v-bind&lt;/code&gt; directive. Which allows to bind a value of a field with a variable. In React, that's not possible as React supports only one-way data-binding. Just think, if it was available in React, it'd been a god sent gift but alas!. Don't be sad as we can mimic two-way data-binding in React with &lt;em&gt;state-up-lifting&lt;/em&gt;. It means declaring the state of a child in the parent &amp;amp; then passing that down to child. Kind of like prop-drilling but in a managed manner. You shouldn't do more than 2 times of state-up-lifting as it'd make the UI slow leading to huge re-renders&lt;/p&gt;

&lt;p&gt;So I experimentally tried mimicking &lt;code&gt;v-bind&lt;/code&gt; for &lt;code&gt;input&lt;/code&gt; &amp;amp; walla, it worked fine. I though it might cause performance issues &amp;amp; huge re-renders but surprisingly it didn't instead it's fast &amp;amp; fast as normal. Then, I decided finish writing the library&lt;/p&gt;

&lt;p&gt;Another reason is simplicity. I found every form library having some kind of complexity even though, they were doing their best to simplify React Form handling experience. Custom schema for validation, spreading props in Fields, hooks that returns huge amount of objects which you've to manually handle etc. kind of made me really confused. I discovered that, every form library is either completely hook based or completely component render function based or provides both API. I never found a single one that utilizes both custom components &amp;amp; hooks together. So, in with React-Binden I tried closing that gap which will give the developer best of both world&lt;/p&gt;

&lt;p&gt;Third &amp;amp; final reason is size. Current form libraries aren't lightweight except react-final-form. So making a decent form library which is lightweight and tree-shakable was React Binden's one of the main goals. BTW, it uses ESNext module system&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use
&lt;/h2&gt;

&lt;p&gt;React Binden is extremely easy to use. It has the simplest API for handling React Forms. It provides hooks &amp;amp; custom components&lt;/p&gt;

&lt;p&gt;The most important ones are the &lt;code&gt;useModel&lt;/code&gt;, &lt;code&gt;Form&lt;/code&gt; &amp;amp; &lt;code&gt;Input&lt;/code&gt;. Using these 3, you can complete most of the job. Now let's see how to use &lt;code&gt;useModel&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using useModel
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;useModel&lt;/code&gt; is nothing but a simple hook that just declares some required states for a &lt;code&gt;Input&lt;/code&gt;'s model. It requires a &lt;em&gt;default value&lt;/em&gt; which can be a number, string or an Array (applicable only for checkbox-group)&lt;em&gt;.&lt;/em&gt; And has an optional parameter that can be used pass all the validation prop of an &lt;code&gt;Input&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useModel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-binden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/*---------------------------------------*/&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;some-field&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;minimum 5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;maxLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_touched&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;semantic-validation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&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="c1"&gt;// all the states/properties&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touched&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// all the methods&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTouched&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Learn more about &lt;code&gt;useModel&lt;/code&gt; &lt;a href="https://react-binden.netlify.app/docs/hooks/use-model" rel="noopener noreferrer"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using Form
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Form&lt;/code&gt; is a typical form component as well as a context provider. It doesn't hold a huge amount of state for the field. Only keeps the state of submit, reset &amp;amp; if all field have don't have any error&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;onSubmit&lt;/code&gt; prop to handle form submission just like a vanilla HTML form. And it supports all other form attributes&lt;/p&gt;

&lt;p&gt;Example of a &lt;code&gt;Form&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;
    &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// handling form submission&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="c1"&gt;// handle exceptions&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*... other components*/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;More about &lt;code&gt;Form&lt;/code&gt; can be found &lt;a href="https://react-binden.netlify.app/docs/components/form" rel="noopener noreferrer"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using Input
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Input&lt;/code&gt;  is what acts like an input field. &lt;code&gt;useModel&lt;/code&gt;'s model have to be bind with this component's &lt;code&gt;model&lt;/code&gt; prop. It takes all the validation props. Since React Binden follows HTML validation standards &amp;amp; semantic HTML form validation attributes, all the HTML input validation props is present. But instead of validating while submission, React Binden validates in real time. You can pass custom error message with those validation props&lt;/p&gt;

&lt;p&gt;Validation props that are supported&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt; (for numeric values)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minLength&lt;/code&gt;, &lt;code&gt;maxLength&lt;/code&gt; (for string values)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pattern&lt;/code&gt; (through regex)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;required&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;maxLength&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// passing a custom error msg using Tuples&lt;/span&gt;
            &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Minimum 5 characters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;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;blockquote&gt;
&lt;p&gt;Here's more about &lt;strong&gt;&lt;a href="https://react-binden.netlify.app/docs/components/input" rel="noopener noreferrer"&gt;Input&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Learn more about Validation &lt;a href="https://react-binden.netlify.app/docs/tutorials/validation/" rel="noopener noreferrer"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Validating password &amp;amp; confirm password is a pain, right? This is where &lt;code&gt;imprint-model&lt;/code&gt; comes handy. Pass the password model to this &amp;amp; the field will only match to that password field&lt;/p&gt;

&lt;p&gt;Example of &lt;code&gt;imprint-model&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
            &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moderatePassword&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;confirmPassword&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// passing the model that should be imprinted/followed&lt;/span&gt;
            &lt;span class="na"&gt;imprint-model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
            &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Confirm Password"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign Up&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Might be thinking about &lt;strong&gt;integrating React Binden with an UI component library/framework&lt;/strong&gt;. No worries. You can learn about it &lt;a href="https://react-binden.netlify.app/docs/tutorials/3rd-party-ui-libraries" rel="noopener noreferrer"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Input&lt;/code&gt; can be validated completely manually using the &lt;code&gt;validate&lt;/code&gt; prop. As a companion, there is also a &lt;strong&gt;&lt;a href="https://react-binden.netlify.app/docs/hooks/use-validator-chain" rel="noopener noreferrer"&gt;&lt;code&gt;useValidatorChain&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; hook which can be used for using multiple validation functions at once&lt;/p&gt;

&lt;h3&gt;
  
  
  Regex
&lt;/h3&gt;

&lt;p&gt;React Binden also provides an useful list of Regular Expressions that can be used with &lt;code&gt;Input&lt;/code&gt;'s &lt;code&gt;pattern&lt;/code&gt; prop. Suppose validating an email field. This can be done with &lt;code&gt;regex&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-binden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*....Form &amp;amp; other component....*/&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Input&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
            &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// using `regex.email` for validating email&lt;/span&gt;
            &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Should be a valid email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;required&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*....Form &amp;amp; other component....*/&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;blockquote&gt;
&lt;p&gt;The list of available Regex can be found &lt;a href="https://react-binden.netlify.app/docs/regex" rel="noopener noreferrer"&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;React Binden also supports &lt;a href="https://react-binden.netlify.app/docs/components/textarea" rel="noopener noreferrer"&gt;&lt;strong&gt;Textarea&lt;/strong&gt;&lt;/a&gt;, &lt;strong&gt;&lt;a href="https://react-binden.netlify.app/docs/components/select" rel="noopener noreferrer"&gt;Select&lt;/a&gt;, &lt;a href="https://react-binden.netlify.app/docs/examples/checkboxes" rel="noopener noreferrer"&gt;Checkboxes&lt;/a&gt; &amp;amp; &lt;a href="https://react-binden.netlify.app/docs/examples/radio-group" rel="noopener noreferrer"&gt;Radio Group&lt;/a&gt;.&lt;/strong&gt; And the entire library is written with Typescript❤️ so don't worry about Typescript support&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is a short review &amp;amp; tutorial of React Binden. The full documentation is what you should be looking for. To learn about all the features of React Binden visit &lt;a href="https://react-binden.netlify.app/" rel="noopener noreferrer"&gt;react-binden.netlify.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React Binden is a completely new library &amp;amp; heavily inspired by other form libraries too. Those are an asset &amp;amp; Thanks❤️ to those library authors for their outstanding generosity for the OSS community&lt;/p&gt;

&lt;p&gt;If you ever encounter any bug related to React  Binden &lt;a href="https://github.com/KRTirtho/react-binden/issues/new?assignees=&amp;amp;labels=&amp;amp;template=bug_report.md&amp;amp;title=%3CSomething%3E+doesn%27t+work" rel="noopener noreferrer"&gt;create an issue on Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have any ideas to improve react-binden? Why not &lt;a href="https://github.com/KRTirtho/react-binden/discussions/new" rel="noopener noreferrer"&gt;Discuss&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Follow us on &lt;a href="https://twitter.com/KrTirtho" rel="noopener noreferrer"&gt;twitter/@krtirtho&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give a 🌟 on &lt;a href="https://github.com/KRTirtho/react-binden" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
