我们来到 Widget 的实现中, canUpdate 方法决定了一个 Widget 的 Element 是否会被更新,而 Element 的更新,又直接关系到了增量渲染。
同时满足两个条件:
新旧 Widget 的类型相同
新旧 Widget 的 Key 相同
如果 Key 是空的,只通过类型判断,就算他们的子 Widget 完全不同
这里就很好的解释了一开始发生的异常现象。
/// Whether the `newWidget` can be used to update an [Element] that currently
/// has the `oldWidget` as its configuration.
///
/// An element that uses a given widget as its configuration can be updated to
/// use another widget as its configuration if, and only if, the two widgets
/// have [runtimeType] and [key] properties that are [operator==].
///
/// If the widgets have no key (their key is null), then they are considered a
/// match if they have the same type, even if their children are completely
/// different.
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
图解
Widget 和 Element 是一一对应的,而 State 是在 Element 中的。
移除一个 Widget 开始检查
第一个 Element 检查到第二个 Widget
调用 canUpdate
类型相同
Key 为空
return ture
于是就用 第一个Element 更新 第二个Widget
依次类推,就发生了前面的异常现象
既然这里提到了 Key ,那么加上 Key 是否就可以上面这个问题呢?
Key 的使用
这里为每个 ColorItem 加上 Key , 并将 Color 属性放回 State 中:
...
class _HomePageState extends State<HomePage> {
//key的作用就非常大了!!
List<Widget> items = [
ColorItem(
'第1个',
key: ValueKey(1),
),
ColorItem(
'第2个',
key: ValueKey(2),
),
ColorItem(
'第3个',
key: ValueKey(3),
),
];
@override
Widget build(BuildContext context) {
...
}
}
class ColorItem extends StatefulWidget {
final String title;
...
}
class _ColorItemState extends State<ColorItem> {
final color = Color.fromRGBO(
Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
...
}
有效!
Key
Key 本身是一个抽象类,有一个工厂方法:
/// A [Key] is an identifier for [Widget]s, [Element]s and [SemanticsNode]s.
///
/// A new widget will only be used to update an existing element if its key is
/// the same as the key of the current widget associated with the element.
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=kn0EOS-ZiIc}
///
/// Keys must be unique amongst the [Element]s with the same parent.
///
/// Subclasses of [Key] should either subclass [LocalKey] or [GlobalKey].
///
/// See also:
///
/// * [Widget.key], which discusses how widgets use keys.
@immutable
abstract class Key {
/// Construct a [ValueKey<String>] with the given [String].
///
/// This is the simplest way to create keys.
const factory Key(String value) = ValueKey<String>;
/// Default constructor, used by subclasses.
///
/// Useful so that subclasses can call us, because the [new Key] factory
/// constructor shadows the implicit constructor.
@protected
const Key.empty();
}
它有两个子类
LocalKey
同一个 父Element 内唯一的
GlobalKey
整个 App 内唯一
刚才用的 ValueKey 是 LocalKey 的子类。
LocalKey
区别哪个 Element 要保留,哪个 Element 要删除。
/// A key that is not a [GlobalKey].
///
/// Keys must be unique amongst the [Element]s with the same parent. By
/// contrast, [GlobalKey]s must be unique across the entire app.
///
/// See also:
///
/// * [Widget.key], which discusses how widgets use keys.
abstract class LocalKey extends Key {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const LocalKey() : super.empty();
}