Preload images in a stateful widget on Flutter

* Note: the complete Dart source code is at the end of this article

Goals

  • Preload images used in a widget so that there will be no gaps when showing them

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<SamleWidget> {
  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,
            ],
          ),
        );
  }
}
comments powered by Disqus