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.
如果一个新的widget
和当前widget
相关联的element
的key
相同,更新这个已存在的element
。
/// Keys must be unique amongst the [Element]s with the same parent.
在同一个parent
下的elements
的key
必须是唯一的。
/// Subclasses of [Key] should either subclass [LocalKey] or [GlobalKey].
Key
的子类应该是LocalKey
或者Globalkey
的子类。
Key
的源码如下:
@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();
}
Flutter
中总共有两种Key
:LocalKey
(局部)和GlobalKey
(全局)。
LocalKey
包含ValueKey
,UniqueKey
和ObjectKey
三种类型。
GlobalKey
A key that is unique across the entire app.
/// Global keys uniquely identify elements. Global keys provide access to other
/// objects that are associated with those elements, such as [BuildContext].
/// For [StatefulWidget]s, global keys also provide access to [State].
GlobalKey
全局唯一标识element
。GlobalKey
提供对与这些element
相关联的其他对象的访问,例如BuildContext
。 对于StatefulWidget
,GlobalKey
也提供对State
的访问。
/// Reparenting an [Element] using a global key is relatively expensive, as
/// this operation will trigger a call to [State.deactivate] on the associated
/// [State] and all of its descendants; then force all widgets that depends
/// on an [InheritedWidget] to rebuild.
使用GlobalKey
来重新布置Element
是非常昂贵的,因为这个操作会调用与其相关联的State
的State.deactivate
和它所有的子节点。然后强制所有依赖的widget
在InheritedWidget
上重建。
/// If you don't need any of the features listed above, consider using a [Key],
/// [ValueKey], [ObjectKey], or [UniqueKey] instead.
如果不使用GlobalKey
的相关特性,推荐使用Key
、ValueKey
、ObjectKey
或者UniqueKey
代替。
/// GlobalKeys should not be re-created on every build. They should usually be
/// long-lived objects owned by a [State] object.
/// Instead, a good practice is to let a State object own the GlobalKey, and
/// instantiate it outside the build method, such as in [State.initState].
不应该在每次构建时都重新创建GlobalKeys
。它们应该被一个State长期拥有。
GlobalKey
应该在build
方法外进行实例化,例如State.initState
。
@optionalTypeArgs
abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
/// Creates a [LabeledGlobalKey], which is a [GlobalKey] with a label used for
/// debugging.
///
/// The label is purely for debugging and not used for comparing the identity
/// of the key.
factory GlobalKey({ String? debugLabel }) => LabeledGlobalKey<T>(debugLabel);
/// Creates a global key without a label.
///
/// Used by subclasses because the factory constructor shadows the implicit
/// constructor.
const GlobalKey.constructor() : super.empty();
Element? get _currentElement => WidgetsBinding.instance!.buildOwner!._globalKeyRegistry[this];
/// The build context in which the widget with this key builds.
///
/// The current context is null if there is no widget in the tree that matches
/// this global key.
BuildContext? get currentContext => _currentElement;
/// The widget in the tree that currently has this global key.
///
/// The current widget is null if there is no widget in the tree that matches
/// this global key.
Widget? get currentWidget => _currentElement?.widget;
/// The [State] for the widget in the tree that currently has this global key.
///
/// The current state is null if (1) there is no widget in the tree that
/// matches this global key, (2) that widget is not a [StatefulWidget], or the
/// associated [State] object is not a subtype of `T`.
T? get currentState {
final Element? element = _currentElement;
if (element is StatefulElement) {
final StatefulElement statefulElement = element;
final State state = statefulElement.state;
if (state is T)
return state;
}
return null;
}
}
ValueKey
A key that uses a value of a particular type to identify itself.
使用一种特殊的类型去标识自身的Key
。
/// This class can be subclassed to create value keys that will not be equal to
/// other value keys that happen to use the same value. If the subclass is
/// private, this results in a value key type that cannot collide with keys from
/// other sources, which could be useful, for example, if the keys are being
/// used as fallbacks in the same scope as keys supplied from another widget.
源码如下:
class ValueKey<T> extends LocalKey {
/// Creates a key that delegates its [operator==] to the given value.
const ValueKey(this.value);
/// The value to which this key delegates its [operator==]
final T value;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ValueKey<T>
&& other.value == value;
}
@override
int get hashCode => hashValues(runtimeType, value);
@override
String toString() {
final String valueString = T == String ? "<'$value'>" : '<$value>';
// The crazy on the next line is a workaround for
// https://github.com/dart-lang/sdk/issues/33297
if (runtimeType == _TypeLiteral<ValueKey<T>>().type)
return '[$valueString]';
return '[$T $valueString]';
}
}
UniqueKey
// A key that is only equal to itself.
class UniqueKey extends LocalKey {
UniqueKey();
@override
String toString() => '[#${shortHash(this)}]';
}
ObjectKey
A key that takes its identity from the object used as its value.
/// Used to tie the identity of a widget to the identity of an object used to
/// generate that widget.
GlobalObjectKey
A global key that takes its identity from the object used as its value.
/// If the object is not private, then it is possible that collisions will occur
/// where independent widgets will reuse the same object as their
/// [GlobalObjectKey] value in a different part of the tree, leading to a global
/// key conflict. To avoid this problem, create a private [GlobalObjectKey]
/// subclass, as in:
///
/// ```dart
/// class _MyKey extends GlobalObjectKey {
/// const _MyKey(Object value) : super(value);
/// }
/// ```
/// Since the [runtimeType] of the key is part of its identity, this will
/// prevent clashes with other [GlobalObjectKey]s even if they have the same
/// value.
/// Any [GlobalObjectKey] created for the same value will match.
如何使用Key?
/// If the [runtimeType] and [key] properties of the two widgets are
/// [operator==], respectively, then the new widget replaces the old widget by
/// updating the underlying element (i.e., by calling [Element.update] with the
/// new widget). Otherwise, the old element is removed from the tree, the new
/// widget is inflated into an element, and the new element is inserted into the
/// tree.
如果两个widget
的runtimeType
和key
相同,然后新的widget
将会通过调用其Element.update
来替换旧的widget
。同时,从树中删除旧的element
,新的element
将会被插入到树中。
/// In addition, using a [GlobalKey] as the widget's [key] allows the element
/// to be moved around the tree (changing parent) without losing state. When a
/// new widget is found (its key and type do not match a previous widget in
/// the same location), but there was a widget with that same global key
/// elsewhere in the tree in the previous frame, then that widget's element is
/// moved to the new location.
此外,使用GlobalKey
作为小部件的Key
允许Element
在树周围移动(更改父级)而不丢失状态。当发现一个新的widget
(它的key
与type
与同一个位置的前一个widget
不匹配),但在前一帧树中的其它地方有一个具有相同GlobalKey
的widget
,那么该widget
的element
将被移动到新位置。
/// Generally, a widget that is the only child of another widget does not need
/// an explicit key.
通常作为另一个widget
的唯一子widget
的widget
不需要显式Key
。