Flutter Form 获取所有表单字段的有效性且不报错

问题描述 投票:0回答:3

基本上,我正在寻找

FormFieldState.isValid
,但寻找
FormState

所以挑战是我想根据所有表单字段是否有效来动态启用/禁用我的表单提交按钮,而不调用

FormState.validate()
直到实际单击提交按钮,因为这会报告尚未生效的错误领域。

flutter forms user-experience
3个回答
0
投票

我为 isValid 保留一个额外的变量并将其存储在流中,因此每次字段更改时我们都会询问其是否有效并通知我们的 isValid 变量,这可能在某些时候与表单不同步,因此并不理想,但它作品类型


-1
投票

您可以尝试在

AutovalidateMode
上研究
FormField
吗?我认为他们会对这种情况有一些选择。

/// Used to configure the auto validation of [FormField] and [Form] widgets.
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,
}

-1
投票

请参考以下代码

解决方案 1:始终启用 AutovalidateMode

class HomeScreen extends StatelessWidget {
  HomeScreen({Key key}) : super(key: key);

  final TextEditingController addressController = TextEditingController();
  final FocusNode addressFocus = FocusNode();
  final TextEditingController stateController = TextEditingController();
  final FocusNode stateFocus = FocusNode();
  final _validationKey = GlobalKey<FormState>();

  int validateAddress(String address) {
    String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
    RegExp regExp = new RegExp(patttern);
    if (address.isEmpty || address.length == 0) {
      return 1;
    } else if (address.length < 10) {
      return 3;
    } else {
      return 0;
    }
  }

  int validateState(String state) {
    String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
    RegExp regExp = new RegExp(patttern);
    if (state.isEmpty || state.length == 0) {
      return 1;
    } else {
      return 0;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.lightBlue,
        automaticallyImplyLeading: true,
        leading: Icon(
          Icons.arrow_back,
        ),
        title: Text("Example"),
        centerTitle: true,
      ),
      body: Container(
        padding: EdgeInsets.all(15.0),
        child: Column(
          children: [
            Form(
              key: _validationKey,
              child: Column(
                children: [
                  /* State */
                  TextFormField(
                    autovalidateMode: AutovalidateMode.always,
                    /* autovalidate is set to true */
                    controller: stateController,
                    inputFormatters: [
                      FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
                      FilteringTextInputFormatter.deny(RegExp(
                          r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
                    ],
                    keyboardType: TextInputType.text,
                    maxLength: 160,
                    onChanged: (val) {},
                    maxLines: 1,
                    validator: (value) {
                      int res = validateAddress(value);
                      if (res == 1) {
                        return "Please enter state";
                      } else {
                        return null;
                      }
                    },
                    focusNode: stateFocus,
                    autofocus: false,
                    decoration: InputDecoration(
                      errorMaxLines: 3,
                      counterText: "",
                      filled: true,
                      fillColor: Colors.white,
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      disabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      enabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                        ),
                      ),
                      errorBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.all(Radius.circular(4)),
                          borderSide: BorderSide(
                            width: 1,
                            color: Colors.red,
                          )),
                      focusedErrorBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Colors.red,
                        ),
                      ),
                      hintText: "Enter state" ?? "",
                    ),
                  ),
                  SizedBox(
                    height: 15.0,
                  ),

                  /* Address */
                  TextFormField(
                    autovalidateMode: AutovalidateMode.always,
                    /* autovalidate is enabled */
                    controller: addressController,
                    inputFormatters: [
                      FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
                      FilteringTextInputFormatter.deny(RegExp(
                          r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
                    ],
                    keyboardType: TextInputType.text,
                    maxLength: 60,
                    onChanged: (val) {},
                    maxLines: 1,
                    validator: (value) {
                      int res = validateAddress(value);
                      if (res == 1) {
                        return "Please enter address";
                      } else if (res == 3) {
                        return "Please enter minimum 10 characters";
                      } else {
                        return null;
                      }
                    },
                    focusNode: addressFocus,
                    autofocus: false,
                    decoration: InputDecoration(
                      errorMaxLines: 3,
                      counterText: "",
                      filled: true,
                      fillColor: Colors.white,
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      disabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      enabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                        ),
                      ),
                      errorBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.all(Radius.circular(4)),
                          borderSide: BorderSide(
                            width: 1,
                            color: Colors.red,
                          )),
                      focusedErrorBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Colors.red,
                        ),
                      ),
                      hintText: "Enter address" ?? "",
                    ),
                  ),
                ],
              ),
            ),
            SizedBox(
              height: 15.0,
            ),
          ],
        ),
      ),
    );
  }

解决方案2:仅在用户交互后启用验证

class MainScreen extends StatelessWidget {
  MainScreen({Key key}) : super(key: key);

  final TextEditingController addressController = TextEditingController();
  final FocusNode addressFocus = FocusNode();
  final TextEditingController stateController = TextEditingController();
  final FocusNode stateFocus = FocusNode();
  final _validationKey = GlobalKey<FormState>();

  int validateAddress(String address) {
    String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
    RegExp regExp = new RegExp(patttern);
    if (address.isEmpty || address.length == 0) {
      return 1;
    } else if (address.length < 10) {
      return 3;
    } else {
      return 0;
    }
  }

  int validateState(String state) {
    String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
    RegExp regExp = new RegExp(patttern);
    if (state.isEmpty || state.length == 0) {
      return 1;
    } else {
      return 0;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.lightBlue,
        automaticallyImplyLeading: true,
        leading: Icon(
          Icons.arrow_back,
        ),
        title: Text("Example"),
        centerTitle: true,
      ),
      body: Container(
        padding: EdgeInsets.all(15.0),
        child: Column(
          children: [
            Form(
              key: _validationKey,
              child: Column(
                children: [
                  /* State */
                  TextFormField(
                    autovalidateMode: AutovalidateMode.onUserInteraction,
                    /* autovalidate is disabled */
                    controller: stateController,
                    inputFormatters: [
                      FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
                      FilteringTextInputFormatter.deny(RegExp(
                          r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
                    ],
                    keyboardType: TextInputType.text,
                    maxLength: 160,
                    onChanged: (val) {},
                    maxLines: 1,
                    validator: (value) {
                      int res = validateAddress(value);
                      if (res == 1) {
                        return "Please enter state";
                      } else {
                        return null;
                      }
                    },
                    focusNode: stateFocus,
                    autofocus: false,
                    decoration: InputDecoration(
                      errorMaxLines: 3,
                      counterText: "",
                      filled: true,
                      fillColor: Colors.white,
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      disabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      enabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                        ),
                      ),
                      errorBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.all(Radius.circular(4)),
                          borderSide: BorderSide(
                            width: 1,
                            color: Colors.red,
                          )),
                      focusedErrorBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Colors.red,
                        ),
                      ),
                      hintText: "Enter state" ?? "",
                    ),
                  ),
                  SizedBox(
                    height: 15.0,
                  ),

                  /* Address */
                  TextFormField(
                    autovalidateMode: AutovalidateMode.onUserInteraction,
                    /* autovalidate is disabled */
                    controller: addressController,
                    inputFormatters: [
                      FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
                      FilteringTextInputFormatter.deny(RegExp(
                          r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
                    ],
                    keyboardType: TextInputType.text,
                    maxLength: 60,
                    onChanged: (val) {},
                    maxLines: 1,
                    validator: (value) {
                      int res = validateAddress(value);
                      if (res == 1) {
                        return "Please enter address";
                      } else if (res == 3) {
                        return "Please enter minimum 10 characters";
                      } else {
                        return null;
                      }
                    },
                    focusNode: addressFocus,
                    autofocus: false,
                    decoration: InputDecoration(
                      errorMaxLines: 3,
                      counterText: "",
                      filled: true,
                      fillColor: Colors.white,
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      disabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      enabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Color(0xffE5E5E5),
                        ),
                      ),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                        ),
                      ),
                      errorBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.all(Radius.circular(4)),
                          borderSide: BorderSide(
                            width: 1,
                            color: Colors.red,
                          )),
                      focusedErrorBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(4)),
                        borderSide: BorderSide(
                          width: 1,
                          color: Colors.red,
                        ),
                      ),
                      hintText: "Enter address" ?? "",
                    ),
                  ),
                ],
              ),
            ),
            SizedBox(
              height: 15.0,
            ),
            OutlinedButton(
              onPressed: () {
                _validationKey.currentState.validate();
                if (stateController.text.isEmpty) {
                  stateFocus.requestFocus();
                } else if (addressController.text.isEmpty ||
                    addressController.text.length < 10) {
                  addressFocus.requestFocus();
                }
              },
              child: Text("Validate"),
            )
          ],
        ),
      ),
    );
  }
}

解决方案 3:也可以使用自动验证,但目前已弃用

class MainScreen extends StatelessWidget {
  MainScreen({Key key}) : super(key: key);

  final TextEditingController addressController = TextEditingController();
  final FocusNode addressFocus = FocusNode();

  int validateAddress(String address) {
    String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
    RegExp regExp = new RegExp(patttern);
    if (address.isEmpty || address.length == 0) {
      return 1;
    } else if (address.length < 10) {
      return 3;
    } else {
      return 0;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.lightBlue,
        automaticallyImplyLeading: true,
        leading: Icon(
          Icons.arrow_back,
        ),
        title: Text("Example"),
        centerTitle: true,
      ),
      body: Container(
          padding: EdgeInsets.all(15.0),
          child: Center(
            child: TextFormField(
              autovalidate: true,
              controller: addressController,
              inputFormatters: [
                FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
                FilteringTextInputFormatter.deny(RegExp(
                    r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
              ],
              keyboardType: TextInputType.text,
              maxLength: 60,
              onChanged: (val) {},
              maxLines: 1,
              validator: (value) {
                int res = validateAddress(value);
                if (res == 1) {
                  return "Please enter address";
                } else if (res == 3) {
                  return "Please enter minimum 10 characters";
                } else {
                  return null;
                }
              },
              focusNode: addressFocus,
              autofocus: false,
              decoration: InputDecoration(
                errorMaxLines: 3,
                counterText: "",
                filled: true,
                fillColor: Colors.white,
                focusedBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(4)),
                  borderSide: BorderSide(
                    width: 1,
                    color: Color(0xffE5E5E5),
                  ),
                ),
                disabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(4)),
                  borderSide: BorderSide(
                    width: 1,
                    color: Color(0xffE5E5E5),
                  ),
                ),
                enabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(4)),
                  borderSide: BorderSide(
                    width: 1,
                    color: Color(0xffE5E5E5),
                  ),
                ),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(4)),
                  borderSide: BorderSide(
                    width: 1,
                  ),
                ),
                errorBorder: OutlineInputBorder(
                    borderRadius: BorderRadius.all(Radius.circular(4)),
                    borderSide: BorderSide(
                      width: 1,
                      color: Colors.red,
                    )),
                focusedErrorBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(4)),
                  borderSide: BorderSide(
                    width: 1,
                    color: Colors.red,
                  ),
                ),
                hintText: "Hint Text" ?? "",
              ),
            ),
          )),
    );
  }
}

© www.soinside.com 2019 - 2024. All rights reserved.