How to use BlurHash as an image placeholder in Flutter? Find out in this tutorial.
Loading an image from the network may take a few seconds to complete, depending on the connection speed. While the image is being fetched, it's quite common to show a placeholder. There are some techniques for showing a placeholder. For example, you can show a colored box. However, it would be nicer if the placeholder can be similar to the actual image. For that purpose, you can use BlurHash.
BlurHash is a compact representation of a placeholder for an image. It works by generating a hash string from an image. The generated hash string will be used to render the placeholder. This tutorial shows how to decode the BlurHash string to be rendered as an image placeholder in a Flutter application.
Using flutter_blurhash
Package
The Flutter Community has created a package called flutter_blurhash
which makes it easier for us to render a placeholder based on the given hash string.
Add Dependency
To use the flutter_blurhash
, you need to add it as a dependency in the dependencies
section of pubspec.yaml
file. This tutorial uses version 0.6.0 of the package.
dependencies:
flutter_blurhash: ^0.6.0
Then, run flutter pub get
to install the package.
Display Blurhash Image
To use the package, you need to add the below import statement.
import 'package:flutter_blurhash/flutter_blurhash.dart';
After that, you can call the constructor.
const BlurHash({
required String hash,
Key? key,
Color color = Colors.blueGrey,
BoxFit imageFit = BoxFit.fill,
double decodingWidth = _DEFAULT_SIZE,
double decodingHeight = _DEFAULT_SIZE,
String image,
VoidCallback? onDecoded,
VoidCallback? onReady,
VoidCallback? onStarted,
Duration duration = const Duration(milliseconds: 1000),
Curve curve = Curves.easeOut,
})
The constructor requires you to pass a parameter hash
which is the hash string computed using the BlurHash algorithm. If you don't already have the hash string, you can use their official website blurha.sh to generate the hash string from the image that you want to use.
Below is an example which only passes the hash
argument. Because the Image
argument is not passed, it will show the placeholder forever.
const BlurHash(
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
),
Output:
Next is another example where the image
argument is passed with a remote URL of an image as the value.
const BlurHash(
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
image: 'https://www.psdstack.com/wp-content/uploads/2019/08/copyright-free-images-750x420.jpg',
),
Output:
Set Size
In most cases, you also need to set the size of the area where the placeholder is rendered (which will be occupied by the image after it has been loaded). The easiest way to set the size is by wrapping the BlurHash
widget as the child of another widget with size constraints. For example, you can set the BlurHash
widget as the child of a SizedBox
widget.
const SizedBox(
width: 200,
height: 200,
child: const BlurHash(
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
image: 'https://www.psdstack.com/wp-content/uploads/2019/08/copyright-free-images-750x420.jpg',
),
)
Output:
Set Image Fit
You can set how to fit the image to the available space by passing a BoxFit
enum as the imageFit
argument to the constructor. If you don't pass the argument, the value defaults to BoxFit.fill
. The usage of imageFit
argument is similar to the fit
property of FittedBox
. You can read our tutorial about FittedBox
which shows the differences between the BoxFit
enums.
The below example sets the imageFit
to BoxFit.fitWidth
const SizedBox(
width: 200,
height: 200,
imageFit: BoxFit.fitWidth,
child: const BlurHash(
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
image: 'https://www.psdstack.com/wp-content/uploads/2019/08/copyright-free-images-750x420.jpg',
),
)
Output:
The above screenshot shows the output when the image has been loaded. As you can see, if the image doesn't fill the entire box, a portion of the placeholder is still visible. You may need a workaround to handle that problem. But if you know the size or the aspect ratio from the beginning, you can adjust the size of the widget by applying size constraints (usually by using Container
, SizedBox
, BoxConstraints
, etc.) or set the aspect ratio using AspectRatio
widget.
Set Animation Duration
After the image has been successfully downloaded, the image will be animated for a given duration before completely shown. The duration can be set by passing a Duration
value as the duration
argument. The default value if you don't pass the argument is 1 second.
const SizedBox(
width: 200,
height: 200,
duration: const Duration(seconds: 3),
child: const BlurHash(
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
image: 'https://www.psdstack.com/wp-content/uploads/2019/08/copyright-free-images-750x420.jpg',
),
)
Output:
Set Animation Curve
The animation curve can be set by passing curve
argument. The default value is Curves.easeOut
.
const SizedBox(
width: 200,
height: 200,
curve: Curves.bounceInOut,
child: const BlurHash(
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
image: 'https://www.psdstack.com/wp-content/uploads/2019/08/copyright-free-images-750x420.jpg',
),
)
Output:
Add Callback Functions
There are some callback functions you can pass to the constructor.
onDecoded
: Called when the hash is decoded.onStarted
: Called when the download starts.onReady
: Called when the image is downloaded.
All of them are VoidCallback
, which has no arguments and returns no data. Below is the example of how to pass the callback functions.
BlurHash(
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
image: 'https://www.psdstack.com/wp-content/uploads/2019/08/copyright-free-images-750x420.jpg',
onStarted: () {
print('onStarted');
},
onDecoded: () {
print('onDecoded');
},
onReady: () {
print('onReady');
},
)
BlurHash
- Parameters
Key? key
: The widget's key.required String hash
: The hash to decode.color
: Displayed background color before decoding. Defaults toColors.blueGrey
.imageFit
: How to fit the decoded hash and the downloaded image. Defaults toBoxFit.fill
.double decodingWidth
: Decoding definition. Defaults to 32.double decodingHeight
: Decoding definition. Defaults to 32.String image
: Remote resource to download.VoidCallback? onDecoded
: A callback to be called when the hash is decoded.VoidCallback? onReady
: A callback to be called when the hash is downloaded.VoidCallback? onStarted
: A callback to be called when the download starts.Duration duration
: The animation duration. Defaults toconst Duration(milliseconds: 1000)
.Curve curve
: The animation curve. Defaults toCurves.easeOut
.
Full Code
import 'package:flutter/material.dart';
import 'package:flutter_blurhash/flutter_blurhash.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Woolha.com Flutter Tutorial',
home: BlurHashExample(),
);
}
}
class BlurHashExample extends StatelessWidget {
@override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("BlurHash")),
body: Center(
child: SizedBox(
width: 200,
height: 200,
child: BlurHash(
imageFit: BoxFit.fitWidth,
duration: const Duration(seconds: 3),
curve: Curves.bounceInOut,
hash: 'LsG*BvEeM{n#-tR%W9oI5Tw[xCay',
image: 'https://www.psdstack.com/wp-content/uploads/2019/08/copyright-free-images-750x420.jpg',
onStarted: () {
print('onStarted');
},
onDecoded: () {
print('onDecoded');
},
onReady: () {
print('onReady');
},
),
),
),
),
);
}
Summary
That's how to decode a BlurHash-encoded value to be displayed as an image placeholder. It can be done easily thanks to flutter_blurhash
package.