引言:
在Flutter中,Key是一个非常重要的概念,它可以帮助我们优化性能,避免重建问题,并确保Widget正确地进行更新。本文将对Flutter Key进行详细解析,并介绍如何在实际开发中灵活应用。
一. 什么是Key?
Key是Flutter中的一个抽象类,它用于标识Widget。每个Flutter Widget都可以使用Key作为唯一标识符来识别自己,也可以用于将新的Widget与旧的Widget进行比较。Key本身没有任何具体实现,但它的子类有多种类型可供选择,如ValueKey、ObjectKey等。
具体分类如下:
1.1Key的类型在Flutter中,Key主要有以下几种类型:
-
ValueKey:这是最常用的Key类型,它将一个值赋予给Key。当两个ValueKey的值相同时,Flutter框架会认为这两个Key是相同的。
-
ObjectKey:与ValueKey类似,但是ObjectKey使用的是对象的引用。
-
UniqueKey:每次创建都会生成一个唯一的Key。
-
GlobalKey:这是一个全局唯一的Key,可以在整个应用中引用
-
LocalKey
LocalKey 直接继承至 Key,它应用于拥有相同父 Element 的小部件进行比较的情况,也就是上述例子中,有一个多子 Widget 中需要对它的子 widget 进行移动处理,这时候你应该使用Localkey。它也是我们日常用到的比较多的key。
Localkey 派生出了许多子类 key:
- ValueKey : ValueKey(‘String’)
- ObjectKey : ObjectKey(Object)
- UniqueKey : UniqueKey()
- Valuekey 又派生出了 PageStorageKey : PageStorageKey(‘value’)
注:如果是statelessWidget,那么就没必要使用key了,因为每一帧widget都会重建;而statefulWidget由于使用了state,state当中通常会缓存很多需要在UI上展示的变量,它不会时常重建,所以就需要使用key来强制改变。
- ValueKey
可以传入任何对象,比较的时候会对比它的内容是否一致。
operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ValueKey<T>
&& other.value == value;
}
bool
- ObjectKey
可以传入任何参数,比较的时候会比较它的指针是否一致,即是否为同一个对象。
operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ObjectKey
&& identical(other.value, value);
}
bool
- UniqueKey
不需要传任何参数,每次刷新都会生成一个新的值,通常用于动画的过渡当中。比如:
AnimatedSwitcher(
duration: const Duration(seconds: 1),
child: Text("no keyrrths", key: UniqueKey()),
)
每次改变文字时,假如不传uniqueKey,就不会有动画的渐变效果,而如果传了UniqueKey,则会有渐变动画效果。因为不传uniqueKey时,每次都只会认为text的widget发生了变化,只会将text的widget给替换为新的widget,而element还是同一个不会变化,所以会认为UI没有发生变化,因此不会改变;而如果传了uniqueKey时,每次widget比较时都会因为自身的key不一致而被认为是不同的widget,导致会重建element和renderObject,前后两个UI不一致,此时就会发生动画效果。
- GlobalKey
全局变量,顾名思义,整个应用程序里都是唯一的,所以同一个globalKey只能作用在一个widget上。可以通过globalKey拿到所对应的state和element或者widget,用以改变state的状态或者变量值,以及刷新UI。不过不推荐这样做,一般推荐通过控制外面的变量来刷新UI。
floatingActionButton: FloatingActionButton(
onPressed: (){
var state = (_globalKey as BoxState);
state.count++;
state.setState(() {
});
},
child: const Icon(Icons.add),
),
二. Key的作用
2.1 唯一标识符
Key的主要作用是唯一标识Widget。当Widget结构发生变化时,Flutter会根据Key来识别旧Widget和新Widget的不同,以确定是否需要进行重建。如果没有Key,Flutter将无法区分不同的Widget,会将它们全部重建,导致性能下降。
2.2 优化性能
通过Key,Flutter可以高效地更新Widget树中的部分子树,而不是重新构建整个Widget树。这样可以避免不必要的重建,提高性能。
2.3 避免重建问题
在某些情况下,我们希望保持Widget的状态不变,即使Widget的位置发生了改变。Key可以帮助我们解决这个问题,通过给Widget添加唯一的Key,确保在更新Widget树时能够正确地保留原来的状态,而不是将其重置为默认值。
三. Key的使用示例
3.1 构建列表
在构建列表时,我们经常会用到ListView.builder,此时我们需要使用IndexedWidgetBuilder作为itemBuilder参数。为了确保列表项的正确显示和更新,我们需要给每个列表项添加唯一的Key,通常使用ValueKey或ObjectKey。
List<String> items = ["item1", "item2", "item3"];
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(items[index]), // 或者使用ObjectKey(items[index])
title: Text(items[index]),
);
},
);
3.2 更新Widget
当我们需要更新一个已经存在的Widget时,如果Widget的类型发生了变化,Flutter会认为这是一个不同的Widget,而不仅仅是属性的变化,从而会重新构建整个部分子树。为了避免这种重建,我们可以在更新时使用相同的Key。
ValueKey("myKey") // 使用相同Key更新Widget
四. Key的使用规范
4.1 唯一性
每个Widget应该有一个唯一的Key,这样Flutter才能正确地识别它们的不同。如果出现相同的Key,Flutter将会报错并抛出异常。
4.2 稳定性
Key应该是稳定的,不应该随着Widget的属性变化而变化。如果Key随着Widget的属性改变而改变,那么Flutter将会认为这是一个不同的Widget,导致重建。
4.3 可维护性
Key应该是可读性好的、具有描述性的字符串或其他类型。这可以帮助我们快速定位问题,以及在需要时进行调试。
总结
Key在Flutter中是一个非常重要的概念,它可以帮助我们更好地管理和更新Widget。理解和掌握Key的使用,可以帮助更好地优化性能、避免重建问题,并确保Widget正确地进行更新。