无状态小部件类中的 Keys 是什么?

在 flutter docs 中有一个无状态小部件子类的示例代码,如下所示:

class GreenFrog extends StatelessWidget {
const GreenFrog({ Key key }) : super(key: key);


@override
Widget build(BuildContext context) {
return new Container(color: const Color(0xFF2DBD3A));
}
}

还有这个

class Frog extends StatelessWidget {
const Frog({
Key key,
this.color: const Color(0xFF2DBD3A),
this.child,
}) : super(key: key);


final Color color;


final Widget child;


@override
Widget build(BuildContext context) {
return new Container(color: color, child: child);
}
}

What is a key and when should this super constructor be used? It seems like if you have your own constructor you must have {Key key} why? I've seen other examples where the super keyword is 没有 used so this is where my confusion is.

34017 次浏览

TLDR: All widgets should have a Key key as 可以选择 parameter or their constructor. Key是颤振引擎在识别列表中哪个小部件被更改的步骤中使用的。


当你有一个 名单(ColumnRow,无论什么)的小部件 属于同一类型,可能会被删除/插入时,它是有用的。

假设您有以下代码(代码不能工作,但是您可以理解) :

AnimatedList(
children: [
Card(child: Text("foo")),
Card(child: Text("bar")),
Card(child: Text("42")),
]
)

可能的情况是,您可以单独删除这些小部件中的任何一个。

问题是,我们的列表有一个动画时,一个孩子被删除。因此,让我们删除“吧”。

AnimatedList(
children: [
Card(child: Text("foo")),
Card(child: Text("42")),
]
)

问题: 如果没有 Key,flutter 将无法知道你的 Row的第二个元素是否消失了。或者是最后一个消失了,第二个孩子变了。

因此,如果没有 Key,您可能会有一个 bug,您的 离开动画将在最后一个元素上播放!


这就是 Key发生的地方。

如果我们重新开始我们的例子,使用 key 我们会得到:

AnimatedList(
children: [
Card(key: ObjectKey("foo"), child: Text("foo")),
Card(key: ObjectKey("bar"), child: Text("bar")),
Card(key: ObjectKey("42"), child: Text("42")),
]
)

注意,键是 没有,子索引,但是对于元素来说是唯一的。

From this point, if we remove "bar" again, we'll have

AnimatedList(
children: [
Card(key: ObjectKey("foo"), child: Text("foo")),
Card(key: ObjectKey("42"), child: Text("42")),
]
)

由于 key的存在,颤振引擎现在可以确定哪个小部件被删除了。现在,我们的 leave动画将正确地发挥“酒吧”而不是“42”。

Key 是一个可选参数,用于保持小部件树中的状态,如果想要移动树中的元素集合并保持它们的状态,就必须使用它们。

The best explanation can be found in this video by Google 何时使用按键 -Flutter Widgets 101第4季第4集

钥匙是什么?

密钥是小部件的 ID。所有的小部件都有它们,不仅仅是 StatelessWidgets。它们被 Element 树用来确定一个小部件是否可以重用或者是否需要重新构建。如果没有指定键(通常情况下) ,则使用小部件类型来确定这一点。

为什么要用钥匙?

当小部件的数量或位置发生变化时,键对于维护状态非常有用。如果没有键,那么 Flutter 框架可能会搞不清楚哪个小部件发生了更改。

何时使用钥匙?

只有在框架需要您的帮助才能知道要更新哪个小部件时才使用它们。

大多数时候你不需要用钥匙。由于键通常只用于维护状态,因此如果您有一个子元素都是无状态的无状态小部件,那么就不需要在其上使用键。在这种情况下,使用一把钥匙不会有什么坏处,但也不会有什么帮助。

您可以使用键进行一些微型优化,请参阅 这篇文章

在哪里使用钥匙?

Put the key at the part of the widget tree where the reordering or addition/deletion is taking place. For example, if you are reordering the items of a ListView whose children are ListTile widgets, then add the keys to the ListTile widgets.

使用什么样的钥匙?

密钥只是一个 ID,但是您使用的 ID 的类型可以有所不同。

ValueKey

ValueKey 是一个本地键,它接受一个简单的值,如字符串或整数。

ObjectKey

If you widget is displaying more complex data than a single value, then you can use an ObjectKey for that widget.

UniqueKey

这种类型的密钥保证每次都会给您一个唯一的 ID。如果您使用它,但是,不要把它在 build方法。否则,您的小部件将永远不会有相同的 ID,因此 Element 树将永远不会找到可重用的匹配项。

GlobalKey

GlobalKeys 可以用来维护整个应用程序的状态,但是要尽量少用,因为它们类似于全局变量。通常最好使用状态管理解决方案。

使用键的示例

参考文献

键是用于唯一标识小部件的对象。

它们用于访问或恢复 StatefulWidget中的状态(如果我们的小部件树都是无状态小部件,那么大多数情况下我们根本不需要它们)。 有各种类型的关键,我将尝试解释的基础上的用法。

用途(key types)

1. 在有状态小部件中变异集合 i.e. remove / add / reorder item to list,比如可拖动的待办事项列表,从而删除选中的项目

Something ObjectKey, ValueKey & UniqueKey

2. 将小部件从一个“父”移动到另一个“父”,以保持其状态。

Something GlobalKey

3. 在多个屏幕上显示相同的部件并保持其状态。

Something GlobalKey

4. 验证表格。

Something GlobalKey

5. 您希望在不使用任何数据的情况下给出一个密钥。

Something UniqueKey

如果你可以使用一些特定的数据字段,比如用户的 UUID 作为唯一的密钥。

Something ValueKey

7. If you do not have any unique field to use as key but object itself is unique.

Something ObjectKey

8. 如果有多个窗体或同一类型的多个小部件需要 GlobalKey。

Something GlobalObjectKey, LabeledGlobalKey whichever is appropriate, similar logic to ValueKey and ObjectKey

不要使用随机 string/number作为密钥,这违背了密钥的作用

对于 Dart 2.12或更高版本,如果需要,可以在键后添加 ?,使其成为可选的。

class Frog extends StatelessWidget {
const Frog({
Key? key,
this.color: const Color(0xFF2DBD3A),
this.child,
}) : super(key: key);


final Color color;


final Widget child;


@override
Widget build(BuildContext context) {
return new Container(color: color, child: child);
}
}