Efficient Image Caching in Flutter with Riverpod

TL;DR :-
Discover how to optimize image caching in Flutter using Riverpod for improved app performance.
Learn step-by-step techniques for manual caching, reducing network load, and enhancing UI smoothness without external libraries
When you build a Flutter application, images are often a vital part, and as a developer, you have to deal with them on a daily basis, whether you’re building a product gallery, a social media feed, or a daily inspiration screen.
But if you’re not optimizing them, you’ll be downloading these images from the internet every time, and your app will be slow and you’ll use lots of data.
This guide walks you through a practical way to cache images manually and manage them efficiently using Riverpod, Flutter’s powerful state management tool.
We’ll skip external libraries and show how to achieve smooth, responsive image loading with tools already at your disposal.
Who This Is For
This guide is for Flutter developers dealing with network images. No matter if you are a new developer looking for ways to make a performant app, or an intermediate developer looking to power up a responsive, intelligent, cache-aware image gallery, this article will walk you through a set of tools and techniques for making your app more efficient and enjoyable for users.
Why Cache Images?
Here’s the thing: every time you request an image from a URL, you’re essentially making a call out to the network. Imagine doing that every single time when the same image needs to be displayed.
Doesn’t make much sense, right? But here’s where image caching saves the day.
When you cache an image, you’re saying to your app, “Remember this one for the next time around.” This means that when users scroll, navigate, and return to a screen, the app doesn’t need to go back to the network to fetch the same image repeatedly. It’s already there, ready and waiting.
Struggling with Flutter projects? Let Us Handle It!
Building efficient Flutter apps can be challenging. Don’t let slow performance hold you back. Hire Soft Suave’s developers to take your app to the next level.
Hire Our Experts
Key Benefits of Image Caching:
These are the main reasons why caching is vital
Performance Boost: Cached images load immediately from local storage. This speeds up rendering and provides a smoother & faster experience..
Reduced Network Load: Your app saves bandwidth and makes fewer server requests by not having to download images again.
Smoother UI: With cached images, there’s no delay or flicker during loading. This ensures a seamless, uninterrupted user experience.
Tools We’ll Use
These are the tools that we’ll be using in this guide
Flutter – for UI and native image rendering
Riverpod – for reactive state management
Standard Dart APIs – to cache images manually in memory
Step-by-step process to cache an image in Flutter with Riverpod
Take a look at the simple step-by-step process to cache images in Flutter with Riverpod
Step 1: Define Your Image URL Provider
First, create a simple StateNotifier to manage a list of image URLs:
import 'package:flutter_riverpod/flutter_riverpod.dart';
final imageUrlsProvider = StateNotifierProvider<ImageUrlNotifier, List<String>>(
(ref) => ImageUrlNotifier(),
);
class ImageUrlNotifier extends StateNotifier<List<String>> {
ImageUrlNotifier() : super([]);
void addImage(String url) {
state = [...state, url];
}
void removeImage(String url) {
state = state.where((img) => img != url).toList();
}
}Step 2: Implement a Basic In-Memory Cache
We’ll manually cache images using a Map<String, Uint8List> to avoid re-fetching the same image.
final imageCacheProvider = Provider<ImageCacheManager>((ref) {
return ImageCacheManager();
});
class ImageCacheManager {
final _cache = <String, Uint8List>{};
Future<Uint8List> getImageBytes(String url) async {
if (_cache.containsKey(url)) return _cache[url]!;
final response = await NetworkAssetBundle(Uri.parse(url)).load(url);
final bytes = response.buffer.asUint8List();
_cache[url] = bytes;
return bytes;
}
void clearCache() {
_cache.clear();
}
void removeImage(String url) {
_cache.remove(url);
}
}Step 3: Build the Image Gallery Widget
Use Image.memory() instead of Image.network() for cached image data:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ImageGallery extends ConsumerWidget {
const ImageGallery({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final imageUrls = ref.watch(imageUrlsProvider);
final cacheManager = ref.watch(imageCacheProvider);
return ListView.builder(
itemCount: imageUrls.length,
itemBuilder: (context, index) {
final url = imageUrls[index];
return FutureBuilder<Uint8List>(
future: cacheManager.getImageBytes(url),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Padding(
padding: EdgeInsets.all(16),
child: Center(child: CircularProgressIndicator()),
);
}
if (snapshot.hasError || !snapshot.hasData) {
return const Icon(Icons.error);
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: Image.memory(snapshot.data!),
);
},
);
},
);
}
}Step 4: Add Images Dynamically
Add a new image URL to the list using a simple button:
ElevatedButton(
onPressed: () {
ref.read(imageUrlsProvider.notifier).addImage(
'https://example.com/newimage.jpg',
);
},
child: const Text("Add Image"),
)Step 5: Manual Cache Control
You can expose cache management buttons (optional):
ElevatedButton(
onPressed: () {
ref.read(imageCacheProvider).clearCache();
///(or we can remove a single Image using ref.read(imageCacheProvider).removeImage('https://example.com/image.jpg');)
},
child: const Text("Clear All Cached Images"),
),Supercharge Your Flutter App’s Performance with Professional Help
Hire Soft Suave’s team to improve your app’s performance and create high-quality solutions tailored to your needs.
Get Expert HelpAdvantages of Using Riverpod
These are the advantages of using Riverpod for caching images
Scoped Memory Usage: Riverpod manages memory efficiently and prevents unnecessary memory usage. It keeps state alive only as long as necessary.
Separation of Concerns: Riverpod makes sure that state management is isolated from UI logic, which results in a clean and maintainable app.
Testability: Riverpod’s architecture makes it easy to mock providers, which means you can easily unit test your code without relying on an overly complex UI.
Reactivity: Riverpod uses a reactive model, so your app only updates when it has to, boosting performance.
Final Result
With this approach:
Images are downloaded once and stored in memory
They are rendered instantly once cached
You can dynamically add/remove images
No external packages are needed
Conclusion
While Flutter packages like cached_network_image offer automatic solutions, building your own image caching logic gives you more control and understanding of how caching works.
Using Riverpod for reactive state and in-memory storage for image bytes, this guide showed you how to cache images without plugins, dynamically manage image state, and keep your UI fast and clean.
Want persistent disk caching? You could extend this with path_provider and file storage. But for most use cases, in-memory caching gives a significant performance win.
Ramesh Vayavuru is the Founder & CEO of Soft Suave Technologies, with 15+ years of experience delivering innovative IT solutions.




