Flutter Canvas drawImage持续报Object has been disposed错误
自定义绘制流程中drawImage触发
Object has been disposed的原因说明 异常触发的核心逻辑
Flutter渲染管线对每帧绘制的Canvas对象有严格的生命周期约束:
- 当某一帧的绘制流程启动时,引擎会创建绑定到当前帧的原生Canvas实例,传入CustomPainter的
paint方法中。 - 只要
paint方法的同步执行流程完全结束,引擎就会认为当前帧的所有绘制指令已经收集完成,立刻回收当前绑定的Canvas对象、以及关联的所有渲染上下文资源,这个回收过程是自动的,不会等待paint方法中声明的异步回调执行完成。
你代码中触发报错的drawImage逻辑,完全嵌套在三层异步回调链路里:
rootBundle.load是异步文件IO操作ui.instantiateImageCodec是异步图片解码操作codec.getNextFrame是异步帧获取操作
整个异步链路走完的耗时,远高于单帧的绘制周期(单帧渲染通常在16ms内完成)。等所有异步回调真正执行、调用canvasWrapper.drawImage的时候,之前绑定的Canvas实例早就被引擎回收dispose了,此时操作已销毁的对象,自然会抛出Object has been disposed错误。
同实例下drawCircle可以正常运行的原因
drawCircle的调用是在_drawDetails方法的同步代码块中完成的:当执行到这行代码时,整个调用栈还处于paint方法的同步执行阶段,Canvas对象还未被引擎回收,绘制指令会立刻被写入当前帧的渲染队列,完全不等异步回调触发就已经完成了提交。
哪怕等后续异步回调执行时Canvas已经被销毁,drawCircle的同步调用早已执行完毕,自然不会触发异常。
正确的实现方式
drawImage的异常本质是把本应提前完成的异步资源准备工作,放到了paint的同步执行流里做,修正方向非常明确:
- 绝对不要在paint方法内部发起异步图片加载、解码流程,也不要在paint方法里声明async逻辑(CustomPainter的paint方法本身是同步签名,声明async后返回的Future不会被引擎监听,只会让异步绘制时序完全脱离帧生命周期)
- 把图片加载、解码流程提前到paint触发之前完成,将解码完成的可绘制
ui.Image对象缓存为自定义Painter的成员变量,图片加载完成后主动触发一次重绘 - 重绘触发后,在paint的同步执行流程中,直接使用缓存好的Image对象调用drawImage即可
参考正确结构:
class MyCustomPainter extends CustomPainter { // 提前缓存解码完成的图片对象 ui.Image? cachedBkImage; MyCustomPainter({this.cachedBkImage}); @override void paint(Canvas canvas, Size size) { // 所有绘制逻辑全部同步执行 if (cachedBkImage != null) { canvas.drawImage(cachedBkImage!, const Offset(10, 20), Paint()); } final paint1 = Paint() ..color = const Color(0xff63aa65) ..style = PaintingStyle.fill; canvas.drawCircle(const Offset(15, 25), 8.0, paint1); } @override bool shouldRepaint(covariant MyCustomPainter oldDelegate) { // 图片加载完成后缓存更新时触发重绘 return oldDelegate.cachedBkImage != cachedBkImage; } } // 在业务组件中提前异步加载图片 Future<void> loadAssetImage() async { final bd = await rootBundle.load("assets/test1.png"); final lst = Uint8List.view(bd.buffer); final codec = await ui.instantiateImageCodec(lst); final frameInfo = await codec.getNextFrame(); // 把解码完成的图片传给Painter,触发重绘 painter.cachedBkImage = frameInfo.image; setState(() {}); }
补充注意点:代码中出现的
canvasWrapper.save()与canvasWrapper.restore()必须成对匹配,否则也会引发栈结构错乱的相关异常。
内容的提问来源于stack exchange,提问作者user8012596
相关产品推荐
相关产品推荐

