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

Flutter Canvas图片移动点问题:仅鼠标悬停按钮时才移动

解决Canvas上的点仅在按钮悬停时移动的问题

问题根源

  1. 定时器触发的refreshImage未触发UI更新:方法里仅创建ImagePainter实例,但未调用setState,Flutter框架无法感知状态变化,只有当其他操作(如按钮悬停)触发组件重建时,才会顺带更新点的位置。
  2. ImagePainter重绘逻辑错误:shouldRepaint固定返回false,即便坐标变化也不会触发重绘。
  3. 状态管理不规范:坐标索引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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 22:16:15