This is the 29th day that I have participated in the August More Text Challenge

The principle and use of Flutter Key

The principle and use of Flutter Key

As we explained in the previous chapter, a Flutter can sometimes be confused about the corresponding relationships between widgets of the same type on the same level if they are not given a Key, especially if the order of widgets changes. At this point, we need to pass it a key.

The types of Key

Key has two subclasses:

  • LocalKey the LocalKey must be unique at the same levelHomogeneous uniqueness
  • GlobalKey GlobalKey, which must be unique throughout the App.

In terms of performance, if you don’t need GlobalKey, you don’t need LocalKey because you only compare the same level, so it’s much faster. As mentioned in the previous chapter, this is not considered at the parent or child level. If the uniqueness principle above is violated, the runtime will report an error telling you that the Key does not appear only once.

LocalKey

And LocalKey has three subclasses

  • ValueKey
  • ObjectKey
  • UniqueKey

There is no need to use keys when all your widgets are statelessWidgets. Keys are only possible when using Statefuldiget.

ValueKey

const ValueKey(this.value)

final T value;

@override
bool operator= = (Object other) {
  if(other.runtimeType ! = runtimeType)return false;
  return other is ValueKey<T>
      && other.value == value;
}
Copy the code

It has a very simple constructor, it has a value of type T, which means you can pass whatever you want. Of course, because of the uniqueness principle, the value of a Valukey in a sibling cannot be the same. You can see this in its operator method.

ObjectKey

const ObjectKey(this.value);

final Object? value;

@override
bool operator= = (Object other) {
  if(other.runtimeType ! = runtimeType)return false;
  return other is ObjectKey
      && identical(other.value, value);
}
Copy the code

ObjectKey is very much the same as ValueKey, but let’s just look at the differences, the type of value has changed from T to Object, and the operator method is different. Identical comparisons, if they are equal or identical, are actually comparisons of references or Pointers, similar to Java comparisons of memory addresses.

Let’s create a slightly more complex class to see the difference in practice:

class People{
  final String name;
  final int age;

  People(this.name, this.age);

  @override
  bool operator= = (Object other) =>
      identical(this, other) ||
      other is People && runtimeType == other.runtimeType && name == other.name && age == other.age;

  @override
  int get hashCode => name.hashCode ^ age.hashCode;
}
Copy the code

Look primarily at the operator method, which determines equality when ages are equal and names are equal. At this point, if the code looks like this:

Box(Colors.red, key: ValueKey(People('a'.18))),
Box(Colors.red, key: ValueKey(People('a'.18))),
Copy the code

After running,

Ah, sure enough, the report was wrong. Let’s try ObjectKey instead:

Because People(‘a’, 18) is a new object (the new keyword can be omitted in DART), Flutter determines that they are not the same object because the corresponding ObjectKey is not equal. That’s the main difference between ObjectKey and ValueKey.

UniqueKey

class UniqueKey extends LocalKey {
  UniqueKey();

  @override
  String toString() => '[#${shortHash(this)}]';
}
Copy the code

As the name implies, a UniqueKey is a UniqueKey, which means it is only equal to itself. So UniqueKey() and UniqueKey() are not equal.

As you can see, after UniqueKey, hot reload, the state is lost becauseKeyThe new UniqueKey is not equal to the old one. So there’s no way for the state to survive.

Which begs the question, what is it really for?

In fact, lost state is one of its uses, which is usually used in animation effects. The other thing is that it doesn’t need a value, so when you don’t want to pass a value, like I often have a headache with naming, when YOU need a key, what value should be passed, I have to think about it for a while, maybe I can’t think about it well, just don’t pass it, so you can use UniqueKey.

So there’s another problem, it’s going to lose state every time, which is not what we want, what do we do?

Define UniqueKey outside the build, for example

final keyRed = UniqueKey();
final keyBlue = UniqueKey();
Copy the code

Since the two keys are new from the beginning, the keyRed is not changed when used, which has achieved our purpose.

GlobalKey

Ok, let’s move on to GlobalKey, which we’ll cover in the next chapter because of the title.