继续创作,加速生长!这是我参加「日新计划 10 月更文挑战」的第15天,点击检查活动详情
概述:
实践事务中,在正式向服务器提交数据前,都会对各个输入框数据进行合法性校验,可是对每一个TextField都别离进行校验将会是一件很费事的事。还有,假如用户想清除一组TextField的内容,除了一个一个清除有没有什么更好的办法呢?为此,Flutter供给了一个Form widget,它可以对输入框进行分组,然后进行一些统一操作,如输入内容校验、输入框重置以及输入内容保存。
Form的后代元素有必要是FormField类型,FormField是一个抽象类,界说几个特点,FormState内部经过它们来完结操作。
FormField
FormField是一个表单控件,此控件包含表单的状况,便利更新UI,一般情况下,我们不会直接运用FormField,而是运用TextFormField。
TextFormField
TextFormField承继自FormField,是一个输入框表单,因此TextFormField中有许多关于TextField的特点,TextFormField的根本用法:
TextFormField(
decoration: InputDecoration(
hintText: '电话号码',
),
autovalidateMode: AutovalidateMode.always,
validator: (value) {
RegExp reg = new RegExp(r'^\d{11}$');
if (!reg.hasMatch(value!)) {
return '请输入11位手机号码';
}
return null;
},
onSaved: (text){
print('$text');
},
)
运转作用:
onSaved
是一个可选参数,当Form调用FormState.save时才会回调此办法。
autovalidateModel
参数为枚举类型,disabled不自动验证,always实时验证,onUserInteraction,用户输入完结验证
enum AutovalidateMode {
/// No auto validation will occur.
disabled,
/// Used to auto-validate [Form] and [FormField] even without user interaction.
always,
/// Used to auto-validate [Form] and [FormField] only after each user
/// interaction.
onUserInteraction,
}
validator
验证函数,输入的值不匹配的时分回来的字符串显现在TextField的errorText特点位置,回来null,表明没有过错。
Form
Form组件是一个容器类控件,可以包含多个FormField表单控件,这样的优点是统一管理。
在运用Form的时分需求设置其key,经过key获取当时的FormState,然后可以调用FormState的save
、validate
、reset
等办法,一般经过如下办法设置:
final _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
...
)
获取FormState并调用相关办法:
var _state = _formKey.currentState;
if(_state.validate()){
_state.save();
}
Form根本用法:
Form(
autovalidateMode: AutovalidateMode.always,
onChanged: () {
Form.of(primaryFocus!.context!)!.save();
},
child: Wrap(
children: List<Widget>.generate(5, (int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ConstrainedBox(
constraints: BoxConstraints.tight(const Size(300, 50)),
child: TextFormField(
onSaved: (String? value) {
print('Value for field $index saved as "$value"');
},
decoration: InputDecoration(
hintText: '电话号码$index',
),
),
),
);
}),
),
)
结构一个登录页面:
_buildLogin(){
var _account;
var _pwd;
final _formKey = GlobalKey<FormState>();
return Form(
child: Column(
children: [
TextFormField(
autovalidateMode: AutovalidateMode.always,
decoration: InputDecoration(hintText: '请输入账号'),
onSaved: (value){
_account = value;
}, validator: (value) {
RegExp reg = new RegExp(r'^.{4}$');
if (!reg.hasMatch(value!)) {
return '账号最少4个字符';
}
return null;
},
),
//---------------
TextFormField(
autovalidateMode: AutovalidateMode.always,
decoration: InputDecoration(hintText: '输入暗码'),
obscureText: true,
onSaved: (value) {
_pwd = value;
},
validator: (value) {
return value!.length >= 6 ? null : '暗码最少6个字符';
},
),
//登录
ElevatedButton(
onPressed: (){
var _state = Form.of(context);
if(_state!.validate()){
_state.save();
_login(_account,_pwd);
}
},
child: Text('登录')
)
],
),
);
}
_login(account,pwd){
print("账号:$account\n暗码:$pwd");
}
我们希望用户在输入表单时点击回来按钮提示用户”承认退出吗?”,用法如下:
_buildLogin1(){
var _account;
var _pwd;
final _formKey = GlobalKey<FormState>();
return Form(
key: _formKey,
onWillPop: ()async{
return await showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Text('提示'),
content: Text('承认退出吗?'),
actions: [
ElevatedButton(onPressed: (){
Navigator.of(context).pop(false);
}, child: Text('取消')),
ElevatedButton(onPressed: (){
Navigator.of(context).pop(true);
}, child: Text('确定')),
],
);
});
},
child: Column(
children: [
TextFormField(
autovalidateMode: AutovalidateMode.always,
decoration: InputDecoration(hintText: '请输入账号'),
onSaved: (value){
_account = value;
}, validator: (value) {
RegExp reg = new RegExp(r'^.{4}$');
if (!reg.hasMatch(value!)) {
return '账号最少4个字符';
}
return null;
},
),
//---------------
TextFormField(
autovalidateMode: AutovalidateMode.always,
decoration: InputDecoration(hintText: '输入暗码'),
obscureText: true,
onSaved: (value) {
_pwd = value;
},
validator: (value) {
return value!.length >= 6 ? null : '暗码最少6个字符';
},
),
//登录
ElevatedButton(
onPressed: (){
var _state = Form.of(context);
if(_state!.validate()){
_state.save();
_login(_account,_pwd);
}
},
child: Text('登录')
)
],
),
);
}
运转作用:
onWillPop
回调决议Form
所在的路由是否可以直接回来,该回调需求回来Future<bool>
,回来false
表明当时路由不会回来;为true
,则会回来到上一个路由。此特点一般用于阻拦回来按钮。
onChanged
:当子表单控件发生变化时回调。
回过头再看看TextField结构函数:
class TextFormField extends FormField<String> {
/// Creates a [FormField] that contains a [TextField].
///
/// When a [controller] is specified, [initialValue] must be null (the
/// default). If [controller] is null, then a [TextEditingController]
/// will be constructed automatically and its `text` will be initialized
/// to [initialValue] or the empty string.
///
/// For documentation about the various parameters, see the [TextField] class
/// and [new TextField], the constructor.
TextFormField({
Key? key,
this.controller,
String? initialValue,
FocusNode? focusNode,
InputDecoration? decoration = const InputDecoration(),
TextInputType? keyboardType,
TextCapitalization textCapitalization = TextCapitalization.none,
TextInputAction? textInputAction,
TextStyle? style,
StrutStyle? strutStyle,
TextDirection? textDirection,
TextAlign textAlign = TextAlign.start,
TextAlignVertical? textAlignVertical,
bool autofocus = false,
bool readOnly = false,
ToolbarOptions? toolbarOptions,
bool? showCursor,
String obscuringCharacter = '•',
bool obscureText = false,
bool autocorrect = true,
SmartDashesType? smartDashesType,
SmartQuotesType? smartQuotesType,
bool enableSuggestions = true,
@Deprecated(
'Use autovalidateMode parameter which provide more specific '
'behaviour related to auto validation. '
'This feature was deprecated after v1.19.0.',
)
bool autovalidate = false,
@Deprecated(
'Use maxLengthEnforcement parameter which provides more specific '
'behavior related to the maxLength limit. '
'This feature was deprecated after v1.25.0-5.0.pre.',
)
bool maxLengthEnforced = true,
MaxLengthEnforcement? maxLengthEnforcement,
int? maxLines = 1,
int? minLines,
bool expands = false,
int? maxLength,
ValueChanged<String>? onChanged,
GestureTapCallback? onTap,
VoidCallback? onEditingComplete,
ValueChanged<String>? onFieldSubmitted,
FormFieldSetter<String>? onSaved,
FormFieldValidator<String>? validator,
List<TextInputFormatter>? inputFormatters,
bool? enabled,
double cursorWidth = 2.0,
double? cursorHeight,
Radius? cursorRadius,
Color? cursorColor,
Brightness? keyboardAppearance,
EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
bool enableInteractiveSelection = true,
TextSelectionControls? selectionControls,
InputCounterWidgetBuilder? buildCounter,
ScrollPhysics? scrollPhysics,
Iterable<String>? autofillHints,
AutovalidateMode? autovalidateMode,
ScrollController? scrollController,
String? restorationId,
bool enableIMEPersonalizedLearning = true,
}) : assert(initialValue == null || controller == null),
assert(textAlign != null),
assert(autofocus != null),
assert(readOnly != null),
assert(obscuringCharacter != null && obscuringCharacter.length == 1),
assert(obscureText != null),
assert(autocorrect != null),
assert(enableSuggestions != null),
assert(autovalidate != null),
assert(
autovalidate == false ||
autovalidate == true && autovalidateMode == null,
'autovalidate and autovalidateMode should not be used together.',
),
assert(maxLengthEnforced != null),
assert(
maxLengthEnforced || maxLengthEnforcement == null,
'maxLengthEnforced is deprecated, use only maxLengthEnforcement',
),
assert(scrollPadding != null),
assert(maxLines == null || maxLines > 0),
assert(minLines == null || minLines > 0),
assert(
(maxLines == null) || (minLines == null) || (maxLines >= minLines),
"minLines can't be greater than maxLines",
),
assert(expands != null),
assert(
!expands || (maxLines == null && minLines == null),
'minLines and maxLines must be null when expands is true.',
),
assert(!obscureText || maxLines == 1, 'Obscured fields cannot be multiline.'),
assert(maxLength == null || maxLength == TextField.noMaxLength || maxLength > 0),
assert(enableInteractiveSelection != null),
assert(enableIMEPersonalizedLearning != null),
super(
key: key,
restorationId: restorationId,
initialValue: controller != null ? controller.text : (initialValue ?? ''),
onSaved: onSaved,
validator: validator,
enabled: enabled ?? decoration?.enabled ?? true,
autovalidateMode: autovalidate
? AutovalidateMode.always
: (autovalidateMode ?? AutovalidateMode.disabled),
builder: (FormFieldState<String> field) {
final _TextFormFieldState state = field as _TextFormFieldState;
final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration())
.applyDefaults(Theme.of(field.context).inputDecorationTheme);
void onChangedHandler(String value) {
field.didChange(value);
if (onChanged != null) {
onChanged(value);
}
}
return UnmanagedRestorationScope(
bucket: field.bucket,
child: TextField(
restorationId: restorationId,
controller: state._effectiveController,
focusNode: focusNode,
decoration: effectiveDecoration.copyWith(errorText: field.errorText),
keyboardType: keyboardType,
textInputAction: textInputAction,
style: style,
strutStyle: strutStyle,
textAlign: textAlign,
textAlignVertical: textAlignVertical,
textDirection: textDirection,
textCapitalization: textCapitalization,
autofocus: autofocus,
toolbarOptions: toolbarOptions,
readOnly: readOnly,
showCursor: showCursor,
obscuringCharacter: obscuringCharacter,
obscureText: obscureText,
autocorrect: autocorrect,
smartDashesType: smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
smartQuotesType: smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
enableSuggestions: enableSuggestions,
maxLengthEnforced: maxLengthEnforced,
maxLengthEnforcement: maxLengthEnforcement,
maxLines: maxLines,
minLines: minLines,
expands: expands,
maxLength: maxLength,
onChanged: onChangedHandler,
onTap: onTap,
onEditingComplete: onEditingComplete,
onSubmitted: onFieldSubmitted,
inputFormatters: inputFormatters,
enabled: enabled ?? decoration?.enabled ?? true,
cursorWidth: cursorWidth,
cursorHeight: cursorHeight,
cursorRadius: cursorRadius,
cursorColor: cursorColor,
scrollPadding: scrollPadding,
scrollPhysics: scrollPhysics,
keyboardAppearance: keyboardAppearance,
enableInteractiveSelection: enableInteractiveSelection,
selectionControls: selectionControls,
buildCounter: buildCounter,
autofillHints: autofillHints,
scrollController: scrollController,
enableIMEPersonalizedLearning: enableIMEPersonalizedLearning,
),
);
},
);
/// Controls the text being edited.
///
/// If null, this widget will create its own [TextEditingController] and
/// initialize its [TextEditingController.text] with [initialValue].
final TextEditingController? controller;
@override
FormFieldState<String> createState() => _TextFormFieldState();
}
表单聚集 FocusNode
TextFormField
支撑 autofocus
特点,在页面进来的时分,为 true 的输入框,会自动聚集
TextFormField(
autofocus: true,
decoration: InputDecoration(
hintText: '用户名',
),
)
动态指定输入框聚集
除了 autofocus
特点之外,TextFormField
还支撑传入一个 focusNode
,类型是 FocusNode
,经过传入之后,可以实现这个根本操作
首要我们需求创立一个 FocusNode:
FocusNode _focusNode = FocusNode();
TextFormField(
focusNode: _focusNode,
decoration: InputDecoration(
hintText: '电话号码',
),
)
经过 FocusScope
使 FocusNode 收效
Positioned(
child: InkWell(
child: Icon(Icons.edit),
onTap: () => FocusScope.of(context).requestFocus(_focusNode)),
bottom: 30,
right: 40,
)
除此之外TextFormField还有许多根底特点,如是否可以修改,装修器作用,等等,在实践项目中都是很常用的。
总结:
本篇主要介绍了Form
,FormField
,TextFormField
等相关组件,以及FormState
,autofocus
的根本运用。
git代码地址:gitee.com/wywinstonwy…