Flutter - 如何使用DrawImage方法在Canvas上绘制图像

13

我正在尝试将图像文件绘制到画布上,以在Flutter中组合我的小部件。

我确实遵循了canvas文档,但没有成功。Image文档中说:

要获取Image对象,请使用instantiateImageCodec。

我尝试使用instantiateImageCodec方法,但只得到一个Codec实例,而不是Image

使用canvas.drawImage绘制到画布上获取ui.Image实例的正确方法是什么?

这是我的代码片段:

Future<ui.Codec> _loadImage(AssetBundleImageKey key) async {
  final ByteData data = await key.bundle.load(key.name);
   if (data == null)
  throw 'Unable to read data';
   return await ui.instantiateImageCodec(data.buffer.asUint8List());
}

final Paint paint = new Paint()
  ..color = Colors.yellow
  ..strokeWidth = 2.0
  ..strokeCap = StrokeCap.butt
  ..style = PaintingStyle.stroke;

var sunImage = new ExactAssetImage("res/images/grid_icon.png");

sunImage.obtainKey(new ImageConfiguration()).then((AssetBundleImageKey key){
  _loadImage(key).then((ui.Codec codec){
    print("frameCount: ${codec.frameCount.toString()}");
    codec.getNextFrame().then((info){
      print("image: ${info.image.toString()}");
      print("duration: ${info.duration.toString()}");
      canvas.drawImage(info.image, size.center(Offset.zero), paint);
    });
  });
});
4个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
12

这个简单的实用方法会返回一个Future<UI.Image>,其中包含图片资源的路径:

import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as UI;

import 'package:flutter/services.dart';

Future<UI.Image> loadUiImage(String imageAssetPath) async {
  final ByteData data = await rootBundle.load(imageAssetPath);
  final Completer<UI.Image> completer = Completer();
  UI.decodeImageFromList(Uint8List.view(data.buffer), (UI.Image img) {
    return completer.complete(img);
  });
  return completer.future;
}

7

ui.Codec有一个方法getNextFrame(),它返回一个Future<FrameInfo>(如果您知道它总是一幅普通图片,那么可以跳过围绕多少帧的逻辑)。FrameInfo具有一个image属性,这是您需要的图像。

编辑:查看您在帖子中的代码后,不清楚您在做什么。所有定义是否都在CustomPainter.paint方法中?如果是,您肯定会遇到问题,因为您只能在paint调用期间使用canvas; 您不应保留对它的任何引用函数外部(包括任何异步调用)。

我建议使用FutureBuilder,以便您只有在添加图像后才在画布上绘制。

它将类似于此:

Future<Image> _loadImage(AssetBundleImageKey key) async {
  final ByteData data = await key.bundle.load(key.name);
  if (data == null)
    throw 'Unable to read data';
  var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
  // add additional checking for number of frames etc here
  var frame = await codec.getNextFrame();
  return frame.image;
}

new FutureBuilder<Image>(
  future: loadImage(....), // a Future<String> or null
  builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.waiting: return new Text('Image loading...');
      default:
        if (snapshot.hasError)
          return new Text('Error: ${snapshot.error}');
        else
          // ImageCanvasDrawer would be a (most likely) statless widget
          // that actually makes the CustomPaint etc
          return new ImageCanvasDrawer(image: snapshot.data)
    }
  },
)

感谢您回答我的问题!我已经根据您的建议更新了我的代码,现在可以访问Image对象。但是当我尝试使用canvas方法进行绘制时,Flutter会抛出一个“Object has been disposed.”的异常。 - Anderson Andrade
这是我的异常: - Anderson Andrade
请看我的编辑,其中使用了FutureBuilder的示例。请注意,您可以使用StatefulWidget,并在图像加载之前将其传递为null(并在画布中优雅地处理null),然后将图像设置为StatefulWidget。 - rmtmckenzie
@KTWorks,您是直接复制了上面的代码吗?听起来您的异步调用出现了一些奇怪的问题。 - rmtmckenzie
1
问题出在 ui.image 导入上。你应该提醒导入自 ui.image。 - otto
显示剩余3条评论

2

以下是一个简单的示例,基于Simon的想法。

import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

Future<ui.Image> loadImage(String imageName) async {
  final data = await rootBundle.load('assets/image/$imageName');
  return decodeImageFromList(data.buffer.asUint8List());
}

late ui.Image image;

void main() async {
  image = await loadImage('image.jpg');
  runApp(App());
}

// canvas.drawImage(image, Offset.zero, Paint());

0
class ImagePainter extends CustomPainter {
  List<ui.Image> images = new List<ui.Image>();
  ImagePainter(
      {Key key,
      @required this.noOfSlice,
      @required this.images,
      @required this.rotation,
      this.boxfit = BoxFit.contain})
      :
        // : path = new Path()
        //     ..addOval(new Rect.fromCircle(
        //       center: new Offset(75.0, 75.0),
        //       radius: 40.0,
        //     )),
        tickPaint = new Paint() {
    tickPaint.strokeWidth = 2.5;
  }
  final int noOfSlice;
  final tickPaint;
  final BoxFit boxfit;
  ui.ImageByteFormat img;
  ui.Rect rect, inputSubrect, outputSubrect;
  Size imageSize;
  FittedSizes sizes;
  double radius,
      rotation = 0.0,
      _x,
      _y,
      _angle,
      _radiun,
      _baseLength,
      _imageCircleradius,
      _incircleRadius,
      _imageOffset = 0.0,
      _imageSizeConst = 0.0;

  @override
  void paint(ui.Canvas canvas, ui.Size size) {
    print("image data:: $images");
    radius = size.width / 2;
    _angle = 360 / (noOfSlice * 2.0);
    _radiun = (_angle * pi) / 180;
    _baseLength = 2 * radius * sin(_radiun);
    _incircleRadius = (_baseLength / 2) * tan(_radiun);
    if (noOfSlice == 4) {
      _imageOffset = 30.0;
      _imageSizeConst = 30.0;
      _x = 8.60;
      _y = 4.10;
    } else if (noOfSlice == 6) {
      _imageOffset = 20.0;
      _x = 10.60;
      _y = 5.60;
    } else if (noOfSlice == 8) {
      _imageOffset = 40.0;
      _imageSizeConst = 30.0;
      _x = 12.90;
      _y = 6.60;
    }

    //print("circle radisu:: $_incircleRadius");

    canvas.save();
    canvas.translate(size.width / 2, size.height / 2);
    canvas.rotate(-rotation);
    int incr = 0;
    rect = ui.Offset((size.width / _x), size.width / _y) & new Size(0.0, 0.0);

    imageSize = new Size(size.width * 1.5, size.width * 1.5);
    sizes = applyBoxFit(
        boxfit,
        imageSize,
        new Size(size.width / 2 * .50 + _incircleRadius * .8,
            size.width / 2 * .50 + _incircleRadius * .8));
    inputSubrect =
        Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
    outputSubrect = Alignment.center.inscribe(sizes.destination, rect);
    if (images.length == noOfSlice && images.isNotEmpty)
      for (var i = 1; i <= noOfSlice * 2; ++i) {
        if (i % 2 != 0) {
          canvas.drawLine(
            new Offset(0.0, 0.0),
            new Offset(0.0, size.width / 2 - 4.2),
            tickPaint,
          );
        } else {
          canvas.save();
          canvas.translate(-0.0, -((size.width) / 2.2));
          ui.Image image = images[incr];
          if (image != null) {
            canvas.drawImageRect(
                image, inputSubrect, outputSubrect, new Paint());
          }

          canvas.restore();
          incr++;
        }
        canvas.rotate(2 * pi / (noOfSlice * 2.0));
      }
    canvas.restore();
  }

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,