继续创作,加速生长!这是我参加「日新计划 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');
      },
    )

运转作用:

15、 Flutter Widgets 之 Form、FormField、TextFormField相关表单控件

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的savevalidatereset等办法,一般经过如下办法设置:

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',
                   ),
                 ),
               ),
             );
           }),
         ),
       )

15、 Flutter Widgets 之 Form、FormField、TextFormField相关表单控件

结构一个登录页面:

_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('登录')
          )
        ],
      ),
    );
  }

运转作用:

15、 Flutter Widgets 之 Form、FormField、TextFormField相关表单控件

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还有许多根底特点,如是否可以修改,装修器作用,等等,在实践项目中都是很常用的。

总结:

本篇主要介绍了FormFormFieldTextFormField等相关组件,以及FormStateautofocus的根本运用。 git代码地址:gitee.com/wywinstonwy…