Flutter:如何使用drawImage方法在Canvas上绘制图片
ui.Image from ImageCodec for canvas.drawImage in Flutter Hey there! I totally get the confusion here—you’ve followed the docs to grab an ImageCodec, but now you need that final ui.Image instance to actually draw on your Canvas. Let’s break this down step by step to get you sorted.
The missing piece: Extracting ui.Image from the Codec
The instantiateImageCodec method gives you a Codec, which acts as a decoder for your image data. To get the usable ui.Image you need for drawing, you have to pull the first (or desired) frame from this codec using getNextFrame(). This method returns a FrameInfo object, which holds the actual ui.Image inside it.
Modified code for your _loadImage method
Here’s how to adjust your existing function to return a Future<ui.Image> instead of a Codec:
import 'dart:ui' as ui; import 'package:flutter/services.dart'; Future<ui.Image> _loadImage(AssetBundleImageKey key) async { // Fetch the raw image data from the asset bundle final ByteData data = await key.bundle.load(key.name); // Create the codec from the image data final ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List()); // Extract the first frame from the codec final ui.FrameInfo frameInfo = await codec.getNextFrame(); // Return the ready-to-use ui.Image return frameInfo.image; }
Using the ui.Image in a CustomPainter
Once you have your ui.Image, you can integrate it into a CustomPainter to draw on the Canvas. Here’s a complete example that handles the async load and drawing:
class MyCustomPainter extends CustomPainter { final ui.Image? image; MyCustomPainter(this.image); @override void paint(Canvas canvas, Size size) { if (image == null) return; // Draw the image at the top-left corner with original size canvas.drawImage(image!, Offset.zero, Paint()); // Optional: Scale the image to fit the entire canvas final scaledImage = ui.Image.resize( image!, width: size.width.toInt(), height: size.height.toInt() ); canvas.drawImage(scaledImage, Offset.zero, Paint()); } @override bool shouldRepaint(covariant MyCustomPainter oldDelegate) { return oldDelegate.image != image; } } // In your widget tree: FutureBuilder<ui.Image>( future: _loadImage(AssetBundleImageKey( bundle: rootBundle, name: 'assets/your_image.png', // Replace with your asset path scale: 1.0, )), builder: (context, snapshot) { if (snapshot.hasData) { return CustomPaint( painter: MyCustomPainter(snapshot.data), size: Size.infinite, ); } else { return const Center(child: CircularProgressIndicator()); } }, )
Quick tips to avoid headaches
- Dispose the image: When your widget is disposed, call
image?.dispose()to free up memory—this prevents leaks, especially if you’re loading multiple images. - Animated images: If you’re working with a GIF or other animated asset, you can loop through frames by calling
codec.getNextFrame()repeatedly. For static images, just the first frame is enough. - Asset setup: Double-check that your image is listed in the
assetssection of yourpubspec.yaml—otherwise the asset bundle won’t be able to find it.
内容的提问来源于stack exchange,提问作者Anderson Andrade

