首页 > 其他分享 >基础组件:表单

基础组件:表单

时间:2024-08-23 14:37:29浏览次数:5  
标签:Form 基础 校验 表单 FormState context child 组件 FormField

实际业务中,在正式向服务器提交数据前,都会对各个输入框数据进行合法性校验,但是对每一个TextField都分别进行校验将会是一件很麻烦的事。还有,如果用户想清除一组TextField的内容,除了一个一个清除有没有什么更好的办法呢?为此,Flutter提供了一个Form 组件,它可以对输入框进行分组,然后进行一些统一操作,如输入内容校验、输入框重置以及输入内容保存。

一、Form

Form继承自StatefulWidget对象,它对应的状态类为FormState。我们先看看Form类的定义:

Form({
  required Widget child,
  bool autovalidate = false,
  WillPopCallback onWillPop,
  VoidCallback onChanged,
})
  • autovalidate:是否自动校验输入内容;当为true时,每一个子 FormField 内容发生变化时都会自动校验合法性,并直接显示错误信息。否则,需要通过调用FormState.validate()来手动校验。
  • onWillPop:决定Form所在的路由是否可以直接返回(如点击返回按钮),该回调返回一个Future对象,如果 Future 的最终结果是false,则当前路由不会返回;如果为true,则会返回到上一个路由。此属性通常用于拦截返回按钮。
  • onChangedForm的任意一个子FormField内容发生变化时会触发此回调。

二、FormField

Form的子孙元素必须是FormField类型,FormField是一个抽象类,定义几个属性,FormState内部通过它们来完成操作,FormField部分定义如下:

const FormField({
  ...
  FormFieldSetter<T> onSaved, //保存回调
  FormFieldValidator<T>  validator, //验证回调
  T initialValue, //初始值
  bool autovalidate = false, //是否自动校验。
})

为了方便使用,Flutter 提供了一个TextFormField组件,它继承自FormField类,也是TextField的一个包装类,所以除了FormField定义的属性之外,它还包括TextField的属性。

三、FormState

FormStateFormState类,可以通过Form.of()GlobalKey获得。我们可以通过它来对Form的子孙FormField进行统一操作。我们看看其常用的三个方法:

  • FormState.validate():调用此方法后,会调用Form子孙FormField的validate回调,如果有一个校验失败,则返回false,所有校验失败项都会返回用户返回的错误提示。
  • FormState.save():调用此方法后,会调用Form子孙FormFieldsave回调,用于保存表单内容
  • FormState.reset():调用此方法后,会将子孙FormField的内容清空。

四、示例

我们修改一下上面用户登录的示例,在提交之前校验:

  1. 用户名不能为空,如果为空则提示“用户名不能为空”。
  2. 密码不能少于 6 位,如果小于 6 为则提示“密码不能少于 6 位”。

完整代码:

import 'package:flutter/material.dart';

class FormTestRoute extends StatefulWidget {
  @override
  _FormTestRouteState createState() => _FormTestRouteState();
}

class _FormTestRouteState extends State<FormTestRoute> {
  TextEditingController _unameController = TextEditingController();
  TextEditingController _pwdController = TextEditingController();
  GlobalKey _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey, //设置globalKey,用于后面获取FormState
      autovalidateMode: AutovalidateMode.onUserInteraction,
      child: Column(
        children: <Widget>[
          TextFormField(
            autofocus: true,
            controller: _unameController,
            decoration: InputDecoration(
              labelText: "用户名",
              hintText: "用户名或邮箱",
              icon: Icon(Icons.person),
            ),
            // 校验用户名
            validator: (v) {
              return v!.trim().isNotEmpty ? null : "用户名不能为空";
            },
          ),
          TextFormField(
            controller: _pwdController,
            decoration: InputDecoration(
              labelText: "密码",
              hintText: "您的登录密码",
              icon: Icon(Icons.lock),
            ),
            obscureText: true,
            //校验密码
            validator: (v) {
              return v!.trim().length > 5 ? null : "密码不能少于6位";
            },
          ),
          // 登录按钮
          Padding(
            padding: const EdgeInsets.only(top: 28.0),
            child: Row(
              children: <Widget>[
                Expanded(
                  child: ElevatedButton(
                    child: Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Text("登录"),
                    ),
                    onPressed: () {
                      // 通过_formKey.currentState 获取FormState后,
                      // 调用validate()方法校验用户名密码是否合法,校验
                      // 通过后再提交数据。
                      if ((_formKey.currentState as FormState).validate()) {
                        //验证通过提交数据
                      }
                    },
                  ),
                ),
              ],
            ),
          )
        ],
      ),
    );
  }
}

运行后效果如下图所示:

Flutter_edit_G.png

注意,登录按钮的onPressed方法中不能通过Form.of(context)来获取FormState,原因是,此处的contextFormTestRoute的context,而Form.of(context)是根据所指定context向根去查找,而FormState是在FormTestRoute的子树中,所以不行。正确的做法是通过Builder来构建登录按钮,Builder会将widget节点的context作为回调参数:

Expanded(
 // 通过Builder来获取ElevatedButton所在widget树的真正context(Element) 
  child:Builder(builder: (context){
    return ElevatedButton(
      ...
      onPressed: () {
        //由于本widget也是Form的子代widget,所以可以通过下面方式获取FormState  
        if(Form.of(context).validate()){
          //验证通过提交数据
        }
      },
    );
  })
)

其实context正是操作Widget所对应的Element的一个接口,由于Widget树对应的Element都是不同的,所以context也都是不同的,有关context的更多内容会在本书后面进阶篇中详细讨论。Flutter中有很多“of(context)”这种方法,读者在使用时一定要注意context是否正确。


标签:Form,基础,校验,表单,FormState,context,child,组件,FormField
From: https://www.cnblogs.com/linuxAndMcu/p/18375951

相关文章

  • OI基础操作
    ----------------------------------IO------------------------------Directory.Exists(path)//判断文件夹是否存在------------------------------------------------------------------Directory.CreateDirectory(path)//根据路径创建出文件夹------------------------......
  • 基础组件:ICON
    Flutter中,可以像Web开发一样使用iconfont,iconfont即“字体图标”,它是将图标做成字体文件,然后通过指定不同的字符而显示不同的图片。在字体文件中,每一个字符都对应一个位码,而每一个位码对应一个显示字形,不同的字体就是指字形不同,即字符对应的字形是不同的。而在iconfont中,只......
  • 基础组件:单选开关和复选框
    一、简介Material组件库中提供了Material风格的单选开关Switch和复选框Checkbox,虽然它们都是继承自StatefulWidget,但它们本身不会保存当前选中状态,选中状态都是由父组件来管理的。当Switch或Checkbox被点击时,会触发它们的onChanged回调,我们可以在此回调中处理选中状态改变逻辑......
  • Flutter 自定义日期范围选择组件,使用更加灵活,满足UI设计需要
    一、实现的效果图二、虽然Flutter也为我们提供了日期范围选择组件showDateRangePicker,但是毕竟系统的UI不符合我们的设计风格,所以被迫只能自己实现一个了系统样式三、日历整体实现逻辑其实也很简单,如下:首先获取每个月份具体有多少天int_getMonthDays(DateTimetim......
  • python 基础语法学习
    1.IPO程序编写方法inputprocessoutput2.ord("北")字符转uncode码chr(21271)uncode码转字符3.文件写入fp=open('node.txt','w')print('北京欢迎你',file=fp)fp.close()4.键盘输入name=input("请输入您的姓名:")......
  • K8S基本概念和组件
    特点便携性无论公有云、私有云、混合云还是多云架构都全面支持可扩展模块化、可插拔、可挂载、可组合,支持各种形式的扩展自修复自保持应用状态、自重启、自复制、自缩放,声明式语法组件etcd保存整个集群状态,充当数据库角色,只与APIServer通讯apiserve......
  • 基础组件:图片
    Flutter中,我们可以通过Image组件来加载并显示图片,Image的数据源可以是asset、文件、内存以及网络。ImageProviderImageProvider是一个抽象类,主要定义了图片数据获取的接口load(),从不同的数据源获取图片需要实现不同的ImageProvider,如AssetImage是实现了从Asset中加载图片的I......
  • C++基础
    C++面向对象编程的四大特性:封装:封装是隐藏对象的属性和实现细节,仅对外公开接口,控制程序对类属性的读取和修改。良好的分装能减少耦合,同时隐藏实现细节。抽象:抽象包括数据抽象和过程抽象。数据抽象关注于目标的特性信息,过程抽象关注于目标的功能是什么,而不是功能怎么实现。继承......
  • 【AI绘画】零基础学会Stable Diffusion!保姆级教程!附全套学习教程,你离成为大佬只差看完
    大家好,我是SD教程菌。最近,我惊奇地发现,还有不少粉丝朋友还没用过AI绘画的顶流工具——StableDiffusion,简称“SD”。今天来出一期SD版零基础的AI绘画课,分为4个部分:SD能有多强?电脑配置要求软件安装基础使用流程1.SD能有多强?其实,2022年8月StableDiffusion就开源发布了,经......
  • C/C++语言基础--字符串(包括字符串与字符数组、字符串与指针、字符串处理函数等),代码
    本专栏目的更新C/C++的基础语法,包括C++的一些新特性前言无论什么语言,字符串都是最重要、最基础的数据类型,他对二进制有很好的对应关系在C语言中没有提供专门的处理字符串的类型,但是我们可以通过字符数组、开辟内存地址来处理字符串本文将从字符串与字符数组的关系、字符......