Analysis of internal principle of flutter – key

Time:2021-3-9

The original text is inhere. This article is a summary of the tubing video. The video address ishere

Basically, every widget has a key parameter, but the methods used are different. When a widget moves from one place in the widget tree to another, the key saves the state. In practice, the key can be used to save the scrolling position of the user or the modified state of the collection.

The internal principle of key

You don’t use the key most of the time. There are no side effects, but there is no need to consume extra space. It’s like thisMap<Foo, Bar> aMap = Map<Foo, Bar>();Initialize a variable and throw it. But,If you want to add, delete or sort a stateful widget collection of the same type, you need to participate in the key

To illustrate why you need to use key when modifying a widget collection, I (the author) wrote a simple example. In this example, there are two widgets that display colors randomly. When you click one of the buttons inside, the two components will swap positions. :

Analysis of internal principle of flutter - key

In the stateless version, there are two stateless components that display random colors. These two stateless widgets are contained in aPositionedTilesIn a stateful wiget. The location of the two color display widgets is also saved in it. WhenFloatingActionButtonWhen it is clicked, the two stateless color components will swap positions. The code is as follows:

void main() => runApp(new MaterialApp(home: PositionedTiles()));

class PositionedTiles extends StatefulWidget {
 @override
 State<StatefulWidget> createState() => PositionedTilesState();
}

class PositionedTilesState extends State<PositionedTiles> {
 List<Widget> tiles = [
   StatelessColorfulTile(),
   StatelessColorfulTile(),
 ];

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Row(children: tiles),
     floatingActionButton: FloatingActionButton(
         child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
   );
 }

 swapTiles() {
   setState(() {
     tiles.insert(1, tiles.removeAt(0));
   });
 }
}

class StatelessColorfulTile extends StatelessWidget {
 Color myColor = UniqueColorGenerator.getColor();
 @override
 Widget build(BuildContext context) {
   return Container(
       color: myColor, child: Padding(padding: EdgeInsets.all(70.0)));
 }
}

But if I let youColorfulTilesWhen I click the button, it looks like nothing will happen.

Analysis of internal principle of flutter - key

List<Widget> tiles = [
   StatefulColorfulTile(),
   StatefulColorfulTile(),
];

...
class StatefulColorfulTile extends StatefulWidget {
 @override
 ColorfulTileState createState() => ColorfulTileState();
}

class ColorfulTileState extends State<ColorfulTile> {
 Color myColor;

 @override
 void initState() {
   super.initState();
   myColor = UniqueColorGenerator.getColor();
 }

 @override
 Widget build(BuildContext context) {
   return Container(
       color: myColor,
       child: Padding(
         padding: EdgeInsets.all(70.0),
       ));
 }
}

However, there is a bug in this code. When you click the “exchange” button, the two color widgets will not exchange. This effect can only be achieved by adding the key parameter to the color widget.

Analysis of internal principle of flutter - key

List<Widget> tiles = [
  StatefulColorfulTile(key: UniqueKey()), // Keys added here
  StatefulColorfulTile(key: UniqueKey()),
];

...
class StatefulColorfulTile extends StatefulWidget {
  StatefulColorfulTile({Key key}) : super(key: key);  // NEW CONSTRUCTOR
 
  @override
  ColorfulTileState createState() => ColorfulTileState();
}

class ColorfulTileState extends State<ColorfulTile> {
  Color myColor;

  @override
  void initState() {
    super.initState();
    myColor = UniqueColorGenerator.getColor();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        color: myColor,
        child: Padding(
          padding: EdgeInsets.all(70.0),
        ));
  }
}

However, it is only necessary to modify the stateful subtree. If the widget collection of the whole subtree is stateless, then the key is not necessary.

That’s all you need to know to use key in flutter. Of course, if you want to know the principle of this, please continue to look…

Why is key necessary sometimes

As you know, each widget has a corresponding element. Just like building a widget tree, flutter builds a corresponding oneElementTrees. thisElementTreeVery simple, only the type and sub elements of each widget are saved. You can think of the element tree as the skeleton and blueprint of the flutter app. Any other information can be found from element and then retrieved.

In the example aboveRowAn ordered list of child nodes is stored in the widget. When we exchangeRowIn the order of the color widget, flutter will traverseElementTreeCompare whether the structure of the tree has changed before and after the exchange.
Analysis of internal principle of flutter - key

Flutter fromRowElementStart, and then move the children.ElementTreeCheck whether the new widget type and key are different from the old node. If it’s different, it points the reference to the new widget. In the stateless version, the widget doesn’t have a key, so flutter just checks the type. If it seems that there is too much information in this way, you can directly look at the moving picture above.

In the element tree, the handling of stateful widgets is slightly different. There will still be widgets and elements mentioned above, but there will also be objects in saved state. Colors are stored in these states for a long time, not in widgets.

Analysis of internal principle of flutter - key

In the case of stateful but no key, when the swap widget button is pressed, the flutter will traverseElementTree, checkRowType, and then update the reference. Then there is the color element, which checks whether the color widget is of the same type and updates the reference. Because flutter usesElementTreeAnd its state to determine what can be displayed on your device. From the user’s point of view, the two color widgets are not exchanged correctly.

Analysis of internal principle of flutter - key

In the modified version of the above problem, a key parameter is added to the color widget. Now when you click the color exchange button again,RowThe widget is the same as before, but the key of the two color elements is different from the key of the widget, which will cause the flutter to run in theRowElement starts to reconstruct the element subtree where the first key value does not match.

After that, flutter will be thereRowFind the element matching the key value in the child node to reconstruct the subtree. If a key value matches, update its reference to the widget. Until the entire element subtree is reconstructed. In this way, the flutter can display the color exchange correctly.

In other words, if you want to modify the number and order of a list of state widgetsKeyIt’s essential. For emphasis, in this case, the value of the color exists in the state. State is very small and inconspicuous when it exists. It can be used in animation, display of user input data and scrolling position.

Where is the key

Basically, if you want to use the key in the app, it should be placed at the top of the widget subtree where the state is stored
A common mistake is that many people put the key in the first state widget, but this is not right. Don’t believe it? Let’s modify the above example a little bit. Now?PaddingThe widget is wrapped outside the color widget, but the key is still on top of the color widget.

void main() => runApp(new MaterialApp(home: PositionedTiles()));

class PositionedTiles extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PositionedTilesState();
}

class PositionedTilesState extends State<PositionedTiles> {
  // Stateful tiles now wrapped in padding (a stateless widget) to increase height 
  // of widget tree and show why keys are needed at the Padding level.
  List<Widget> tiles = [
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulColorfulTile(key: UniqueKey()),
    ),
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulColorfulTile(key: UniqueKey()),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(children: tiles),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
    );
  }

  swapTiles() {
    setState(() {
      tiles.insert(1, tiles.removeAt(0));
    });
  }
}

class StatefulColorfulTile extends StatefulWidget {
  StatefulColorfulTile({Key key}) : super(key: key);
 
  @override
  ColorfulTileState createState() => ColorfulTileState();
}

class ColorfulTileState extends State<ColorfulTile> {
  Color myColor;

  @override
  void initState() {
    super.initState();
    myColor = UniqueColorGenerator.getColor();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        color: myColor,
        child: Padding(
          padding: EdgeInsets.all(70.0),
        ));
  }
}

After clicking the swap button, the two color components display completely different colors.

Analysis of internal principle of flutter - key

This is what the corresponding element tree looks like

Analysis of internal principle of flutter - key
After we exchange the positions of the two child widgets, the element to widget checking mechanism in flutter only checks one layer of the element tree at a time. The leaf nodes are ashed in the figure below, so that we can pay more attention to what happened. At the padding widget level, everything works correctly.

Analysis of internal principle of flutter - key

In the second layer, flutter will find that the key of the color widget does not match the key of the element, and it will remove the reference of these elements. In this case, theLocalKeys. In other words, in the comparison of elements, flutter will only check whether the values of key match in a certain range of the tree.

Because there is no element matching the key value in this range, it will create a new one, so it will initialize a new state. So in this case, the color displayed by the widget is a randomly generated color.

Analysis of internal principle of flutter - key
So, if you arePaddingHow about adding the key value to the widget?

void main() => runApp(new MaterialApp(home: PositionedTiles()));

class PositionedTiles extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => PositionedTilesState();
}

class PositionedTilesState extends State<PositionedTiles> {
  List<Widget> tiles = [
    Padding(
      // Place the keys at the *top* of the tree of the items in the collection.
      key: UniqueKey(), 
      padding: const EdgeInsets.all(8.0),
      child: StatefulColorfulTile(),
    ),
    Padding(
      key: UniqueKey(),
      padding: const EdgeInsets.all(8.0),
      child: StatefulColorfulTile(),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(children: tiles),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
    );
  }

  swapTiles() {
    setState(() {
      tiles.insert(1, tiles.removeAt(0));
    });
  }
}

class StatefulColorfulTile extends StatefulWidget {
  StatefulColorfulTile({Key key}) : super(key: key);
 
  @override
  ColorfulTileState createState() => ColorfulTileState();
}

class ColorfulTileState extends State<ColorfulTile> {
  Color myColor;

  @override
  void initState() {
    super.initState();
    myColor = UniqueColorGenerator.getColor();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        color: myColor,
        child: Padding(
          padding: EdgeInsets.all(70.0),
        ));
  }
}

Flutter has noticed the problem and will update it correctly.

Analysis of internal principle of flutter - key

What kind of key should I use

The key type we want to use mainly depends on what features the widget should distinguish. Here are four keys:ValueKey, ObjectKey, UniqueKeyandPageStorageKey, GlobalKey.

ValueKey

For example, in the todo app below, you can reorder the items.

Analysis of internal principle of flutter - key

In this scenario, if the text of an item can be regarded as a constant and unique, it is used forValueKey. The text is the value.

return TodoItem(
  key: ValueKey(todo.task),
  todo: todo,
  onDismissed: (direction) => _removeTodo(context, todo),
);

Objectkey

Another scenario, for example, you have an address book app. It contains information about different people. In this case, each widget holds a complex piece of data. Each individual field, such as name or date of birth, may be the same as other data, but the combined data is unique. So, it’s very practicalObjectKey

Analysis of internal principle of flutter - key

Uniquekey

If multiple widgets have the same value, or you want to make sure that each widget is different, you can use itUniqueKey. This is used in the above exampleUniqueKeyBecause there is no other value in the color widget that can be distinguished from other widgets. useUniqueKeyBe careful. If you arebuildMethodUniqueKey, then this widget is called every timebuildMethod will get a different resultUniqueKeyIn this way, all the benefits of key are wiped out.

Similarly, never consider using random numbers as your keys. Each time a widget is calledbuildMethod will generate a random number, and the continuity of multiple frames will be destroyed. Then, the effect is the same as that of the key which was useless in the beginning.

PageStoragekey

This is a very special key, which saves the user’s scroll position, so that the app can save the user’s scroll position for the next time the user opens it, and go directly to the last scroll position.

Analysis of internal principle of flutter - key

GlobalKey

There are two uses:

  • You can change the parent widget anywhere in the app without losing state
  • It can be used to access data from a completely different widget tree

An example of the first case is if you want to display the same widget in different places, and the state is the same, thenGlobalKeyIt’s the best choice.

Second, if you want to verify a password, but don’t want to share state between different widgets.

GlobalKeyIt can also be used for testing, using a key to access a specific widget and then view the data in it.

Analysis of internal principle of flutter - key

Usually (not all),GlobalKeyIt’s more like a global variable. There are always other ways to access state, such asInheritedWidget, or a library similar to Redux or the implementation of bloc mode.

summary

In a word, to save the state in the widget tree, you need to consider using the key. Generally, when you want to modify a list of widgets of the same type, such as a list. Put the key value on the top-level widget of the subtree where you want to save the state. Select the appropriate key type according to the data to be displayed and the scene to be used.

The code of todo app is inhere