Flutter CustomPainter中setState与shouldRepaint的耦合机制及重绘行为差异
核心逻辑拆解
你观察到的现象,是Flutter渲染管线两个独立机制共同作用的结果,和setState的调用场景没有任何关系:
- 两个触发setState的场景没有任何优先级差异,执行效果完全对等
- CustomPaint的重绘触发,既需要它自身的主动标记,也会被所在绘制层的整体重绘被动牵连
首先澄清:两个setState都没有失效
不管是FAB点击触发的空setState,还是onPanUpdate回调里往_points列表加元素的setState,执行逻辑完全一致:
- 都会把
_HomePageState对应的Element节点标记为dirty状态 - 都会在下一帧的构建阶段重新执行
build方法,生成新的Widget树 - 都会正常把拖动过程中收集到的所有
Offset存入_points列表
不存在“手势回调里的setState没生效”的情况——如果它真的没有正常执行,你点击FAB的时候根本不会看到之前积累的所有线条。
为什么拖动过程中看不到绘制内容
CustomPaint的绘制流程和State的build流程是完全解耦的:
- build流程只是生成新的Widget对象,告诉渲染树“这里放着一个CustomPaint,用MyCustomPainter绘制,传入的点集合是
_points” - 真正决定要不要重新调用paint方法画内容的权限,在CustomPaint对应的RenderCustomPaint对象手里:当它收到新的Widget配置时,会对比新旧两个painter实例,调用你重写的
shouldRepaint方法——只有这个方法返回true,它才会给自己标记needsPaint,通知渲染管线“下一帧我需要重新执行绘制逻辑”。
你的代码里把shouldRepaint固定返回false,意味着不管传入的_points列表内容变化了多少,RenderCustomPaint永远不会主动给自己标记needsPaint。
再加上你没有给CustomPaint添加RepaintBoundary做绘制隔离,整个Scaffold下的所有组件(包括CustomPaint、FAB)都共享同一个根绘制层。当拖动事件触发build流程后,没有任何一个RenderObject被标记为needsPaint(CustomPaint因为shouldRepaint返回false不主动标记,GestureDetector、Scaffold本身也因为没有内容变化不需要标记),整个根绘制层没有被标记为dirty状态,渲染管线会直接复用上一帧的绘制缓存——也就是初始的空白页面,根本不会重新调用CustomPaint的paint方法,自然看不到任何线条。
为什么点击FAB后,之前的线条会全部显示
当你点击FAB的时候,会触发一个连锁反应:
- FAB自身因为点击会触发内置的水波纹交互效果,主动给自己标记
needsPaint - 因为CustomPaint和FAB共享同一个根绘制层,FAB的needsPaint标记会把整个根绘制层标记为dirty
- 当渲染管线绘制这个被标记为dirty的根绘制层时,会遍历层下所有覆盖在屏幕范围内的RenderObject,不管这些对象有没有被单独标记为needsPaint,都会调用它们的paint方法重新绘制内容
这时候CustomPaint的paint方法就被被动触发了,它从一直持续存储的_points列表里取出所有的坐标点,按顺序把所有线段画出来,你就看到了之前拖动时积累的全部内容。
内容的提问来源于stack exchange,提问作者iDecode
相关产品推荐
相关产品推荐

