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
相关产品推荐
相关产品推荐

