有关如何更新ListView
的文档here说:
在Flutter中,如果要更新内部的窗口小部件列表 setState(),您将很快看到您的数据没有更改 视觉上。这是因为当调用setState()时,Flutter 渲染引擎查看小部件树以查看是否有任何东西 改变了。当到达ListView时,它将执行==检查,然后 确定两个ListView相同。什么也没有变, 因此不需要更新。
对于更新ListView的简单方法,请在其中创建一个新List setState(),然后将数据从旧列表复制到新列表。
在这种情况下,我不了解渲染引擎如何确定小部件树中是否有任何更改。
AFAICS,我们关心调用setState
,该标记将State
对象标记为脏对象并要求其重建。重建之后,会有一个新的ListView
,不是吗?那么==
支票为什么说它是同一对象呢?
此外,新的List
将位于State
对象的内部,Flutter引擎是否会比较State
对象内部的所有对象?我以为它只比较了Widget
树。
因此,基本上,我不了解渲染引擎如何决定要更新的内容以及要忽略的内容,因为我看不到如何创建新的List
来将任何信息发送到渲染引擎,正如文档所述,渲染引擎只是在寻找新的ListView
...而AFAIK会寻找新的List
不会创建新的ListView
。
答案 0 :(得分:8)
颤振不仅由Widgets组成。
呼叫setState
时,将小部件标记为脏。但是,此小部件实际上并不是您在屏幕上渲染的。
存在用于创建/更改RenderObjects的小部件;这些RenderObjects在屏幕上绘制内容。
RenderObjects和Widget之间的链接使用一种新型的Widget:RenderObjectWidget(例如LeafRenderObjectWidget)
在某种程度上,Flutter提供的大多数小部件都是RenderObjectWidget,包括ListView。
典型的RenderObjectWidget示例如下:
class MyWidget extends LeafRenderObjectWidget {
final String title;
MyWidget(this.title);
@override
MyRenderObject createRenderObject(BuildContext context) {
return new MyRenderObject()
..title = title;
}
@override
void updateRenderObject(BuildContext context, MyRenderObject renderObject) {
renderObject
..title = title;
}
}
此示例使用小部件创建/更新RenderObject。仅仅通知框架还有一些东西需要重画。
要重新渲染RenderObject,必须在所需的renderObject上调用markNeedsPaint
或markNeedsLayout
。
这通常是由RenderObject本身使用自定义字段设置器通过以下方式完成的:
class MyRenderObject extends RenderBox {
String _title;
String get title => _title;
set title(String value) {
if (value != _title) {
markNeedsLayout();
_title = value;
}
}
}
请注意if (value != previous)
。
此检查可确保在不更改任何内容的情况下重建窗口小部件时,Flutter不会中继或重绘任何内容。
正是由于这种确切条件,使List
或Map
发生突变并不能使ListView
重新呈现。它基本上具有以下内容:
List<Widget> _children;
List<Widget> get children => _children;
set children(List<Widget> value) {
if (value != _children) {
markNeedsLayout();
_children = value;
}
}
但是这意味着,如果您对列表进行变异而不是创建一个新列表,则RenderObject不会被标记为需要重新布局/重新绘制。因此,不会进行任何视觉更新。