Flutter Canvas图片移动点问题:仅鼠标悬停按钮时才移动
解决Canvas上的点仅在按钮悬停时移动的问题
问题根源
- 定时器触发的
refreshImage未触发UI更新:方法里仅创建ImagePainter实例,但未调用setState,Flutter框架无法感知状态变化,只有当其他操作(如按钮悬停)触发组件重建时,才会顺带更新点的位置。 ImagePainter重绘逻辑错误:shouldRepaint固定返回false,即便坐标变化也不会触发重绘。- 状态管理不规范:坐标索引
i及xList/yList使用全局变量,状态无法被Flutter正确跟踪。
修改后的完整代码
import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class five_og extends StatefulWidget { @override State createState() { return _five_og(); } } class _five_og extends State<five_og> { ui.Image? image; Timer? timer; // 将索引和坐标列表移到状态类内部管理 int i = 0; List<double> xList = [/* 填入你的X坐标列表 */]; List<double> yList = [/* 填入你的Y坐标列表 */]; void refreshImage() { setState(() { if (i >= xList.length - 1) { i = 0; } else { i++; } }); } @override void dispose() { timer?.cancel(); // 用?避免空指针异常 super.dispose(); } @override void initState() { super.initState(); loadImage('assets/images/Erdgeschoss.png'); timer = Timer.periodic( const Duration(milliseconds: 500), (timer) => refreshImage(), ); } Future loadImage(String path) async { final data = await rootBundle.load(path); final bytes = data.buffer.asUint8List(); final image = await decodeImageFromList(bytes); setState(() { this.image = image; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Test'), ), body: Center( child: image == null ? const CircularProgressIndicator() : Container( height: 600, width: 600, child: FittedBox( child: SizedBox( width: image!.width.toDouble(), height: image!.height.toDouble(), // 修正之前错误的width为height,保证图片比例正确 child: CustomPaint( // 将当前坐标传递给Painter painter: ImagePainter(image!, Offset(xList[i], yList[i])), ), ), ), ), ), ); } } class ImagePainter extends CustomPainter { final ui.Image image; final Offset currentPoint; // 去掉const修饰,因为参数是动态变化的 ImagePainter(this.image, this.currentPoint); @override void paint(Canvas canvas, Size size) { canvas.drawImage(image, Offset.zero, Paint()); final paint = Paint() ..color = const Color.fromARGB(255, 15, 182, 253) ..style = PaintingStyle.fill; canvas.drawCircle(currentPoint, 13, paint); } @override bool shouldRepaint(ImagePainter oldDelegate) { // 仅当坐标变化时才重绘,优化性能 return oldDelegate.currentPoint != currentPoint; } }
关键修改点说明
- 触发UI更新:在
refreshImage中用setState包裹索引更新逻辑,让Flutter感知状态变化并重建组件。 - Painter重绘控制:给
ImagePainter传入当前点坐标,在shouldRepaint中对比新旧坐标,仅变化时才重绘,避免不必要的性能消耗。 - 状态内聚:把索引和坐标列表移到状态类内部,避免全局变量带来的状态混乱。
- 修复小错误:修正
SizedBox的height属性之前误写为width的问题,保证图片显示比例正确。
内容的提问来源于stack exchange,提问作者David Wild
相关产品推荐
相关产品推荐

