Flutter中paintImage随机无法绘制标记点问题排查
Flutter paintImage绘制标记点偶尔不显示的排查与修复
核心问题:paint方法使用异步返回类型
CustomPainter的paint方法必须是同步函数,你这里把它声明为Future<void>是完全错误的。Flutter的绘制流程是帧驱动的,每帧的绘制必须在当前帧周期内完成,异步方法会让标记点的绘制逻辑延迟到帧结束后执行——此时画布已经提交,自然看不到标记点,而且这种问题会因为异步调度的不确定性偶尔出现。
修复:把paint方法的返回类型改成void,去掉async关键字:
@override void paint(Canvas canvas, Size size) { // 保留原绘制逻辑,移除async修饰符 }
其他可能引发偶现问题的点
1. imagePin未完成加载
如果imagePin是通过AssetImage异步加载的,直接传入paintImage时可能还没加载完成,此时绘制会无内容。必须确保imagePin是已加载完成的ui.Image对象:
- 可以在Widget的
initState中提前加载图片,加载完成后再更新Painter:
late ui.Image imagePin; @override void initState() { super.initState(); AssetImage('assets/pin.png').resolve(ImageConfiguration()).addListener( ImageStreamListener((info, _) { setState(() { imagePin = info.image; }); }), ); }
只有当imagePin加载完成后,再传入MyMapPainter,避免绘制空内容。
2. 坐标计算错误:用外部尺寸而非画布实际尺寸
你在计算标记点位置时用了传入的width和height,但CustomPaint的实际绘制尺寸是size.width和size.height,如果两者不一致,标记点可能会被绘制到画布外,导致看不到:
// 错误:使用外部传入的width/height Rect.fromLTWH(item.dx * width - 16, item.dy * height - 16, 32, 32) // 正确:使用画布实际size Rect.fromLTWH(item.dx * size.width - 16, item.dy * size.height - 16, 32, 32)
3. shouldRepaint逻辑过于激进
当前shouldRepaint总是返回true,会导致频繁重绘,虽不是偶现不显示的直接原因,但可能引发其他绘制异常。应该对比新旧Painter的参数是否变化:
@override bool shouldRepaint(MyMapPainter oldDelegate) { return oldDelegate.backgroundImage != backgroundImage || !listEquals(oldDelegate.listOfPins, listOfPins) || oldDelegate.imagePin != imagePin; }
修复后的完整Painter代码
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'dart:ui' as ui; class MyMapPainter extends CustomPainter { final ui.Image backgroundImage; final List<Offset> listOfPins; final ui.Image imagePin; MyMapPainter({ required this.backgroundImage, required this.listOfPins, required this.imagePin, }); @override void paint(Canvas canvas, Size size) { if (kDebugMode) { print("Start painting with list of ${listOfPins.length} map pins."); } // 绘制背景图 paintImage( canvas: canvas, rect: Rect.fromLTWH(0, 0, size.width, size.height), image: backgroundImage, fit: BoxFit.scaleDown, filterQuality: FilterQuality.high, ); // 绘制标记点 for (final Offset item in listOfPins) { if (kDebugMode) { print("painting item with x: ${item.dx} y: ${item.dy}"); } paintImage( canvas: canvas, image: imagePin, rect: Rect.fromLTWH( item.dx * size.width - 16, item.dy * size.height - 16, 32, 32, ), ); } } @override bool shouldRepaint(MyMapPainter oldDelegate) { return oldDelegate.backgroundImage != backgroundImage || !listEquals(oldDelegate.listOfPins, listOfPins) || oldDelegate.imagePin != imagePin; } }
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

