14 March 2019
Categories: Software Development
Posted in: Flutter, Dart, Android, iOS, Web, Image, Widget
* Note: the complete Dart source code is at the end of this article
Goals
Preload asset images in a stateful widget on Flutter
When we need to use images in Flutter we can load them into an Image widget. The following code creates several images from assets:
@override
void initState() {
super.initState();
image1 = Image.asset("assets/image1.png");
image2 = Image.asset("assets/image2.png");
image3 = Image.asset("assets/image3.png");
image4 = Image.asset("assets/image4.png");
}
The images will actually be only loaded once the widget is built, so there will be a gap (empty space) in the display the first time that the image is built. This gap can be more or less disturbing depending on image size and provider (asset, network, file, etc.).
If you're rotating through a list of images, you could set the property gaplessPlayback to true so that the previous image would still be visible while the next image is loaded:
@override
void initState() {
super.initState();
image1 = Image.asset("assets/image1.png", gaplessPlayback: true);
image2 = Image.asset("assets/image2.png", gaplessPlayback: true);
image3 = Image.asset("assets/image3.png", gaplessPlayback: true);
image4 = Image.asset("assets/image4.png", gaplessPlayback: true);
}
Unfortunately, gaplessPlayback will eliminate the empty image during the transition to the next image, but it does nothing to the time it takes to load the next image.
If we want to obtain a a smooth, real-time transition between images, we need to preload them so that they will be readily available when you need them. To do so, we can use Flutter's precacheImage function:
precacheImage(image1.image, context);
Please note that if you try to use the above precacheImage function within the initState function, you will get an error that tells you that "references to to inherited widgets should occur in widget build() methods" and not in initState.
Since we want to preload our images as soon as our widget is initialized, we can place our precacheImage code in the didChangeDependencies method, which is called after initState and whenever the dependencies change thereafter, as follows:
@override
void didChangeDependencies() {
super.didChangeDependencies();
precacheImage(image1.image, context);
precacheImage(image2.image, context);
precacheImage(image3.image, context);
precacheImage(image4.image, context);
}
Now that all images have been preloaded, we can use them in our build method and there will be no gaps in the visualization of the widget when rotating through the images.
Putting it all together, please find a complete Flutter sample below.
Happy Coding! ;)
class _SampleWidgetState extends State<SampleWidget> {
Image image1;
Image image2;
Image image3;
Image image4;
@override
void initState() {
super.initState();
image1 = Image.asset("assets/image1.png");
image2 = Image.asset("assets/image2.png");
image3 = Image.asset("assets/image3.png");
image4 = Image.asset("assets/image4.png");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
precacheImage(image1.image, context);
precacheImage(image2.image, context);
precacheImage(image3.image, context);
precacheImage(image4.image, context);
}
@override
Widget build(BuildContext context) {
return Container(
child: Stack(
children: <Widget>[
image1,
image2,
image3,
image4,
],
),
);
}
}