<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roshan Jung Kunwar</title>
    <description>The latest articles on DEV Community by Roshan Jung Kunwar (@rshnkunwar).</description>
    <link>https://dev.to/rshnkunwar</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F144248%2F05781daa-df26-49d0-ae5d-a302e5737f58.jpg</url>
      <title>DEV Community: Roshan Jung Kunwar</title>
      <link>https://dev.to/rshnkunwar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rshnkunwar"/>
    <language>en</language>
    <item>
      <title>Flutter OTA Updates with Shorebird</title>
      <dc:creator>Roshan Jung Kunwar</dc:creator>
      <pubDate>Sat, 19 Sep 2026 17:39:00 +0000</pubDate>
      <link>https://dev.to/rshnkunwar/flutter-ota-updates-with-shorebird-2fo3</link>
      <guid>https://dev.to/rshnkunwar/flutter-ota-updates-with-shorebird-2fo3</guid>
      <description>&lt;h1&gt;
  
  
  Flutter OTA Updates with Shorebird
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Why We Needed OTA Updates
&lt;/h3&gt;

&lt;p&gt;We pushed a build with a small logic error. It was a one-line fix.&lt;/p&gt;

&lt;p&gt;By the time we noticed, the app was already crashing on our users' devices. Sentry was firing nonstop.&lt;/p&gt;

&lt;p&gt;We fixed the code fast. But shipping the fix was slow. The new build took about &lt;strong&gt;8 hours&lt;/strong&gt; to go live on Android and &lt;strong&gt;12 hours&lt;/strong&gt; on iOS. The crashes kept happening the whole time.&lt;/p&gt;

&lt;p&gt;That's when we knew we needed OTA updates. That's how we found Shorebird.&lt;/p&gt;

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

&lt;p&gt;Shorebird is a code push tool for Flutter. It sends Dart code updates (called patches) straight to your users' phones. No new store release. No store review.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Silent.&lt;/strong&gt; Patches download in the background. Users don't have to do anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast.&lt;/strong&gt; Patches skip store review, so they reach users far sooner than a normal release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple for users.&lt;/strong&gt; The patch applies the next time the app restarts.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Shorebird patches Dart code only. Native changes, like a new plugin or a new permission, still need a store release.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;We run Shorebird in our production app, with two flavors: &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt;. Here is the setup, step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create an Account
&lt;/h3&gt;

&lt;p&gt;Sign up on the Shorebird Console: &lt;a href="https://shorebird.dev" rel="noopener noreferrer"&gt;shorebird.dev&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Install the CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh &lt;span class="nt"&gt;-sSf&lt;/span&gt; | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Set-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RemoteSigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CurrentUser&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;iwr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-UseBasicParsing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'https://raw.githubusercontent.com/shorebirdtech/install/main/install.ps1'&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;iex&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;Shorebird CI was deprecated on August 8, 2026. Please switch to the open source package &lt;a href="https://pub.dev/packages/shorebird" rel="noopener noreferrer"&gt;shorebird&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Verify the Setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it works, you'll see something 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;[~]$ shorebird doctor
Shorebird 1.6.119 • git@github.com:shorebirdtech/shorebird.git
Flutter 3.47.1 • revision 91f8bd75076e9c740aa13cf67eb9ec1a093f68f5
Engine • revision 03e67977a7ff5893d96ac97f22c6a795530c0040

URL Reachability
✓ https://api.shorebird.dev OK (0.5s)
✓ https://console.shorebird.dev OK (1.2s)
✓ https://oauth2.googleapis.com OK (0.2s)
✓ https://storage.googleapis.com OK (0.1s)
✓ https://cdn.shorebird.cloud OK (96ms)

✓ Shorebird is up-to-date (3.2s)
  [!] A new version of shorebird is available! Run `shorebird upgrade` to upgrade.
✓ Xcode project does not override FLUTTER_ build settings (0ms)
1 issue detected.

A new version of shorebird is available!
Run shorebird upgrade to upgrade.
[~]$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;That warning is fine. It only means a newer version is available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Log In
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens a browser window. Log in, then return to your terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Add Shorebird to Your App
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird create my_shorebird_app
&lt;span class="nb"&gt;cd &lt;/span&gt;my_shorebird_app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h3&gt;
  
  
  6. Keep It Up to Date
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring &lt;code&gt;shorebird.yaml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;After &lt;code&gt;shorebird init&lt;/code&gt; or &lt;code&gt;shorebird create&lt;/code&gt;, Shorebird adds a &lt;code&gt;shorebird.yaml&lt;/code&gt; file to your project root.&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="c1"&gt;# This file is used to configure the Shorebird updater used by your application.&lt;/span&gt;
&lt;span class="c1"&gt;# Learn more at https://shorebird.dev&lt;/span&gt;
&lt;span class="c1"&gt;# This file should be checked into version control.&lt;/span&gt;

&lt;span class="c1"&gt;# This is the unique identifier assigned to your app.&lt;/span&gt;
&lt;span class="c1"&gt;# It is used by your app to request the correct patches from Shorebird servers.&lt;/span&gt;
&lt;span class="na"&gt;app_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;864ab1b0-ba78-4b15-990a-a63cec35a41b&lt;/span&gt;
&lt;span class="na"&gt;flavors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;864ab1b0-ba78-4b15-990a-a63cec35a41b&lt;/span&gt;
  &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;6b6e6631-4fbe-4645-8d9d-d5247656d975&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each flavor gets its own app ID. This keeps &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt; patches apart.&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;app_id&lt;/code&gt; is not a secret. Commit it and share it freely.&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://docs.shorebird.dev/code-push/initialize/" rel="noopener noreferrer"&gt;Initialize Shorebird&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Releasing and Patching
&lt;/h2&gt;

&lt;p&gt;Shorebird works in two steps: first a &lt;strong&gt;release&lt;/strong&gt;, then &lt;strong&gt;patches&lt;/strong&gt; on top of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create a Release
&lt;/h3&gt;

&lt;p&gt;You can't patch until you have a release. This is the build you upload to the Google Play Store or the Apple App Store.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird release android &lt;span class="nt"&gt;--flavor&lt;/span&gt; dev
shorebird release android &lt;span class="nt"&gt;--flavor&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird release ios &lt;span class="nt"&gt;--flavor&lt;/span&gt; dev
shorebird release ios &lt;span class="nt"&gt;--flavor&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h3&gt;
  
  
  2. Preview the Release
&lt;/h3&gt;

&lt;p&gt;Run the release on a device or emulator before you ship it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird preview
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Push a Patch
&lt;/h3&gt;

&lt;p&gt;Fixed a bug in Dart code? Push a patch. It reaches every user on that release version. No store review.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird patch android &lt;span class="nt"&gt;--flavor&lt;/span&gt; dev
shorebird patch android &lt;span class="nt"&gt;--flavor&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shorebird patch ios &lt;span class="nt"&gt;--flavor&lt;/span&gt; dev
shorebird patch ios &lt;span class="nt"&gt;--flavor&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h2&gt;
  
  
  Tracking Releases in the Console
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Releases
&lt;/h3&gt;

&lt;p&gt;Every release and patch shows up in the &lt;a href="https://console.shorebird.dev" rel="noopener noreferrer"&gt;Shorebird Console&lt;/a&gt;. Pick your app to see its releases, and open one to see its patches.&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%2Fwww.roshankunwar.com.np%2F_next%2Fimage%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fshorebird-list-releases.3385b99c.webp%26w%3D3840%26q%3D75" 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%2Fwww.roshankunwar.com.np%2F_next%2Fimage%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fshorebird-list-releases.3385b99c.webp%26w%3D3840%26q%3D75" alt="Shorebird Console listing an app's releases" width="3248" height="2324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Insights
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Insights&lt;/strong&gt; tab shows how many people actually use your app.&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%2Fwww.roshankunwar.com.np%2F_next%2Fimage%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fshorebird-insights.ecb6f639.webp%26w%3D3840%26q%3D75" 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%2Fwww.roshankunwar.com.np%2F_next%2Fimage%3Furl%3D%252F_next%252Fstatic%252Fmedia%252Fshorebird-insights.ecb6f639.webp%26w%3D3840%26q%3D75" alt="Shorebird Insights tab showing active users" width="2720" height="1754"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DAU, WAU, MAU.&lt;/strong&gt; Daily, weekly and monthly active users, with the change against the previous period.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time range.&lt;/strong&gt; Switch between 24h, 7d, 28d, 84d and 12m, or set a custom range.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare.&lt;/strong&gt; Turn on "Previous 28 days" to draw the last period as a dotted line.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Insights count unique device launches. The same device launching twice counts once.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;That one-line bug taught us something: writing the fix is the easy part. Getting it to your users is what hurts. With store reviews, a fix can take 8 to 12 hours to go live. In that time, every crash is a real person having a bad day.&lt;/p&gt;

&lt;p&gt;Shorebird changed that for us. Setup took one sitting: install the CLI, run &lt;code&gt;shorebird init&lt;/code&gt;, and you're ready. After that, the flow is short:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Release&lt;/strong&gt; the build you send to the stores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview&lt;/strong&gt; it on a device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patch&lt;/strong&gt; it when something breaks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Our &lt;code&gt;dev&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt; flavors keep test patches away from real users. The Console shows what shipped, and Insights shows how many people are on it.&lt;/p&gt;

&lt;p&gt;A few things to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shorebird patches Dart code only. Native changes still need a store release.&lt;/li&gt;
&lt;li&gt;Patches apply on the next app restart, not instantly.&lt;/li&gt;
&lt;li&gt;Always try a patch on &lt;code&gt;dev&lt;/code&gt; before you push it to &lt;code&gt;prod&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shorebird is not a replacement for good testing or a proper release process. It is a safety net. When something slips through, you can fix it in minutes instead of waiting a day.&lt;/p&gt;

&lt;p&gt;If you ship a Flutter app to real users, set it up before you need it. We wish we had.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://www.roshankunwar.com.np/posts/ota-updates-in-flutter-with-shorebird-realtime-code-push-silent-updates" rel="noopener noreferrer"&gt;https://www.roshankunwar.com.np/posts/ota-updates-in-flutter-with-shorebird-realtime-code-push-silent-updates&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>shorebird</category>
      <category>ota</category>
    </item>
    <item>
      <title>Preparing for Your Flutter Interview: A Complete Guide</title>
      <dc:creator>Roshan Jung Kunwar</dc:creator>
      <pubDate>Fri, 20 Feb 2026 07:32:04 +0000</pubDate>
      <link>https://dev.to/rshnkunwar/preparing-for-your-flutter-interview-a-complete-guide-1k6b</link>
      <guid>https://dev.to/rshnkunwar/preparing-for-your-flutter-interview-a-complete-guide-1k6b</guid>
      <description>&lt;h1&gt;
  
  
  Flutter Interview Questions: Beginner to Advanced
&lt;/h1&gt;




&lt;h2&gt;
  
  
  🟢 Beginner
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is the difference between Stateless and Stateful Widgets?&lt;/li&gt;
&lt;li&gt;What is the difference between &lt;code&gt;final&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is the difference between runtime constant and compile-time constant?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;main()&lt;/code&gt; do vs &lt;code&gt;runApp()&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;context&lt;/code&gt; in Flutter?&lt;/li&gt;
&lt;li&gt;What are null-aware operators in Dart?&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;assert()&lt;/code&gt; and when is it active?&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;async/await&lt;/code&gt; and how does asynchronous programming work in Dart?&lt;/li&gt;
&lt;li&gt;What is the difference between a &lt;code&gt;Future&lt;/code&gt; and a &lt;code&gt;Stream&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is the difference between &lt;code&gt;EQUAL (=)&lt;/code&gt; and &lt;code&gt;LIKE&lt;/code&gt; in a SQL query?&lt;/li&gt;
&lt;li&gt;What are the different build modes in Flutter (Debug / Profile / Release)?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;flutter run --verbose&lt;/code&gt; do?&lt;/li&gt;
&lt;li&gt;What are Class Constructors in Dart? What types exist?&lt;/li&gt;
&lt;li&gt;What is a &lt;code&gt;typedef&lt;/code&gt; in Dart?&lt;/li&gt;
&lt;li&gt;What is the difference between &lt;code&gt;Expanded&lt;/code&gt; and &lt;code&gt;Flexible&lt;/code&gt;?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🟡 Intermediate
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is the &lt;code&gt;late&lt;/code&gt; keyword and when should you use it?&lt;/li&gt;
&lt;li&gt;What are extension methods and when would you use them?&lt;/li&gt;
&lt;li&gt;What are mixins and when should you use them?&lt;/li&gt;
&lt;li&gt;What are Keys in Flutter? (ValueKey, ObjectKey, UniqueKey, GlobalKey)&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;InheritedWidget&lt;/code&gt; and how does it work?&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;setState()&lt;/code&gt; and how does it relate to &lt;code&gt;markNeedsBuild()&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is the Widget lifecycle in a StatefulWidget?&lt;/li&gt;
&lt;li&gt;What are the three trees in Flutter? (Widget → Element → Render)&lt;/li&gt;
&lt;li&gt;What is the difference between Slivers and regular Widgets?&lt;/li&gt;
&lt;li&gt;What are Intrinsic Widgets and when should you use them?&lt;/li&gt;
&lt;li&gt;What is a Singleton and why would you use one?&lt;/li&gt;
&lt;li&gt;What is Dependency Injection (DI) and why is it useful?&lt;/li&gt;
&lt;li&gt;What is the difference between abstract, interface, and sealed classes? When do you use each?&lt;/li&gt;
&lt;li&gt;What is the difference between Bloc, Provider, and Riverpod?&lt;/li&gt;
&lt;li&gt;What are Freezed and Equatable packages? What is value vs reference equality?&lt;/li&gt;
&lt;li&gt;What is Tree Shaking and why does it matter?&lt;/li&gt;
&lt;li&gt;Why split-per-ABI when building an Android APK?&lt;/li&gt;
&lt;li&gt;What is MVVM vs MVC architecture?&lt;/li&gt;
&lt;li&gt;What are SOLID principles and how do they apply to Flutter?&lt;/li&gt;
&lt;li&gt;Functional vs class-based Widgets — which is recommended and why?&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;For full article visit on: &lt;a href="https://www.roshankunwar.com.np/posts/preparing-flutter-interview-guide" rel="noopener noreferrer"&gt;https://www.roshankunwar.com.np/posts/preparing-flutter-interview-guide&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>flutter</category>
      <category>interview</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Flutter Dio Interceptor for Caching API Responses</title>
      <dc:creator>Roshan Jung Kunwar</dc:creator>
      <pubDate>Thu, 08 Jan 2026 06:02:11 +0000</pubDate>
      <link>https://dev.to/rshnkunwar/flutter-dio-interceptor-for-caching-api-responses-1db3</link>
      <guid>https://dev.to/rshnkunwar/flutter-dio-interceptor-for-caching-api-responses-1db3</guid>
      <description>&lt;h2&gt;
  
  
  Flutter Dio Interceptor for Caching API Responses
&lt;/h2&gt;

&lt;p&gt;In modern mobile applications, providing a seamless user experience even with poor or no network connectivity is crucial. This article demonstrates how to implement a robust caching mechanism for API responses in Flutter using Dio interceptors, ensuring your app remains functional offline by serving cached data when network requests fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mechanism
&lt;/h2&gt;

&lt;p&gt;The caching interceptor works with a straightforward yet effective approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persists GET responses&lt;/strong&gt;: Only GET method responses are cached, as these typically represent data retrieval operations that are safe to cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network-first strategy&lt;/strong&gt;: When network connectivity is available, the interceptor fetches fresh data from the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback to cache&lt;/strong&gt;: If a network error occurs, the interceptor automatically falls back to previously cached responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic cache updates&lt;/strong&gt;: Successful responses automatically update the cache for future offline access&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating the Custom Interceptor
&lt;/h3&gt;

&lt;p&gt;To implement caching with Dio, we need to create a custom interceptor that implements the &lt;code&gt;Interceptor&lt;/code&gt; interface and handles three key lifecycle methods: &lt;code&gt;onRequest&lt;/code&gt;, &lt;code&gt;onResponse&lt;/code&gt;, and &lt;code&gt;onError&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage Abstraction
&lt;/h3&gt;

&lt;p&gt;First, let's define a storage abstraction layer that allows flexibility in choosing different storage mechanisms:&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;/// Simple key-value storage interface for caching&lt;/span&gt;
&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheStorage&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;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&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;key&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="kd"&gt;set&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;key&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;value&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;remove&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;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;/// Implementation using SharedPreferences (via AppPreference)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CacheStorageImpl&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;CacheStorage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CacheStorageImpl&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;_pref&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;AppPreference&lt;/span&gt; &lt;span class="n"&gt;_pref&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;Future&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="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&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;key&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;return&lt;/span&gt; &lt;span class="n"&gt;_pref&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="n"&gt;key&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;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="kd"&gt;set&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;key&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;value&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;_pref&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="n"&gt;key&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="nd"&gt;@override&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;remove&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;key&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;_pref&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&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="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 abstraction provides a clean interface for storage operations, making it easy to swap implementations (e.g., from SharedPreferences to Hive or secure storage) without changing the interceptor code.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Cache Interceptor
&lt;/h3&gt;

&lt;p&gt;Here's the complete implementation of the caching interceptor:&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:dio/dio.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;CacheInterceptor&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;Interceptor&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;CacheStorage&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;CacheInterceptor&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;storage&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;void&lt;/span&gt; &lt;span class="n"&gt;onRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RequestInterceptorHandler&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pass through all requests without modification&lt;/span&gt;
    &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&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="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ResponseInterceptorHandler&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Cache only GET responses&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;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestOptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_saveResponseToCache&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="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&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="p"&gt;}&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;onError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DioException&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrorInterceptorHandler&lt;/span&gt; &lt;span class="n"&gt;handler&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;// Try to retrieve cached data for GET requests&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;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestOptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;'GET'&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;cached&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;_getCachedResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestOptions&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;cached&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="c1"&gt;// Return cached response instead of error&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&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;// Show no internet modal for network errors&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;_isNetworkError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;NoInternetModalWidget&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="c1"&gt;// Propagate the error if no cache available&lt;/span&gt;
    &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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="n"&gt;_isNetworkError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DioException&lt;/span&gt; &lt;span class="n"&gt;err&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;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DioExceptionType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connectionTimeout&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DioExceptionType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendTimeout&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DioExceptionType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;receiveTimeout&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DioExceptionType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connectionError&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="n"&gt;Response&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;_getCachedResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestOptions&lt;/span&gt; &lt;span class="n"&gt;options&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;try&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;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;${options.uri}&lt;/span&gt;&lt;span class="s"&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;cacheEntry&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;storage&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="n"&gt;cacheKey&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;cacheEntry&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;return&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;final&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;decodedCache&lt;/span&gt; &lt;span class="o"&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;cacheEntry&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;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;requestOptions:&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;data:&lt;/span&gt; &lt;span class="n"&gt;decodedCache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;statusCode:&lt;/span&gt; &lt;span class="mi"&gt;200&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;catch&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="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="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;_saveResponseToCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;response&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;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;${response.realUri}&lt;/span&gt;&lt;span class="s"&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;cacheEntry&lt;/span&gt; &lt;span class="o"&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;response&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cacheEntry&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;
  
  
  How It Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Request Phase (&lt;code&gt;onRequest&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The interceptor doesn't modify outgoing requests, allowing them to proceed normally to the API endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Response Phase (&lt;code&gt;onResponse&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;When a successful response arrives, the interceptor checks if it's a GET request. If so, it saves the response to cache using the complete URI as the cache key:&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;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;${response.realUri}&lt;/span&gt;&lt;span class="s"&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 ensures each unique endpoint has its own cached response, preventing data conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Error Handling Phase (&lt;code&gt;onError&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This is where the magic happens. When a request fails, the interceptor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks if the failed request was a GET method&lt;/li&gt;
&lt;li&gt;Attempts to retrieve cached data using the request URI as the key&lt;/li&gt;
&lt;li&gt;If cached data exists, resolves the error by returning the cached response using &lt;code&gt;handler.resolve(cached)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If no cache exists, propagates the original error using &lt;code&gt;handler.next(err)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;_getCachedResponse&lt;/code&gt; method safely handles JSON decoding and returns &lt;code&gt;null&lt;/code&gt; if anything goes wrong, ensuring the app doesn't crash due to corrupted cache data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Injecting the Interceptor into Dio
&lt;/h3&gt;

&lt;p&gt;To use the cache interceptor, inject it when creating your Dio instance:&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:dio/dio.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;ApiClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;late&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Dio&lt;/span&gt; &lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;ApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppPreference&lt;/span&gt; &lt;span class="n"&gt;appPreference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;dio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;BaseOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;baseUrl:&lt;/span&gt; &lt;span class="s"&gt;'https://api.example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;connectTimeout:&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;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nl"&gt;receiveTimeout:&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;30&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;// Create cache storage implementation&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cacheStorage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CacheStorageImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appPreference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Add the cache interceptor&lt;/span&gt;
    &lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interceptors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheStorage&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Add other interceptors as needed (logging, auth, etc.)&lt;/span&gt;
    &lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interceptors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LogInterceptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;requestBody:&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;responseBody:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example API Call
&lt;/h3&gt;

&lt;p&gt;Once configured, your API calls work normally, with caching happening transparently:&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;ProductRepository&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;ApiClient&lt;/span&gt; &lt;span class="n"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;ProductRepository&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;apiClient&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;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getProducts&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;try&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;apiClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dio&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;'/products'&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;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&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="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Product&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;json&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;();&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="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Handle error or rethrow&lt;/span&gt;
      &lt;span class="k"&gt;rethrow&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;When network is available, this fetches fresh data and caches it. When offline, it returns the cached response without throwing an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h3&amp;gt;🔄 Transparent Caching&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;No changes required to existing API calls - caching works automatically&amp;lt;/p&amp;gt;



&amp;lt;h3&amp;gt;📴 Offline Resilience&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Automatic fallback to cached data during network failures&amp;lt;/p&amp;gt;



&amp;lt;h3&amp;gt;🎯 Clean Architecture&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Separation of concerns through storage abstraction&amp;lt;/p&amp;gt;



&amp;lt;h3&amp;gt;🔌 Easy Integration&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Simple integration with dependency injection patterns&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Advanced Enhancements
&lt;/h2&gt;

&lt;p&gt;Consider extending this implementation with the following features:&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Expiration
&lt;/h3&gt;

&lt;p&gt;Add timestamps to determine cache freshness:&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;_saveResponseToCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="n"&gt;response&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;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;${response.realUri}&lt;/span&gt;&lt;span class="s"&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;cacheData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;'data'&lt;/span&gt;&lt;span class="o"&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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'timestamp'&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="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cacheEntry&lt;/span&gt; &lt;span class="o"&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;cacheData&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;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cacheEntry&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;
  
  
  Cache Size Management
&lt;/h3&gt;

&lt;p&gt;Implement LRU (Least Recently Used) cache eviction:&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;CacheManager&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;int&lt;/span&gt; &lt;span class="n"&gt;maxCacheSize&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;CacheStorage&lt;/span&gt; &lt;span class="n"&gt;storage&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;evictOldestCache&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;// Implementation for removing oldest cached entries&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;
  
  
  Per-Endpoint Cache Strategies
&lt;/h3&gt;

&lt;p&gt;Configure different caching behaviors for different endpoints:&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;CacheConfig&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;Duration&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ttl&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;CacheConfig&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;ttl&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;enabled&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="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cacheConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s"&gt;'/products'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CacheConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;ttl:&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;hours:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="s"&gt;'/user/profile'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CacheConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;enabled:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Implementing a caching interceptor for Dio provides significant benefits for Flutter applications. This approach offers a robust offline-first experience where users can continue accessing previously loaded content even without internet connectivity. &lt;/p&gt;

&lt;p&gt;The storage abstraction pattern ensures flexibility and maintainability, while the interceptor seamlessly handles the complexity of cache management without cluttering your business logic. The network-first strategy ensures users always get fresh data when possible while maintaining functionality during connectivity issues.&lt;/p&gt;

&lt;p&gt;This interceptor provides a solid foundation for building resilient Flutter applications that gracefully handle network instability while maintaining excellent user experience. Start with this basic implementation and extend it based on your specific application requirements.&lt;/p&gt;





&lt;p&gt;Found this article helpful? Share it with your fellow Flutter developers!&lt;/p&gt;
&lt;br&gt;
  &lt;p&gt;Happy Coding!&lt;/p&gt;

&lt;p&gt;Find full article on:&lt;br&gt;
&lt;a href="https://www.roshankunwar.com.np/posts/flutter-dio-interceptor-for-caching-api-responses" rel="noopener noreferrer"&gt;https://www.roshankunwar.com.np/posts/flutter-dio-interceptor-for-caching-api-responses&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dio</category>
      <category>caching</category>
      <category>api</category>
    </item>
  </channel>
</rss>
