flutter: 打造自己的FormField
Flutter提供了Form组件,方便开发者对表单输入进行统一操作,如输入内容校验、输入框重置以及输入内容保存等。Form的子孙必须是FormField类型,FormField是一个抽象类,部分属性的定义如下:
const FormField({
...
FormFieldSetter<T> onSaved, //保存回调
FormFieldValidator<T> validator, //验证回调
T initialValue, //初始值
bool autovalidate = false, //是否自动校验。
})
FormState为Form的State类,可以通过Form.of()或GlobalKey获得。我们可以通过它来对Form的子孙FormField进行统一操作。常用的方法有:
-
FormState.validate():调用此方法后,会调用Form子孙FormField的validate回调,如果有一个校验失败,则返回false,所有校验失败项都会返回用户返回的错误提示。 -
FormState.save():调用此方法后,会调用-Form子孙FormField的save回调,用于保存表单内容 -
FormState.reset():调用此方法后,会将子孙FormField的内容清空。
FormField本身的自定义实现较为复杂,所以官方提供了TextFormField这一组件,它继承自FormField类,用户通过这个组件可以对输入的内容进行上述几种常见操作。但是对于自定义的其他表单组件,比如我们自己实现了一个选择器selector,想把它放入Form里,验证和清空就不能使用Form自带的方法,如果表单中包含很多这类组件,校验、保存数据等常用的操作就显得非常麻烦,因此自己继承Formfield来加入到Form的统一操作是十分必要的,但是这方面的教程和资料在社区中难以查找。
因此本文希望给读者一个自定义Formfield的案例,让读者能够快速上手。我们以表单的重置(恢复初始值)为例,构建一个SelectorFormField ,让它能够设置初始值,并能够在Form执行reset的时候回到初始值,用同样的方法你也可以让表单实现所有组件在提交时统一检查,保存数据等操作。
1 目标
我们的目标是可以使用Form的reset方法统一使Form中包含的所有选择器的值回到初始值。
2 选择器简介
首先我们先简要介绍一下自己封装的选择器selector,该选择器在使用时可自定义颜色、icon、大小、选项列表等配置,实现效果如图所示,在这里我们需要着重关注的属性是选项列表itemList、默认选项defaultValue、值改变后的回调函数selectChange。

// 选项列表结构
class ListItem {
final String value;
final String label;
const ListItem({
this.value,
this.label,
});
}
List<ListItem> messageList = [
ListItem(value: 'all', label: '全部'),
ListItem(value: 'link', label: '链接'),
ListItem(value: 'file', label: '文件'),
ListItem(value: 'image', label: '图片/视频'),
ListItem(value: 'miniprogram', label: '小程序'),
],
// 组件使用示例
Selector(
backgroundColor: Colors.grey[200],
width: 168,
height: 32,
icon: Icon(
Icons.arrow_drop_down,
color: context.colors.black,
size: 16.0,
),
itemList: messageList,
defaultValue: 'all',
selectChange: handleChanged,
),
3 写一个可以改变和重置内容的FormFied
选择器在Form中的最主要交互就是【选择列表项目 ➡️ 改变选择值】,重置的主要交互就是【点击重置 ➡️ 将选择值恢复到初始值】,因此我们围绕这两个交互的最简单形式,设计一个FormFied,只包含三个元素,一个字符串用于显示当前选择的值,一个选择按钮用于改变选择的值,模拟选择行为,一个重置按钮将字符串恢复到初始值。
在SimpleField组件内,我们放置选择的字符串,和用于改变选择的值的按钮,用来模拟选择行为,我们先从外部传入默认值:“我是默认文字”,按下按钮后,通过didChange方法可以改变state内的值。
class SimpleFormField extends FormField<String> {
SimpleFormField({
String defaultValue,
}) : super(
initialValue: defaultValue,
builder: (FormFieldState<String> state) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(state.value),
IconButton(
icon: Icon(
Icons.radio_button_checked,
),
onPressed: () {
state.didChange('嘿,文字改变了');
},
),
],
);
});
}
实现的效果如下:

实现了在内部修改String的文字之后,我们在外部实现内容的重置,这也符合我们最终需要在Form中统一重置所有表单组件的结构设计。
对Form的状态进行重置的一般方法是定义一个Global_key,然后将key传入Form,需要重置时调用key当前状态的reset方法,具体代码如下:
final _key = GlobalKey<FormState>();
Form(
key: _key,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SimpleFormField(
defaultValue: "我是默认文字",
),
TextButton(
onPressed: () {
_key.currentState.reset();
},
child: Text('重置')),
],
)
),
通过这种外层调用方式,Form会将我们自定义的SimpleFormField中的值恢复到初始值,实现效果如下:

这样我们就基本实现了一个简单的可以改变和重置内容的formfield。
4 将FormField封装在选择器组件外层
选择器的选择行为可以看作是改变FromFied的字符串值,因此我们只需要将FormField的状态值传入选择器作为显示的值,并在传入的回调函数中写入改变值的的行为,基本的封装代码如下所示:
class SelectorFormField extends FormField<String> {
SelectorFormField({ // 外层接收所有参数,并一一写入内层组件
SelectButtonSize size,
double width,
double height,
Color backgroundColor,
Color borderColor,
bool noBorder,
Widget icon,
double listWidth,
double listHeight,
List itemList,
String defaultValue,
ValueChanged<String> selectChange,
}) : super(
initialValue: defaultValue,
builder: (FormFieldState<String> state) {
return Selector(
formValue: state.value, // 加入状态保存的值,用于内部选择值的显示
size: size,
width: width,
height: height,
backgroundColor: backgroundColor,
borderColor: borderColor,
noBorder: noBorder,
icon: icon,
listWidth: listWidth,
listHeight: listHeight,
itemList: itemList,
defaultValue: defaultValue,
onChange: (value) { //回调函数在Formfield中改变选择的值,并向最外层传递
state.didChange(value);
selectChange(value);
});
});
}
通过此类封装,只需要使用与selector同样的方法调用SelectorFormField,即可实现Form对selector的统一操作,实现效果如下:
这里以表单重置为例,感兴趣的同学也可自行尝试表单验证validator或者数据存储onSaved等其他表单行为。
转载自:https://juejin.cn/post/7270082131962314811