Introduction
Progressive image loading improves perceived performance in mobile development by displaying a low-resolution placeholder while the full image loads. In Flutter, CachedNetworkImage handles caching and network requests, but placeholders typically rely on static or loading spinners. BlurHash bridges that gap by generating a compact string representation of an image, which you can decode into a blurred preview. In this tutorial, you’ll learn how to integrate BlurHash with CachedNetworkImage to deliver smooth, visually pleasing placeholders for your Flutter app.
Understanding BlurHash
BlurHash is an algorithm that converts an image into a short ASCII string. This string captures color and brightness information in a coarse grid. You send the BlurHash string alongside your image URL. On the client side, a small decoding function reconstructs a blurred version of the original. The result is a lightweight, automatically generated preview that fits in memory and network budgets.
Key points:
The BlurHash string is typically generated on the server or build step.
Decoding in Flutter requires only milliseconds.
The decoded blur is displayed as a placeholder until the high-resolution image appears.
Implementing Progressive Loading with CachedNetworkImage and BlurHash
First, add dependencies in your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
cached_network_image: ^3.2.0
blurhash_flutter
Import the packages where you render images:
import 'package:cached_network_image/cached_network_image.dart';
import 'package:blurhash_flutter/blurhash_flutter.dart';
Use CachedNetworkImage with a custom placeholderBuilder that decodes the BlurHash string:
CachedNetworkImage(
imageUrl: imageUrl,
placeholder: (context, url) => BlurHash(
hash: blurHash,
imageFit: BoxFit.cover,
),
errorWidget: (context, url, error) => Icon(Icons.error_outline),
fit: BoxFit.cover,
);Explanation:
imageUrl: URL of the full-resolution image.
placeholder: A widget displayed while loading. Here we instantiate BlurHash with your hash.
errorWidget: A fallback icon if the network request fails.
fit: Ensures the image and placeholder fill the container.
Customizing Placeholders and Error Widgets
You can adjust the placeholder decoding resolution, colors, and fade durations for a more refined effect. For example, wrap BlurHash in an AnimatedOpacity to cross-fade smoothly:
Widget blurredPlaceholder(String blurHash) {
return AnimatedOpacity(
opacity: 1.0,
duration: Duration(milliseconds: 500),
child: BlurHash(
hash: blurHash,
decodingWidth: 32,
decodingHeight: 32,
imageFit: BoxFit.cover,
),
);
}
CachedNetworkImage(
imageUrl: imageUrl,
placeholder: (ctx, url) => blurredPlaceholder(blurHash),
errorWidget: (ctx, url, err) => Center(
child: Container(
color: Colors.grey[200],
child: Icon(Icons.broken_image, size: 48),
),
),
);Tips:
decodingWidth/decodingHeight control blur detail vs performance.
Use BoxDecoration with a background color that matches your design.
Fade in the high-res image by specifying fadeInDuration.
Best Practices
Precompute BlurHash strings during your asset pipeline or server build step. Avoid computing on the client at runtime for production.
Store the BlurHash alongside each image in your database or metadata service.
Use a consistent grid size for BlurHash (e.g., 4×3) to balance placeholder quality and string length.
Cache network images aggressively by configuring cache sizes and eviction policies in CachedNetworkImage.
Test on varying network conditions using Flutter’s network profiler or external tools.
Applying these best practices ensures your placeholders always appear quickly, even on slow connections.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Progressive image loading with CachedNetworkImage and BlurHash elevates user experience in Flutter mobile development. By displaying a low-resolution, blurred preview before the full image arrives, you avoid blank states and loading spinners. Integrating BlurHash requires minimal code changes and leverages existing caching mechanisms. Follow the steps and best practices outlined above to create smooth, polished image transitions in your Flutter apps.