2
votes

In my Flutter project, I use a CustomPainter to draw a custom shape. In that CustomPainter, I need to draw a png image, which is available in my assets folder in multiple sizes, so I can get the right image for the right screen density:

assets
   \1.5x
      image.png   // 54 x 54 pixels
   \2.0x
      image.png   // 72 x 72 pixels
   \3.0x
      image.png   // 108 x 108 pixels
   \4.0x
      image.png   // 144 x 144 pixels
   image.png      // 36 x 36 pixels

Here is how I load my image, to get an Image file:

import 'dart:ui' as ui;

Future<ui.Image> getImage() async {
   AssetImage assetImage = AssetImage("assets/image.png");
   ImageStream stream = assetImage.resolve(createLocalImageConfiguration(context));
   Completer<ui.Image> completer = Completer();
   stream.addListener(ImageStreamListener((Imageinfo image, _) {
      return completer.complete(image.image);
   }
   return completer.future;
}

And in my CustomPainter.paint() function, here is how I draw the Image, once loaded:

@override
void paint(Canvas canvas, Size size) {
   // ...
   canvas.drawImage(
      myImage, // <- the loaded image
      Offset(20, 20),
      Paint()
   );
}

I have two problems:

  • the image isn't drawn at the right size (it appears with a size of, let's say, 54 points in a HDPI screen, while the loaded image has a size of 54 pixels... is it a Flutter bug?)
  • even if I use canvas.drawImageRect() to draw an image of 36 x 36 points, the drawn image is sometimes still glitchy, depending on the image. And yes, I doubled checked the size of the original images.

So what should I do to draw on my canvas the right image for the right screen density, so it's drawn properly?

Thanks.

1
tried to use AssetImage? - pskink
@pskink I was thinking of it, but I don't understand how to use it, do you have an example, by any chance? - matteoh
check ImageProvider official docs: they have one example - pskink
thanx for info, so you can use your drawImageRect too :-) - pskink
sure, your welcome, merry Xmas :-) - pskink

1 Answers

3
votes

Here is how I finally got it to work (thanks @pskink):

Step 1: return the whole ImageInfo object, and not only the image:

Future<ImageInfo> getImageInfo(BuildContext context) async {
   AssetImage assetImage = AssetImage("assets/image.png");
   ImageStream stream = assetImage.resolve(createLocalImageConfiguration(context));
   Completer<ImageInfo> completer = Completer();
   stream.addListener(ImageStreamListener((Imageinfo imageInfo, _) {
      return completer.complete(imageInfo);
   }
   return completer.future;
}

Step 2: use the ImageInfo.scale property, and some filtering, to draw the image:

@override
void paint(Canvas canvas, Size size) {
   // ...
   paintImage(
      canvas: canvas,
      rect: Rect.fromLTWH(
         20, 20,
         myImageInfo.width / myImageInfo.scale,
         myImageInfo.height / myImageInfo.scale),
      image: myImageInfo.image,                // <- the loaded image
      filterQuality: FilterQuality.low,
   );
}