最小可重复代码:
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Offset> _points = [];
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {}), // This setState works
child: Icon(Icons.refresh),
),
body: GestureDetector(
onPanUpdate: (details) => setState(() => _points.add(details.localPosition)), // but this doesn't...
child: CustomPaint(
painter: MyCustomPainter(_points),
size: Size.infinite,
),
),
);
}
}
class MyCustomPainter extends CustomPainter {
final List<Offset> points;
MyCustomPainter(this.points);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.red;
for (var i = 0; i < points.length; i++) {
if (i + 1 < points.length) {
final p1 = points[i];
final p2 = points[i + 1];
canvas.drawLine(p1, p2, paint);
}
}
}
@override
bool shouldRepaint(MyCustomPainter oldDelegate) => false;
}
试着在屏幕上长时间拖动来画一些东西,你看不到画的任何东西。现在,按下FAB,它将显示绘制的绘画,可能是因为FAB调用setState
,但onPanUpdate
也调用setState
,并且该调用不会在屏幕上绘制任何内容。为什么?
注意:我不是在寻找如何启用油漆的解决方案,一个简单的return true
就可以了。我需要知道的是,为什么一个setState
能工作(在屏幕上作画(,而另一个却失败了。
要了解onPanUpdate
中的setState((不起作用的原因,您可能需要查看小部件paint Renderer,即CustomPaint
。
CustomPaint(如文档所述(在完成该帧的渲染后访问画家对象(在您的情况下为MyCustomPainter
(。为了确认,我们可以查看CustomPainter的来源。我们可以看到只有当我们通过setter访问CCD_ 10对象时,CCD_。为了更清楚地了解RenderCustomPaint
的来源,您肯定会理解它:
void _didUpdatePainter(CustomPainter? newPainter, CustomPainter? oldPainter) {
// Check if we need to repaint.
if (newPainter == null) {
assert(oldPainter != null); // We should be called only for changes.
markNeedsPaint();
} else if (oldPainter == null ||
newPainter.runtimeType != oldPainter.runtimeType ||
newPainter.shouldRepaint(oldPainter)) { //THIS
markNeedsPaint();
}
.
.
.
}
在每次setState调用中,您的点都在更新,但每次创建"MyCustomPainter"的新实例时,都会创建小部件树,但由于上述原因,画家尚未绘制。
这就是为什么调用markNeedPaint()
(即绘制对象(的唯一方法是将true
返回到shouldRepaint
,或者oldDeleagate
为null(仅发生这种情况(,并且CustomPainter
的第一个UI构建,您可以在列表中提供一些默认点来验证这一点。
还指出
即使在[shouldRepaint]返回false(例如,如果祖先或后代需要重新喷漆(。也有可能是〔paint〕方法将在根本没有调用[shouldRepaint]的情况下被调用(例如,如果框改变大小(。
所以setState of Fab在这里工作的唯一原因(哪个接缝有效(是Fab正在以某种方式重建自定义画家的任何父级。你也可以尝试在"web构建"中调整UI的大小,或者使用dartpad,你会发现当父级重建自己时,点会变得可见,所以setState直接与shouldRepaint无关。即使将鼠标悬停在fab(在dartpad中(按钮上,也会导致ui重建,因此可以看到点。