Flutter – Drawing images with the CustomPainter

The CustomPainterer provides a method to draw an image on the canvas. First, however, you must know how to load your images from your assets and offer them to your CustomPainter. Luckily, when you know how to do this, this is not hard! Let’s quickly get started. The first step is to add the image to your assets. You have to define this asset in the pubspec.yaml.

flutter:
  assets:
  - hello.png

Now you can access the image in your Flutter Application. However, most examples use the image directly. For instance, with the Image Widget, you only have to provide the asset. But to draw this image on the canvas, you need to load this image first.

Future<ui.Image> _loadImage(String imageAssetPath) async {
  final ByteData data = await rootBundle.load(imageAssetPath);
  final codec = await ui.instantiateImageCodec(
    data.buffer.asUint8List(),
    targetHeight: 300,
    targetWidth: 300,
  );
  var frame = await codec.getNextFrame();
  return frame.image;
}

The method to load the image is an asynchronous process. There are multiple ways to deal with this. One way is to use the Future Builder to load the image. When the image is loaded, you can provide the image to the CustomPainter. Another way would be to load all your assets before starting the Application.

class ImageOnCanvas extends StatelessWidget {
  const ImageOnCanvas({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<ui.Image>(
      future: _loadImage("assets/hello.png"),
      builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.waiting:
            return const Text('Image loading...');
          default:
            if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            } else {
              return Center(
                child: CustomPaint(
                  child: const SizedBox(
                    width: 300,
                    height: 300,
                  ),
                  painter: ImagePainter(snapshot.data!),
                ),
              );
            }
        }
      },
    );
  }
}

Finally, you can draw the image with the CustomPainter. This step is probably the easiest step because the CustomPainter provides a method to draw images. You can then call this method with the image, and this will draw the image on the canvas!

class ImagePainter extends CustomPainter {
  final ui.Image image;

  ImagePainter(this.image);

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawImage(image, const Offset(0, 0), Paint());
  }

  @override
  bool shouldRepaint(ImagePainter oldDelegate) {
    return false;
  }
}

This way, you can draw your images with the CustomPainter. You can find the complete code of this example on Github. If you have any suggestions, questions, or remarks, feel free to let me know!

Leave a Reply