Flutter中GlobalKey的用法

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter中GlobalKey的用法相关的知识,希望对你有一定的参考价值。

GlobalKey的作用

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

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

GlobalKey实战举例

登录肯定要有输入用户名和密码的输入框,在Flutter中我们只用Form表单+TextFormField的形式加以实现。现在就来讲讲FormTextFormField的简单使用,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

以上是关于Flutter中GlobalKey的用法的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 局部Widget刷新

Flutter 局部Widget刷新

为啥 Flutter 中的 Form 需要 GlobalKey?

flutter通过 GlobalKey 获取界面任意元素坐标尺寸

Flutter:多个小部件使用相同的 GlobalKey

在小部件树中检测到 Flutter Duplicate GlobalKey