表单组件是个包含表单元素的区域,表单元素容许用户输入内容,好比:文本区域,下拉表单,单选框、复选框等,常见的应用场景有:登录、注册、输入信息等。表单里有两个重要的组件,一个是Form组件用来作整个表单提交使用的,另外一个是TextFormField组件用来作用户输入的。html
属性 | 类型 | 说明 |
Key | Key | 组件在整个Widget树中的key值 |
autovalidate | bool | 是否自动提交表单 |
child | Widget | 组件child只能有一个组件 |
onChange | VoidCallback | 当FormField值改变时的回调函数 |
属性名 | 类型 | 说明 |
autovalidate | bool | 自动验证值 |
initalValue | T | 表单字段初始值,好比:输入收获地址时,默认回填本的地址信息 |
onSaved | FormFieldSetter<T> | 当Form表单调用保存方法Save时回调的函数 |
validator | FormFieldValidator<T> | Form表单验证器 |
对于输入框咱们最关心的时输入内容是否合法,好比邮箱地址是否正确,电话号码是不是数字等等,等用户输入完成后,咱们须要知道输入框输入的内容。那么咱们要如何才能获取到表单对象呢?为了获取表单的实例,咱们须要设置一个全局类型的key,经过这个key的属性,来获取表单对象:app
GlobalKey<FormState> globalKey = new GlobalKey<FormState>();
咱们来简单的写一个登录页面,校验输入框内的内容,当内容不合法时,并给出相应的提示:ide
import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; void main() => runApp(DemoApp()); class DemoApp extends StatefulWidget{ @override _DemoAppState createState() => new _DemoAppState(); } class _DemoAppState extends State<DemoApp> { String userName; String userPwd; GlobalKey<FormState> globalKey = new GlobalKey<FormState>(); void check(){ var loginForm = globalKey.currentState; //验证表单 if(loginForm.validate()){ loginForm.save(); Fluttertoast.showToast(msg: '信息提交成功',toastLength: Toast.LENGTH_LONG,gravity: ToastGravity.BOTTOM,textColor: Colors.white); } } @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: false, title: 'From表单Demo', home: new Scaffold( appBar: new AppBar( title: new Text('Form表单Demo'), leading: Icon(Icons.menu,size: 30,), actions: <Widget>[ IconButton(icon: Icon(Icons.search),iconSize: 30, onPressed: null) ], ), body: new Column( children: <Widget>[ new Form( key: globalKey, child: new Column( children: <Widget>[ new TextFormField( decoration: new InputDecoration( labelText: '请输入用户名', ), onSaved: (value){ userName = value; }, ), new TextFormField( decoration: new InputDecoration( contentPadding: EdgeInsets.only(left: 20,top: 10,right: 0,bottom: 0), hintText: '请输入密码', hintStyle: new TextStyle(fontSize: 30,color: Colors.amberAccent) ), obscureText: true, validator: (value){ return value.length < 6 ? '密码长度不够6位' : null; }, onSaved: (value){ userPwd = value; }, ) ], ), ), new Container( margin: new EdgeInsets.symmetric(vertical: 20,horizontal: 0), width: 330, height: 50, child: new SizedBox( child: new RaisedButton( onPressed: check, child: new Text( '肯定', style: new TextStyle( fontSize: 20, color: Colors.white ), ), ), ), ) ], ), ), ); } }
先看一下上面代码的效果截图,而后我会给你们讲一下代码的内容函数
写的比较丑,你们见谅,主要是由于我想多试试一些属性怎么用,总之是要多尝试嘛~布局
上面是输入内容前和输入内容后的对比图,从直观的表象上能够看的出来,用户名输入框输入内容后,提示内容被挤到上面去了,而密码输入框输入内容后提示内容则消失了,这是由于两个输入框提示语的text的类型使用的不一样,用户名:labelText,密码:hintText,这两个都有style属性,均可以对默认的提示内容字体进行属性设置。在点击确认按钮后,会调用check()方法,在check()方法中提交表单验证,这时候会触发密码输入框中的validator: (value),从而实现咱们密码输入框内容的校验,这个就是TextFromField中的验证回调方法,在表单验证的时候,会先验证这个方法中的逻辑判断,若是验证失败返回错误信息,若是验证经过则返回null。post
接下来的内容有兴趣的能够继续看一下,我想以Android中写xml的布局方式理解一下Flutter中的页面构建,由于我前面一直是在写简单的组件Demo,尚未造成一个完整的构建意识,如下是我我的的理解,若是有不对的地方,还请留言批评、指正,不胜感谢!!!学习
从整个页面来构思的话,能够看作是一个大容器,容器里面有三个组件,分别是两个输入框和一个按钮,这3个组件是垂直方向排列的,写Android的同窗看到这个页面,很容易就会想到最外层放一个Linearlayout或者是一个RelativeLayout,而后在里面垂直方向放上两个EditText和一个Button,再写一下控件的属性的就完事儿了,其实写Flutter也是这种思想。字体
注:在这里我先说明如下在Flutter中child里面只能放一个Widget,Children能够放多个Widget。ui
咱们来把Android和Flutter对比着理解,这样应该能够更容易理解一点,也更直观一点:url
1.Android:最外层垂直方向的Linearlayout或RelativeLayout。
Flutter:最外层body咱们new了一个Column(Column也是一个容器,相似于Container)
2.Android:放上两个EditText和一个Button
Flutter:容器放置好了,咱们要开始往容器里面加组件元素了,由于容器里面咱们要放置多个组件,因此咱们要使用children,children里面放一个Form表单和一个button,在Form表单里面,咱们要放两个输入框,并且是垂直方向放置的,想到这里是否是就该先考虑怎么把方向设置好呢?因此Form表单里的第一层widget就要放置一个Column容器,容器里面放两个输入框,既然是两个,那么是否是两个输入框就应该被一个children包裹起来呢?
总的来讲:根widget(column)->children(里面包含Form和button)->Form表单(第一层)->child(column)->children(里面包含了两个输入框TextFormField)->TextFormField(第三层)
最后看一下按钮部分的代码,说明一下为何外面要包一层Container
new Container( margin: new EdgeInsets.symmetric(vertical: 20,horizontal: 0), width: 330, height: 50, child: new SizedBox( child: new RaisedButton( onPressed: check, child: new Text( '肯定', style: new TextStyle( fontSize: 20, color: Colors.white ), ), ), ), )
之因此最外层包了个Container,是由于SizedBox和RaiseButton这两个组件都没有margin或padding属性,因此UI上要想控制边距等操做,这是一种处理方式。
也不知道我上面大白话写了那么多,能不能表达清楚个人意思,若是有没看懂的,还麻烦留言提问吧!!!