GlobalKey的作用

先说结论,后文会分析此结论的由来: 每个 Widget 都对应一个 Element ,我们可以直接对 Widget 进行操作,但是无法直接操作 Widget 对应的 Element 。而 GlobalKey 就是那把直接访问 Element 的钥匙。通过 GlobalKey 可以获取到 Widget 对应的 Element ,比如获取 StatelessElement StatefulElement . 比如如果获取到了 StatefullElement ,那么我们就可以获取 StatefulElement State 对象。

下面我们就以 Form 表单为例来分析 GlobalKey 为什么可以获取 Widget 对应的 Element

GlobalKey实战举例

登录肯定要有输入用户名和密码的输入框,在 Flutter 中我们只用 Form 表单+ TextFormField 的形式加以实现。现在就来讲讲 Form TextFormField 的简单使用, demo 中登录界面如下:



然后我们在不输入任何字符的情况下点击submit按钮,效果如下所示:


上图布局的代码如下所示:

import 'package:flutter/material.dart';
//使用GlobalKey来使用Form
class FormUseGlobalKeyDemo extends StatefulWidget {
  @override
  _FormState createState() {
    return _FormState();
class _FormState extends State<FormUseGlobalKeyDemo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("使用GlobalKey的Form表单"),
      body: _createForm(),
  final _formKey = GlobalKey<FormState>();
  Widget _createForm() {
    return Form(
        key: _formKey,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            TextFormField(
              validator: (value) {///输入字符校验
                if (value.isEmpty) {
                  return '请输入文字';
                return null;
            _createSubmitButton(),///创建submit按钮
  Widget _createSubmitButton() {
    return RaisedButton(
      onPressed: () {
        Form.of(context);
        if (_formKey.currentState.validate()) {///点击时开始非空验证
            print('验证通过');
      child: Text('Submit'),

如上所示首先初始化GlobalKey对象。然后将此对象设置为Form的key,最后再点击Submit按钮的时候,我们没有直接操作TextFormField,而是通过_formKey.currentState.validate对输入框TextFormField的内容进行非空验证。代码中的_formKey.currentState其类型是FormState

 class Form extends StatefulWidget {
  const Form({
    Key key,
    @required this.child,
  @override
  FormState createState() => FormState();
 //调用Form.of(context)也可以获取FormState对象
 //详情请看【Flutter之实战InheritedWidget详解】
  static FormState of(BuildContext context) {
    final _FormScope scope = context.inheritFromWidgetOfExactType(_FormScope);
    return scope?._formState;

GlobalKey获取Element的原理

abstract class GlobalKey<T extends State<StatefulWidget>> extends Key { //一个静态的变量map集合 static final Map<GlobalKey, Element> _registry = <GlobalKey, Element>{};

从 GlobalKey<T extends State<StatefulWidget>> 的类结构可以看出,GlobalKey主要用来存储状态信息 State<StatefulWidget>State指的是StatefulWidget 的状态类,通过StatefulWidget 的createState方法创建:

abstract class StatefulWidget extends Widget {
  //Key是个options的,可以设置也可以不设置
  const StatefulWidget({ Key key }) : super(key: key);
  @protected
  State createState();

上文中为什么通过GlobalKey.currentState就可以获取到FormState呢?二者是怎么关联起来的呢?现在就来一探究竟。

先来看看GlobalKeycurrentState方法的具体实现:

  T get currentState {
    //当前的Element对象
    final Element element = _currentElement;
    //检测是否是SatefulElement对象
    if (element is StatefulElement) {
      final StatefulElement statefulElement = element;
      //获取StatefulElement对象的State对象
      final State state = statefulElement.state;
      //如果状态匹配,则返回对应的T
      if (state is T)
        return state;
    return null;
  //_currentElement是一个map集合Map<GlobalKey, Element> 
  //该集合以GlobalKeyweight对象,其值保存的是Element。
  Element get _currentElement => _registry[this];

GlobalKey内部有一个静态的的_registry Map集合,该集合以GlobalKey为key,以Element为value;其提供的currentState 方法就是以GlobalKey对象为Key获取对应的StatefulElement 对象,然后从StatefulElement.state里获取具体的值FormState那么什么时候往_registry 集合里填充数据呢?通过Fultter之Element和Widget对应关系解析我们知道一个Element在创建之后会调用mount方法:

void mount(Element parent, dynamic newSlot) { ///省略部分代码 if (widget.key is GlobalKey) { final GlobalKey key = widget.key; //将Element对象注册进来 key._register(this); //GlobalKey的_register方法。 void _register(Element element) { _registry[this] = element;

可以发现在mount方法将我们创建的Element注入到GlobalKey的静态map集合中去!所以GlobalKey的作用就是:*持有当前WidgetElement对象,因此通过GlobalKey对象可以获取到当前StatefulWidgetStatefullElement,在通过StatefullElement获取State状态对象,从而操控State的相关方法。比如FormState的validate()方法进行非空校验

事实上我们还可以使用Form.of(context)方法也可以获到FormState对象,然后调用validate方法完成TextFormField的非空校验,其中原理,详细解析见Flutter之实战InheritedWidget详解

完整代码:https://github.com/guoyanqiu/flutter_login

当一个主页面比较复杂时,会包含多个widget,如果直接调用setState,会遍历所有子Widget的build,这是非常不必要的性能开销,有没有单独刷新指定Widget的方式呢?这个时候就要用到GlobalKey了 globakey 需要包裹你要更新的子weight import 'package:cell_input/globalkey/button.dart'; import 'package:cell_input/globalkey/text.dart'; import 我们在上一章说到,同一级相同类型的Widget不给它传Key的话,Flutter有时候就会出现分不清它们之间的对应关系,尤其是Widget之间的顺序发生改变的时候. 此时,我们就需要传个key给它. Key的种类 Key有两个子类: LocalKey 局部键,在同一级要唯一,可以理解为同级唯一性 GlobalKey 全局键 , 在整个App必须是唯一的. 从性能上来讲,如果不需要用到GlobalKey的话,尽量不用,LocalKey因为只对比同一级别,因此会快很多.上一章也说过,在父级或者子级是不 Flutter Key的原理和使用(一) 没有Key会发生什么 Flutter Key的原理和使用 (二) Widget 和 Element 的对应关系 Flutter Key的原理和使用(三) LocalKey的三种类型 上一章,因为标题的原因哈,没有介绍到关于GlobalKey的内容,今天来讲一讲GlobalKey. GlobalKey是在整个应用程序唯一的键。 我们之前讲到,LocalKey是局部键,所以出现层级改变的时候: Column( children: [ Box(Color class SwitcherWidget extends StatefulWidget { SwitcherWidget({Key key}):super(key:key); @override Swit. 博主打算通过登录功能来总结一下Flutter的相关知识点。为什么选择登陆功能呢?因为登录功能逻辑简单,很好抽象,功能也很普遍,这也是博主以此为突破口写android MVC和MVP探讨的原因。本系列应该不长,初步估计也就三篇左右,通过层层递进,慢慢地会勾勒出一个完整的登陆功能的demo,demo的源码点此可得。俗话说站在巨人的肩膀上成长的更快,当然这个俗话是我瞎篇水字数的,本系列博文的重要参考资料... GlobalKey只能单独使用不能重复使用,不然会报Duplicate GlobalKey detected in widget tree.当然你可以定义多个GlobalKey。当我们需要找到一个数据时,我们给这个组件定义一个GlobalKey。 _globalKey.currentState:获取所定义的State状态 _globalKey.currentWidget:获取所定义的 如果PageA没有被打开过,调用 PageAState.currentInstance().reloadData()是会出现空指针的。在 PageB 里调用 PageAState 的 reloadData() 方法。PageA的定义如下。 前言作为系列文章的第三篇,继篇章一和篇章二之后,本篇将为你着重展示:Flutter开发过程的打包流程、APP包对比、细节技巧与问题处理。本篇主要描述的Flutter的打包、在开发过程遇到的各类问题与细节,算是对上两篇的补全。一、打包首先我们先看结果,如下表所示,是 Flutter 与 React Native 、IOS 与 Android 的纵向与横向对比 。从上表我们可以看到:Fluuter的... 当你有一个滑动列表,你通过某一个 Item 跳转到了一个新的页面,当你返回之前的列表页面时,你发现滑动的距离回到了顶部。LocalKey 直接继承至 Key,它应用于拥有相同父 Element 的小部件进行比较的情况,也就是上述例子,有一个多子 Widget 需要对它的子 widget 进行移动处理,这时候你应该使用Localkey。如果您有一个 Todo List 应用程序,它将会记录你需要完成的事情。如果你有一个生日应用,它可以记录某个人的生日,并用列表显示出来,同样的还是需要有一个滑动删除操作。. 这几天想着需要一个Flutter的Toast, 一开始的想法就是自己定义一个简单用用, 也看到有人提供了相关的 fluttertoast组件, 但是因为是学习Flutter, 所以感觉还是自己实现一个比较好, 索性网络上也有不少方法… 这里主要是使用Overlay来进行Toast的实现, 用过RN的teaset 基本对Overlay不陌生, 当然还是需要重新认识一下Flutter下的Overlay Flutter 89: 图解基本 Overlay 悬浮新手引导 Flutter 使用 Overlay 实现全局