本文是对Flutter中的Key详解的补充,建议读本文前先读完Flutter中的Key详解。
在Widget类的定义当中,有定义这样一个canUpdate函数:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
这个函数用于判断Element树中某个位置上的Element是否可以被复用,可以看到,只有当新、旧Widget的runtimeType和key都相等的时候,该Element才会被复用。如果没有设置key,那么他们的key就都是null,此时就只需要判断新、旧Widget的runtimeType即可。
在Flutter中的Key详解中有这样一段描述:
上面👆红框内的描述调整如下:
我们再来看上面的例子,当交换了两组件的位置之后,依次有序遍历Element树中各Element节点,首先Element树中第一位置存储了数字2的element发现widget树中第一位置新的Widget(newWidget)和element中关联的旧的widget(oldWidget)一致(未设置Key,并且类型一样),因此就将该element与newWidget建立了对应关系,此时就复用了Element中存储的State中的数字;同样的道理,Element树中第二位置存储了数字1的Element发现,Widget树中第二位置上新的Widget(newWidget)与该element中关联的旧的Widget(oldWidget)一致(未设置Key,并且类型一样),也就将该element与newWidget建立了对应关系,此时就复用了Element中存储的state中的数字。最终的结果就是,虽然Widget被交换了位置,但是所有的Element还是按照原来的位置被重新复用了,Element中存储的State中的数字也就被复用了;同时因为Element的复用,当颜色发生变化的时候,RenderObject也不会被销毁重建,只是修改了Widget中配置的颜色,然后再渲染到UI上。
在增加了Key之后,Flutter中的Key详解中的描述如下:
上面👆红框内的描述更新如下:
再次交换两组件的位置,我们发现颜色和数字都发生了变化。分析如下:
依次有序遍历Element树中各Element节点,首先遍历到Element树中第一位置存储了数字2的Element,获取到该Element中存储的oldWidget,然后与Widget树中第一位置上的最新的Widget(newWidget)对比,发现二者不一致(类型一样,但key不相同),此时Element不会立刻被销毁,而是会继续在Widget树的同级目录下逐个查找,如果能找到和旧的Widget(oldWidget)一致的新的Widget(newWidget),那么该Element还是会被保存下来复用,并重新建立Element和新widget位置的对应关系;相反,如果没有找到一致的,那么旧的Element就会被销毁而重新创建,而一旦Element被销毁,那么其中保存的state也会丢失,对应的旧的RenderObject也会被销毁。
如上所述,Element树中第一位置存储了数字2的Element会继续对比Widget树中第二位置的widget,此时发现一致(因为类型一致并且Key一致),则建立对应关系并复用Element;同理,Element树中第二位置存储了数字1的Element对比发现widget树中第一位置的widget跟旧的widget一致,也建立了对应的关系并复用Element,这样,最终因为加了Key,Element也随Key准确对应到了新的widget上(也可以理解成,新的Widget通过Key准确找到了旧widget对应的element)。
由此可见,Key是非常重要的,所以我们自己在封装Widget组件的时候,一定要将Key暴露出来。
以上。