Flutter入门笔记系列文章部分内容来源于《Flutter 实战》,如有侵权请联系删除!
实际业务中,在正式向服务器提交数据前,都会对各个输入框数据进行合法性校验,但是对每一个TextField都分别进行校验将会是一件很麻烦的事。还有,如果用户想清除一组TextField的内容,除了一个一个清除有没有什么更好的办法呢?为此,Flutter提供了一个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,则会返回到上一个路由。此属性通常用于拦截返回按钮。
- onChanged:Form的任意一个子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
FormState为Form的State类,可以通过Form.of()或GlobalKey获得。我们可以通过它来对Form的子孙FormField进行统一操作。我们看看其常用的三个方法:
- FormState.validate():调用此方法后,会调用Form子孙FormField的validate回调,如果有一个校验失败,则返回false,所有校验失败项都会返回用户返回的错误提示。
- FormState.save():调用此方法后,会调用Form子孙FormField的save回调,用于保存表单内容
- FormState.reset():调用此方法后,会将子孙FormField的内容清空。
假设我们需要在Flutter基础组件<输入框TextField>一文中登录功能的基础上增加新的需求:
验证用户名和密码格式:用户名为11位,密码不少于6位。
class FormPage extends StatefulWidget {
final String title;
FormPage({Key key, this.title}) : super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new FormPageState();
}
}
class FormPageState extends State<FormPage> {
TextEditingController _unameController = new TextEditingController();
TextEditingController _pwdController = new TextEditingController();
GlobalKey _formKey = new GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: AppBar(
title: Text("Form"),
),
body: Center(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
autofocus: true,
controller: _unameController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: "请输入手机号码",
labelText: "手机号",
prefixIcon: Icon(Icons.phone),
// 未获得焦点下划线设为灰色
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
//获得焦点下划线设为蓝色
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
),
),
//校验手机号
validator: (phone) {
return phone.length == 11 ? null : "手机号错误";
},
),
TextFormField(
controller: _pwdController,
keyboardType: TextInputType.visiblePassword,
obscureText: true,
decoration: InputDecoration(
hintText: "请输入密码",
labelText: "密码",
prefixIcon: Icon(Icons.vpn_key),
// 未获得焦点下划线设为灰色
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey),
),
//获得焦点下划线设为蓝色
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
),
),
//校验密码
validator: (password) {
return password.length > 6 ? null : "密码不能少于6位数";
},
),
Padding(
padding: EdgeInsets.all(16),
child: RaisedButton(
child: Text("开始登录"),
onPressed: () {
//开始登录前验证
if ((_formKey.currentState as FormState).validate()) {
print("验证通过");
}
}),
)
])),
),
);
}
}
运行效果:
格式校验