如何在触摸CustomPainter绘制的图形时绘制提示框矩形?
问题解决:CustomPainter触摸时绘制提示框
错误原因分析
- 直接在触摸回调中绘制报错:
paint方法执行完毕后,Canvas的原生资源会被系统回收,此时再调用canvas.drawRect会触发native peer已被回收的错误,因为Canvas对象已失效。 - 修改变量后无绘制效果:修改状态变量后没有触发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
相关产品推荐
相关产品推荐

