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

Flutter中setState未触发CustomPainter重绘,绘制功能失效

Flutter Canvas绘制问题:滑动时线条不生效,仅热重载后可见

我编写了一段在Canvas上绘制的Flutter代码,通过容器手势检测记录滑动偏移量,添加到offsets数组后传递给CustomPainter渲染线条。调用setState更新状态期望触发重绘,但滑动时线条不生效,仅热重载后可见。排查发现offsets正常添加、setState执行,但CustomPainter未被调用,请问原因是什么?

import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Home(),
    );
  }
}

// offsets
List<Offset?> offsets = [];

class Home extends StatefulWidget {
  const Home({
    Key? key,
  }) : super(key: key);

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  // offsets
  List<Offset?> offsets = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Drawing'),
      ),
      body: GestureDetector(
        onPanDown: (details) {
          setState(() {
            offsets.add(details.localPosition);
          });
        },
        onPanUpdate: (details) {
          setState(() {
            offsets.add(details.localPosition);
          });
        },
        onPanEnd: (details) {
          setState(() {
            offsets.add(null);
          });
        },
        child: Container(
          width: double.infinity,
          height: MediaQuery.of(context).size.height,
          color: Colors.grey,
          child: CustomPaint(
            foregroundPainter: Painter(offsets: offsets),
          ),
        ),
      ),
    );
  }
}

class Painter extends CustomPainter {
  Painter({required this.offsets});

  List<Offset?> offsets;

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..strokeWidth = 2
      ..style = PaintingStyle.stroke;

    // for every offset and the next offset
    for (int i = 0; i < offsets.length - 1; i++) {
      // if both are not null
      if (offsets[i] != null && offsets[i + 1] != null) {
        canvas.drawLine(offsets[i]!, offsets[i + 1]!, paint);
      } else if (offsets[i] != null && offsets[i + 1] == null) {
        canvas.drawPoints(PointMode.points, [offsets[i]!], paint);
      }
    }
    print('paint called');
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

问题原因

核心问题出在CustomPainter的shouldRepaint方法上,你当前返回了false,这会告诉Flutter:新旧CustomPainter实例没有差异,不需要重绘。

当调用setState时,Home组件会重建,CustomPaint会创建新的Painter实例,但因为shouldRepaint返回false,Flutter会跳过重绘流程,新的offsets自然不会被渲染。而热重载时整个组件树重新初始化,才会触发一次paint调用。

修复方案

修改shouldRepaint方法,对比新旧Painter的offsets是否发生变化,有变化则返回true触发重绘:

// 需要先导入package:flutter/foundation.dart
import 'package:flutter/foundation.dart';

@override
bool shouldRepaint(covariant Painter oldDelegate) {
  return listEquals(oldDelegate.offsets, offsets);
}

另外,代码中同时存在全局offsets变量和State内的同名变量,建议移除全局变量,避免逻辑混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 05:45:38