Contact Me
Blog

Embedded Images in Flutter

The most common way to incorporate an image into a Flutter app is to put the file in the assets directory, and use the Image widget to display it. When building for mobile or desktop, the assets directory gets bundled up with the app; but when building for the web it's served over HTTP(S). This can lead to a short delay while the image gets fetched, and also introduces a possible source of failure should the request fail.

Another option is to embed the image directly into a component, eliminating that risk as well as reducing the number of HTTP requests. That comes at a cost, of course — the image's data is now a part of the app bundle.

However, for small images that cost can be neglibible when used responsibly.

So how do we go about that?

In essence, this approach entails creating an Image widget from a Base64-encoded image.

The first step is to obtain the source of an image in Base64 format. Online tools such as this one are one way to do that.

Suppose we have the folowing PNG image:

Running that through the decoding process gives us the following:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAA+UlEQVR42u3TLU+CURyG8QvnmJubxcBms9hMTzGbNNu0+QUcyUq0Gg02bVI1mS0n2Sw2N4KFDcYYgxsC4eGMl8P/HBLn+n2AO91sXTcTG+yOISMabKQKD2jqkR0SV+UVlbyxR8L2+UCeTw5IVA2H5vjmiAQd84MW+OWEyApaaIl/zojonDZaocOl/VR9FGDArfVUCjTi3nCqMJbz7fKMDF6ohp3qHYWxnO+QLxTBUTOeymc53yl/KIEWRdip7Dpc4HVFDyXU59p4qvXPV6GBwtjOV8eVdFGkLq6kjpdDkRwz5YFkA02ePM20AwV+RR7IA3kgD+SB3DY1Brjz/Xio3c9AAAAAAElFTkSuQmCC

This is basically the same technique for rendering HTML images using data URLs as the src attribute.

Note that this particular encodeer includes information about the MIME type at the beginning:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAA+UlEQVR42u3TLU+CURyG8QvnmJubxcBms9hMTzGbNNu0+QUcyUq0Gg02bVI1mS0n2Sw2N4KFDcYYgxsC4eGMl8P/HBLn+n2AO91sXTcTG+yOISMabKQKD2jqkR0SV+UVlbyxR8L2+UCeTw5IVA2H5vjmiAQd84MW+OWEyApaaIl/zojonDZaocOl/VR9FGDArfVUCjTi3nCqMJbz7fKMDF6ohp3qHYWxnO+QLxTBUTOeymc53yl/KIEWRdip7Dpc4HVFDyXU59p4qvXPV6GBwtjOV8eVdFGkLq6kjpdDkRwz5YFkA02ePM20AwV+RR7IA3kgD+SB3DY1Brjz/Xio3c9AAAAAAElFTkSuQmCC

We don't want that for our purposes, so we're going to strip it out.

Create a new stateless widget, include a constructor and add the image data as a static constant:

class MyImage extends StatelessWidget {

  const MyImage();

  static const src = 'iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAQAAAD9CzEMAAAA+UlEQVR42u3TLU+CURyG8QvnmJubxcBms9hMTzGbNNu0+QUcyUq0Gg02bVI1mS0n2Sw2N4KFDcYYgxsC4eGMl8P/HBLn+n2AO91sXTcTG+yOISMabKQKD2jqkR0SV+UVlbyxR8L2+UCeTw5IVA2H5vjmiAQd84MW+OWEyApaaIl/zojonDZaocOl/VR9FGDArfVUCjTi3nCqMJbz7fKMDF6ohp3qHYWxnO+QLxTBUTOeymc53yl/KIEWRdip7Dpc4HVFDyXU59p4qvXPV6GBwtjOV8eVdFGkLq6kjpdDkRwz5YFkA02ePM20AwV+RR7IA3kgD+SB3DY1Brjz/Xio3c9AAAAAAElFTkSuQmCC';

  @override
  Widget build(BuildContext context) {
    // 
  }
}

In order to render this as an image, we need to decode it again, which we can do using the base64Decode method provided by the dart:convert package.

Start by importing that:

import 'dart:convert';

Now we can use the memory named constructor on the Image widget, like so:

class MyImage extends StatelessWidget {

  const MyImage();

  static const src = '...';

  @override
  Widget build(BuildContext context) {
    return Image.memory(base64Decode(src));
  }
}

Simple as that.

You'll find the complete code in this Gist, which you can play around with in Dartpad.