Flutter - Key的原理

作者: Lcr111 | 来源:发表于2021-12-28 16:47 被阅读0次

前言

上篇文章我们简单探索了Flutter的渲染原理---Flutter初探渲染原理初探,这篇文章我们继续来看看上篇文章中提到的ElementcanUpdate方法里面oldWidget.key == newWidget.key的,帮我我们更深入的理解Flutter底层的渲染原理。

Key的种类

  • Key 本身是一个抽象类,又一个工厂构造方法,创建ValueKey
  • 直接子类主要有: LocalKeyGlobalKey
    • GlobalKey:帮助我们访问某个Widget的信息
    • LocalKey:它用来区别哪个Element要保留,哪个Element 要删除
      1. ValueKey::以值作为参数(数字、字符串)
      2. ObjectKey:以对象作为参数
      3. UniqueKey:创建唯一标识(保证不会被复用)

LocalKey

下面我们通过一个例子来看看Key 的具体用法:

class StateKeyDemo extends StatefulWidget {
  const StateKeyDemo({Key? key}) : super(key: key);

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

class _StateKeyDemoState extends State<StateKeyDemo> {
  List<Widget> items = [
    statefulItem(
      title: '111',
    ),
    statefulItem(
      title: '222',
    ),
    statefulItem(
      title: '333',
    )
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('demoStl'), backgroundColor: Colors.blue),
      body: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: items,
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          setState(() {
            items.removeAt(0);
          });
        },
      ),
    );
  }
}

class statefulItem extends StatefulWidget {
  final String? title;
  const statefulItem({this.title, Key? key}) : super(key: key);

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

class _statefulItemState extends State<statefulItem> {
  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!),
    );
  }
}
暂未使用Key1

当我们点击下方按钮时候,发现第三块不见了,再次点击,发现第二块不见了,但是我们代码里写的是items.removeAt(0),不是应该最前面的一块不见了嘛!

暂未使用Key2
此时,只剩下一块,颜色和第一块一样,但是文字却是333
如果我们将这几个方块换成StatelessWidget:
class StatlessItem extends StatelessWidget {
  final String? title;
  StatlessItem({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!),
    );
  }
}

我们发现运行结果就是我们想要的结果,方块从前往后一次被删除。
这是为啥呢???
分析
StatlessItem 里面的 color 是widget的一个变量,所以当我们点击按钮,删除方块时,删除的是一个个widget,但是statefulItem 中的color属性是属于state的,state又是保存在Elemnet中的,当删除第一个时,删除的是widget,根据增量渲染的原理,相应的Element树会调用canUpdate方法来判断是否需要更新,依次从左到右,判断每一个Element是否留下还是删除:

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

所以,当前Element树中第一个Element会和Widget树中的第二个widget进行对比,类型一样,key一样(都没有key就是都一样null),发现当前Element可以保留下来进行复用,相应的state保存下来复用,所以color属性就被继续使用了,但是title还是正常展示的。

Widget和Elemwnt演变过程

接下来我们将方块换回为StatefullWidget,并且给每个方块添加属性key

List<Widget> items = [
    statefulItem(
      title: '111',
      key: const ValueKey(111),
    ),
    statefulItem(
      title: '222',
      key: const ValueKey(222),
    ),
    statefulItem(
      title: '333',
      key: const ValueKey(333),
    )
  ];

点击按钮操作,发现这次的结果是我们想要的结果,方块依次从左到右删除了。


结论
添加key属性,就是用来标记widget的,用来区别每一个widget的。

GlobalKey的使用

import 'package:flutter/material.dart';

class GlobalKeyDemo extends StatelessWidget {
  final GlobalKey<_ChildPageState> _globalKey = GlobalKey();
  GlobalKeyDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GlobalKeyDemo'),
      ),
      body: ChildPage(
        key: _globalKey,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _globalKey.currentState!.setState(() {
            _globalKey.currentState!.data =
                'old:' + _globalKey.currentState!.count.toString();
            _globalKey.currentState!.count++;
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

class ChildPage extends StatefulWidget {
  const ChildPage({Key? key}) : super(key: key);

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

class _ChildPageState extends State<ChildPage> {
  int count = 0;
  String data = 'hello';
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: [
          Text(count.toString()),
          Text(data),
        ],
      ),
    );
  }
}
GlobalKey使用

通过使用GlobalKey生成的key,可以实现StatelessWidget 访问StatefulWidget中的数据信息。验证了前文所说的GlobalKey能帮助我们访问某个Widget的信息。

相关文章

  • Flutter - Key的原理

    前言 上篇文章我们简单探索了Flutter的渲染原理---Flutter初探渲染原理初探[https://www....

  • Flutter - Key内部原理浅析

    Key的内部原理 大部分时间用不到Key。加了也不会有什么副作用,不过也没必要消耗额外的空间。就像这样Map

  • Flutter之key的原理和使用

    之前讲flutter的渲染原理时我们知道flutter有三棵树:widgetTree,elementTree,re...

  • Flutter 中 key 的原理及作用

    Key 的原理 如图 1 所示,当我们生成一个 Widget 树的时候也会对应生成 Element 树,Widge...

  • Flutter 渲染原理

    1、Flutter 渲染原理 Flutter框架包含Flutter Framework(Dart)、Flutter...

  • Flutter key

    新创建一个Flutter Application的时候,默认生成的代码里面有这么一段 title很好理解,给App...

  • Flutter ---- Key

    Key Key 本身是一个抽象类 LocalKey:用作diff算法的核心所在,用作Element和Widget进...

  • Flutter Key

    什么是key Key 能够帮助开发者在 Widget tree 中保存状态。 Flutter | 深入浅出Key ...

  • Flutter Key

    LocalKey 应用于拥有相同父 Element 的小部件进行比较的情况 1. UniqueKey A key ...

  • flutter的key

    Key本身是一个抽象类,用作diff的核心算法比较Widget。-ValueKey 以一个数据作为Key-Obje...

网友评论

    本文标题:Flutter - Key的原理

    本文链接:https://www.haomeiwen.com/subject/quhpqrtx.html