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

如何在触摸CustomPainter绘制的图形时绘制提示框矩形?

问题解决:CustomPainter触摸时绘制提示框

错误原因分析

  1. 直接在触摸回调中绘制报错:paint方法执行完毕后,Canvas的原生资源会被系统回收,此时再调用canvas.drawRect会触发native peer已被回收的错误,因为Canvas对象已失效。
  2. 修改变量后无绘制效果:修改状态变量后没有触发Flutter重绘,paint方法不会重新执行,所以看不到提示框。

正确实现方案

核心思路:通过状态变量控制提示框的显示/隐藏和位置,触摸时更新状态并触发重绘,由paint方法统一绘制提示框。

步骤1:在StatefulWidget中维护状态

假设你的图表组件是StatefulWidget,在State类中管理提示框的状态:

class ChartWidget extends StatefulWidget {
  @override
  _ChartWidgetState createState() => _ChartWidgetState();
}

class _ChartWidgetState extends State<ChartWidget> {
  bool _showTooltip = false;
  Offset _tooltipPos = Offset.zero;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: _ChartPainter(
        showTooltip: _showTooltip,
        tooltipPos: _tooltipPos,
        onTap: (Offset localPos) {
          setState(() {
            _showTooltip = true;
            _tooltipPos = localPos;
          });
        },
        // 传入你的barData、lineData等其他参数
      ),
    );
  }
}

步骤2:修改CustomPainter实现

class _ChartPainter extends CustomPainter {
  final bool showTooltip;
  final Offset tooltipPos;
  final Function(Offset) onTap;
  // 声明你的barData、lineData等参数

  _ChartPainter({
    required this.showTooltip,
    required this.tooltipPos,
    required this.onTap,
    // 初始化其他参数
  });

  @override
  void paint(Canvas canvas, Size size) {
    touchable.TouchyCanvas touchCanvas = touchable.TouchyCanvas(context, canvas);

    // 绘制柱状图、折线图等原有逻辑(paintBars、paintLines)
    // 在paintBars/paintLines的触摸回调中调用onTap:
    // 示例:
    // touchCanvas.drawRect(barRect, onTapDown: (details) {
    //   onTap(details.localPosition); // 使用localPosition而非globalPosition,匹配Canvas坐标
    // });

    // 绘制提示框
    if (showTooltip) {
      Paint tooltipPaint = Paint()
        ..color = Colors.blue.withOpacity(0.8)
        ..style = PaintingStyle.fill;

      // 调整提示框位置,避免挡住触摸点
      final tooltipRect = Rect.fromLTWH(
        tooltipPos.dx + 10,
        tooltipPos.dy - 50,
        120,
        40,
      );

      // 绘制圆角背景
      canvas.drawRRect(
        RRect.fromRectAndRadius(tooltipRect, Radius.circular(8)),
        tooltipPaint,
      );

      // 绘制提示文本(可选)
      final textPainter = TextPainter(
        text: TextSpan(
          text: '数据:xxx',
          style: TextStyle(color: Colors.white, fontSize: 14),
        ),
        textDirection: TextDirection.ltr,
      );
      textPainter.layout();
      textPainter.paint(
        canvas,
        Offset(
          tooltipRect.center.dx - textPainter.width / 2,
          tooltipRect.center.dy - textPainter.height / 2,
        ),
      );
    }
  }

  @override
  bool shouldRepaint(_ChartPainter oldDelegate) {
    // 状态变化时触发重绘
    return oldDelegate.showTooltip != showTooltip ||
        oldDelegate.tooltipPos != tooltipPos;
  }
}

关键注意事项

  • 禁止直接在触摸回调操作Canvas:paint方法执行完毕后Canvas资源已回收,直接调用绘制方法必然报错。
  • 必须触发重绘:通过setState(StatefulWidget)或ChangeNotifier(配合Listenable)通知Flutter重新执行paint方法。
  • 坐标转换:使用触摸事件的localPosition而非globalPosition,确保提示框位置与Canvas坐标匹配。
  • shouldRepaint实现:准确判断状态变量是否变化,返回true时才会触发重绘,避免不必要的性能消耗。

内容的提问来源于stack exchange,提问作者bluends

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:05:20