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.