You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:15:19