1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:meta/meta.dart';
6
7import 'diagnostics.dart';
8
9/// A [Key] is an identifier for [Widget]s, [Element]s and [SemanticsNode]s.
10///
11/// A new widget will only be used to update an existing element if its key is
12/// the same as the key of the current widget associated with the element.
13///
14/// {@youtube 560 315 https://www.youtube.com/watch?v=kn0EOS-ZiIc}
15///
16/// Keys must be unique amongst the [Element]s with the same parent.
17///
18/// Subclasses of [Key] should either subclass [LocalKey] or [GlobalKey].
19///
20/// See also:
21///
22/// * [Widget.key], which discusses how widgets use keys.
23@immutable
24abstract class Key {
25 /// Construct a [ValueKey<String>] with the given [String].
26 ///
27 /// This is the simplest way to create keys.
28 const factory Key(String value) = ValueKey<String>;
29
30 /// Default constructor, used by subclasses.
31 ///
32 /// Useful so that subclasses can call us, because the [Key.new] factory
33 /// constructor shadows the implicit constructor.
34 @protected
35 const Key.empty();
36}
37
38/// A key that is not a [GlobalKey].
39///
40/// Keys must be unique amongst the [Element]s with the same parent. By
41/// contrast, [GlobalKey]s must be unique across the entire app.
42///
43/// See also:
44///
45/// * [Widget.key], which discusses how widgets use keys.
46abstract class LocalKey extends Key {
47 /// Abstract const constructor. This constructor enables subclasses to provide
48 /// const constructors so that they can be used in const expressions.
49 const LocalKey() : super.empty();
50}
51
52/// A key that is only equal to itself.
53///
54/// This cannot be created with a const constructor because that implies that
55/// all instantiated keys would be the same instance and therefore not be unique.
56class UniqueKey extends LocalKey {
57 /// Creates a key that is equal only to itself.
58 ///
59 /// The key cannot be created with a const constructor because that implies
60 /// that all instantiated keys would be the same instance and therefore not
61 /// be unique.
62 // ignore: prefer_const_constructors_in_immutables , never use const for this class
63 UniqueKey();
64
65 @override
66 String toString() => '[#${shortHash(this)}]';
67}
68
69/// A key that uses a value of a particular type to identify itself.
70///
71/// A [ValueKey<T>] is equal to another [ValueKey<T>] if, and only if, their
72/// values are [operator==].
73///
74/// This class can be subclassed to create value keys that will not be equal to
75/// other value keys that happen to use the same value. If the subclass is
76/// private, this results in a value key type that cannot collide with keys from
77/// other sources, which could be useful, for example, if the keys are being
78/// used as fallbacks in the same scope as keys supplied from another widget.
79///
80/// See also:
81///
82/// * [Widget.key], which discusses how widgets use keys.
83class ValueKey<T> extends LocalKey {
84 /// Creates a key that delegates its [operator==] to the given value.
85 const ValueKey(this.value);
86
87 /// The value to which this key delegates its [operator==]
88 final T value;
89
90 @override
91 bool operator ==(Object other) {
92 if (other.runtimeType != runtimeType) {
93 return false;
94 }
95 return other is ValueKey<T>
96 && other.value == value;
97 }
98
99 @override
100 int get hashCode => Object.hash(runtimeType, value);
101
102 @override
103 String toString() {
104 final String valueString = T == String ? "<'$value'>" : '<$value>';
105 // The crazy on the next line is a workaround for
106 // https://github.com/dart-lang/sdk/issues/33297
107 if (runtimeType == _TypeLiteral<ValueKey<T>>().type) {
108 return '[$valueString]';
109 }
110 return '[$T $valueString]';
111 }
112}
113
114class _TypeLiteral<T> {
115 Type get type => T;
116}
117