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