Flutter Key的原理和使用 (一) 没有Key会发生什么

1,834 阅读3分钟

这是我参与8月更文挑战的27天,活动详情查看:8月更文挑战

Flutter Key的原理和使用 (一) 没有Key会发生什么 (juejin.cn)

Flutter Key的原理和使用 (二) Widget 和 Element 的对应关系 (juejin.cn)

Flutter Key的原理和使用(三) LocalKey的三种类型 (juejin.cn)

Flutter Key的原理和使用(四) GlobalKey 的用法 - 掘金 (juejin.cn)

Flutter Key的原理和使用(五) 需要key的实例:可拖动改变顺序的Listview - 掘金 (juejin.cn)

在flutter中,几乎每个widget都有一个Key,但是我们使用的时候一般不会传Key , 那么这个Key,它到底是干什么用的呢? 几乎每个widget都有,但我们又很少用到它. 那到底什么时候才需要用呢?

接下来,我们看一下,在需要Key的时候不用key,会发生什么情况.

先举个常见的例子:

Column(
  children: [
    Container(width: 100, height: 100,color: Colors.red),
    Container(width: 100, height: 100,color: Colors.blue),
  ]
),

如果有上面一段代码,那么会显示两个方块,如图:

image.png

如果把两个Container调换位置,那么UI上,两个方块也会换位置.这个没什么说的,很容易理解. 那如果是两个稍微复杂的widget呢?

现在创建一个新的Wdiget:

class Box extends StatefulWidget {
  final Color color;

  const Box(this.color, {Key? key}) : super(key: key);

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

class _BoxState extends State<Box> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Container(
        width: 100,
        height: 100,
        color: widget.color,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$count', style: TextStyle(color: Colors.white)),
            IconButton(
              onPressed: () {
                setState(() {
                  count++;
                });
              },
              icon: Icon(Icons.add),
            )
          ],
        ));
  }
}

这个Widget内部包含一个count,由自己管理它的状态,并且有一个方法,可以改变这个值. 代码如下:

Column(
  children: [
    Box(Colors.red),
    Box(Colors.blue),
  ],
)

现在用这个Widget替代上面的Container, 然后分别改变它们的值:

然后我们把两个widget互换位置,看看之后的效果:

1212.gif

可以看到,它的颜色虽然换了,但是数字却没有互换,似乎不是我们期望的效果.

我们再尝试改变一下代码

Column(
  children: [
    Box(Colors.red),
    Box(Colors.blue),
    Box(Colors.red),
  ],
)

image.png

然后我们把第一个box,也就是红色的widget去掉,hotreload一下, 会是什么样呢?

12123.gif

很神奇,红色的确实消失了,但是按理说应该第一个去掉, 而蓝色的2和红色的3留下,然而却留下了前两个,并且数值还是第一个和第二个.

看起来,Flutter已经分不清谁是谁了,可是在我们看来,这里并没有复杂的逻辑.

那我们如果换成3个红色的box呢? 把代码改成这样:

Column(
  children: [
    Box(Colors.red),
    Box(Colors.red),
    Box(Colors.red),
  ],
)

然后我们再删掉第一个,代码就变成了:

Column(
  children: [
    Box(Colors.red),
    Box(Colors.red),
  ],
)

如果不是自己亲手删除的,根本不知道是哪个不见了.只知道是少了一个.

如果我们依然像之前一样,去调换顺序呢? UI没有改变, 可是调换之后代码并没有变化,这么看 似乎UI不变又不奇怪了. 代码都没有变化,你指望UI有什么变化呢?

好像找到了一点灵感, 是不是说明Flutter不能靠颜色来辨别是哪个widget,或者说颜色不能作为widget的唯一标识. 此时我们需要一个uuid来区分不同的widget, 而这 就是key的作用了.

我们如果给widget分别传入不同的key, 就相当于各自有了一个id, 如果这3个id不同,那么flutter也就不会混淆它们了.

Flutter有几种不同的key,我会在后面为大家介绍. 之前定义box组件的时候,已经定义了一个key的参数, 此时我们分别传入不同的key

Box(Colors.red,key: ValueKey(1)),
Box(Colors.red,key: ValueKey(2)),
Box(Colors.red,key: ValueKey(3)),

因为有了key,flutter也就能区分它们了, 现在我们无论是调换还是删除其中的widget, flutter的变化就都会朝着我们预期的方向发展了.

在实际开发中,我们也会遇到不同widget混淆的问题,一般情况下, 加一个ValueKey就可以解决了.

至于有什么情况加ValueKey不能解决,我们后面也会介绍到.