Flutter Key的原理和使用(四) GlobalKey 的用法

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

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

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

Flutter Key的原理和使用(四) GlobalKey 的用法

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

上一章,因为标题的原因哈,没有介绍到关于GlobalKey的内容,今天来讲一讲GlobalKey.
GlobalKey是在整个应用程序中唯一的键。

我们之前讲到,LocalKey是局部键,所以出现层级改变的时候:

Column(
  children: [
      Box(Colors.red, key: ValueKey(1)),
      Center(child: Box(Colors.red, key: ValueKey(2))),
  ],
)

12a.gif

可以看到红色的数字从3变成了0,状态丢失了.

12a.gif
这时候,我们就要用到globalKey.我们前面也提过,GlobalKey必须是全局唯一的,所以即使widget从tree中的位置改变了,也依然能够找到它.

12a.gif

GlobalKey的使用

使用GlobalKey不应该在每次build的时候重建GlobalKey, 它应该是State拥有的长期存在的对象.
所以我们应该先定义一个GlobalKey:

final GlobalKey _globalKey= GlobalKey();

将ValueKey替换成GlobalKey

这个数字太小了,我稍微改一下代码, 放大数字,并改成点击数字执行加1操作.并将Column改成Row:

Row(
  children: [
    Box(Colors.blue, key: ValueKey(1)),
    Box(Colors.red, key: _globalKey),
  ],
)

得到如下的效果:

asaed1.gif

可以看到,使用globalKey的widget的状态被完美的保存了下来.
使用ValueKey的没有保留之前的状态是因为Widget TreeElement Tree做对应关系的时候,因为类型改变无法对应,状态就被丢弃了.

GlobalKey的方法

我们可以通过GlobalKey来找到对应的widget,甚至是state等更多相关的东西.

image.png

可以看到,global对象提供了几个方法,我们主要用到的就是前三个,可以通过globalKey来找到对应的BuildContext,State以及Widget.

获取对应的state

比如我们可以通过这个globalKey来找到对应的按钮中的数字,我们打印一下看一看:

asda.gif

对应的代码:

floatingActionButton: FloatingActionButton(
  onPressed: () {
    print((_globalKey.currentState as _BoxState).count);
  },
  child: Icon(Icons.wifi_protected_setup),
)

可以看到,上面我进行了一个类型转换,因为globalKey是任何widget都可以使用的,flutter在运行之前并不知道具体是哪个widget用到了它,所以我们这要转换成对应的类型.

那如果我们想改变里面的值呢,是否可以做到呢?

我们改变一下onPress的代码:

onPressed: () {
  final state = _globalKey.currentState as _BoxState;
  state.count++;
  print(state.count);
  state.setState(() {});
}

切记,一定要进行setState操作,来触发Ui的刷新,否则值会修改,但是没有体现在UI上.

更好的写法是

state.setState(() {
  state.count++;
});

推荐把setState需要刷新的对象放入这个大括号{}内,因为在实际开发中,并不是每次有对象变化的时候多需要刷新UI的,如果能够按照规范将需要刷新的对象放到setState中,等到需求变更,代码需要修改的时候,setState中是空的时候,我们就知道不需要刷新UI了,就可以删除掉这个setState.

反之,如果是写到外面,当我们把需要刷新的对象的代码删除掉的时候,不知道这个setState到底还有没有用(毕竟实际我们可能是多人开发,也可能自己以前写的代码不记得了),就导致了额外的刷新操作.

当然,其实上述操作也不是推荐的做法,一般都是由自己内部来管理它的状态,如果有多个widget共用的话,则应该进行变量提示操作,把变量写到外层.

获取对应的widget

刚才我们也看到了,除了currentState,它还有一些别的东西.
比如currentWidget,当然我们代码对应的类型也要做改变了.

onPressed: () {
  final widget = _globalKey.currentWidget as Box;
  print(widget.color);
}

比如这里,因为widget是Box,所以类型转换为Box. 通过currentWidget,我们就可以得到对应的widget中的属性,例如此处的color.

获取对应的context

通过currentContext,可以获得对应的context,context其实指的就是element.

在第二章中我们介绍到了element tree,并没有详细说element.Element的作用还是比较多的.

比如我想知道对应组件的尺寸,位置.这其实都是难以获得的信息.

final renderBox = _globalKey.currentContext!.findRenderObject() as RenderBox;

此处,我们通过currentContext!.findRenderObject找到了对应的renderObject,RenderObject的类型还是比较多的.因为我们此处的Box刚好是RenderBox, 所以将类型转换为RenderBox.

image.png

可以看到renderBox有一个对应的size方法,通过它就可以拿到对应widget的尺寸.
如果想拿到它的位置信息的话,可以通过

renderBox.localToGlobal(Offset.zero);

来拿到对应widget的左上角距离屏幕左上角的坐标信息.
看一下分别打印的信息:

I/flutter ( 2158): Size(100.0, 100.0)      //对应widget的尺寸
I/flutter ( 2158): Offset(100.0, 80.0)     //对应widget的坐标

小结.

最后做一个简单的总结

本章主要介绍了globalKey的两种用法,两种用法都是先声明一个globalKey,再把这个globalKey传给想要放到的widget里面.

第一种用法,保证有了globalKey的widget可以随便改变它在Wdiget Tree中的位置,而它的状态不会丢失.

第二种用法,相当于通过这个key将widget暴露了出来,借助它就可以利用类似于getElementById之类的方式去找到它各种各样的东西:

  • currentContext: 可以找到包括renderBox在内的各种element有关的东西
  • currentWidget: 可以得到widget的属性
  • currentState: 可以得到state里面的变量.

猜你喜欢

转载自blog.csdn.net/u011272795/article/details/120141542