In the previous section, we did not use a key in the constructor, and any Widget that inherits from the Widget has the key property by default, which is optional. Let’s study the function of a key through a case study.

const MyApp({Key? key}) : super(key: key);
Copy the code

StatefulWidgetIn thekey

Set up a page and randomly create squares of different colors in the middle of the page

class _MyHomePageState extends State<MyHomePage> {
  List<SquareItem1> list = [
    const SquareItem1('Up and up', key: ValueKey(111),),
    const SquareItem1('Middle middle', key: ValueKey(222),),
    const SquareItem1('Down and down', key: ValueKey(333), a)];@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: list,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            list.removeAt(0);
          });
        },
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.); }}Copy the code

Const SquareItem1(this.title, {Key? key}) : super(key: key);

import 'dart:math';
import 'package:flutter/material.dart';
class SquareItem1 extends StatefulWidget {
  final String title;
  const SquareItem1(this.title, {Key? key}) : super(key: key);

  @override
  _SquareItem1State createState() => _SquareItem1State();
}

class _SquareItem1State extends State<SquareItem1> {
  final color = Color.fromRGBO(
      Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100, color: color, child: Text(widget.title), ); }}Copy the code

When the button is clicked, delete the first element in the array once and look carefully. It can be obviously found through the experimental comparison that the order at this time seems to be a bit wrong. Then what is the problem?

  • Look at the text first: the text seems to be in good order, delete the top one each time
  • Let’s look at the color: the color seems to be deleted from the back, each time delete the last one

With that in mind, let’s take a look at the key in the StatelessWidget

StatelessWidgetThe key of the

This time we inherit the StatelessWidget, where the SquareItem1 constructor looks like this: SquareItem(this.title, {Key? key}) : super(key: key);

import 'dart:math';
import 'package:flutter/material.dart';

class SquareItem extends StatelessWidget {
  final String title;
  SquareItem(this.title, {Key? key}) : super(key: key);

  final color = Color.fromRGBO(
      Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100, color: color, child: Text(title), ); }}Copy the code

After observation, it is found that there is no problem with the order of remove this time, and the color and text can correspond to each other. The only difference between the two codes is the location of the color initialization. One is initialized in State and the other in Widget.

keyThe use of

In StatefulWidget we can differentiate between widgets by assigning a key, for example

  List<SquareItem1> list = [
    const SquareItem1(
      'Up and up',
      key: ValueKey(111),),const SquareItem1(
      'Middle middle',
      key: ValueKey(222),),const SquareItem1(
      'Down and down',
      key: ValueKey(333),
    )
  ];
Copy the code

Now run again to find the color + text delete order is correct. So it is reasonable and bold to guess that data was corrupted in Stateful because of a mapping problem. So what was it? Let’s take a look at the API. StatelessWidget -> Widget there is a method canUpdate

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
Copy the code

Flutter is an incremental render and what needs to be updated is determined by the above method. So when both widgets are statefulwidgets, this method returns True if the structure is the same if the key is not specified

  • inWidgetSave Text in
  • inElementThe Color is saved in
  • When deleting, although we deleted the text, we did not specify the key, socanUpdate = trueSo this is the first oneElementThe color is pointing toWidgetSecond, this is the problem in example 1.

Of course, if you add a new widget that looks exactly like the one above, it will not be deleted if there is a new point under Element. Add color= the color under Element

KeyThe principle of

Key itself is an abstract class with a factory constructor that creates ValueKey. The main direct subclasses are LocalKey and GlobalKey

  1. GlobalKey: Helps access information about a Widget
  2. LocalKey: Used to distinguish whichElementNeed to keep
    • const ValueKey(this.value);// Take values as arguments, numbers, or strings
    • const ObjectKey(this.value);// Take an object as an argument
    • UniqueKey();// Create a unique identifier

GlobalKeyThe use of

import 'package:flutter/material.dart';

class GlobalDemo extends StatelessWidget {
  const GlobalDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Page'),
      ),
      body: const BodyCenter(),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // the outer layer needs to call the inner layer count++
        },
        child: constIcon(Icons.add), ), ); }}class BodyCenter extends StatefulWidget {
  const BodyCenter({Key? key}) : super(key: key);

  @override
  _BodyCenterState createState() => _BodyCenterState();
}

class _BodyCenterState extends State<BodyCenter> {
  int count = 0;
  String title = 'hello';

  @override
  Widget build(BuildContext context) {
    returnCenter( child: Column( children: [Text(count.toString()), Text(title)], mainAxisAlignment: MainAxisAlignment.center, ), ); }}Copy the code

In the following design, the outer layer is a StatelessWidget and the body in the middle is a StatefulWidget. When we onPress the outer layer, we normally cannot update the memory count. In this case, we can use GlobalKey to solve the problem

  1. Initialize one in the outer layerGlobalKeyAnd specify which one you want to followStateThe bindingfinal GlobalKey<_BodyCenterState> _globalKey = GlobalKey();
  2. Synchronize binding when the memory widget is initializedbody: BodyCenter(key: _globalKey),
  3. Usage:
onPressed: () { _globalKey.currentState! .setState(() { _globalKey.currentState! .title ='Last time count='+ _globalKey.currentState! .count.toString(); _globalKey.currentState! .count++; }); },Copy the code

This works as long as you’re in the tree of the current child, and you can get the child’s data.