【问题标题】:How to clear error message in TextFormField in Flutter如何在 Flutter 中清除 TextFormField 中的错误消息
【发布时间】:2019-04-23 11:56:34
【问题描述】:

在我的代码中,我验证了电话号码。如果电话号码不正确 - 我会显示错误消息。但是,当用户开始编辑号码时,我想隐藏此错误消息。

我找到了currentState.reset() 的解决方案,但它似乎不是一个好的解决方案。我必须处理保存文本和光标位置的问题。我还有一个小神器。通常,当我按住退格键时-它会一一删除符号。如果我在显示错误消息时执行此操作 - 那么错误消息会消失,并且只会删除一个符号。

有人知道这个案例的正确解决方案吗?

final TextEditingController controller = TextEditingController();
final RegExp _phoneRegex = RegExp(r"^\+{1}\d{10, 15}\$");
bool isError = false;
TextSelection currentPosition;

return Column(
  children: <Widget>[
    Form(
        key: _textKey,
        child: TextFormField(
          controller: controller,
          validator: (str) {
            isError = true;
            if (str.isEmpty) {
              return err_empty_field;
            } else if (!_phoneRegex.hasMatch(str)) {
              return err_invalid_phone;
            }
            isError = false;
          },
        ),
        onChanged: () {
          if (controller.selection.start < 0 &&
              controller.text.length > 0) {
            TextSelection position =
                controller.text.length > currentPosition.start
                    ? currentPosition
                    : TextSelection.fromPosition(
                        TextPosition(offset: controller.text.length));
            controller.selection = position;
          }
          if (isError) {
            isError = false;
            currentPosition = controller.selection;
            if (currentPosition.start > controller.text.length) {
              currentPosition = TextSelection.fromPosition(
                  TextPosition(offset: controller.text.length));
            }
            String currentText = controller.text;
            _textKey.currentState.reset();
            controller.text = currentText;
            controller.selection = currentPosition;
          }
        },
      ),
    RaisedButton(
      onPressed: () {
        _textKey.currentState.validate();
      },
      child: Text(login),
    )
  ],
);

【问题讨论】:

    标签: flutter


    【解决方案1】:

    这里有一个适合这个问题的解决方案。

    您实际上不需要使用onChanged 或任何导致副作用的提示,我通过创建一个初始化为false 的类属性来解决它:

    bool _autovalidate = false;
    

    表单小部件有一个命名属性autovalidate。您应该将前一个布尔值传递给它:

    Form(
      key: _textKey,
      autovalidate: _autovalidate,
      ... 
    )
    

    在您的提交按钮onPressed() 方法中,如果表单是invalid,您应该将_autovalidate 布尔值更新为true,这将使表单在每次onChanged 调用时自动验证TextFormField :

    RaisedButton(
      onPressed: () {
        if (_textKey.currentState.validate()) {
          print('valid');
        } else {
          print('invalid');
          setState(() => _autoValidate = true);
        }
      },
      child: Text(login),
    )
    

    我希望它对某人有所帮助。

    编辑(2020 年 11 月)

    autovalidate 在 v1.19.0 之后被弃用。
    而是使用autovalidateMode:

    Form(
      autovalidateMode: AutovalidateMode.onUserInteraction`.
      ...
    )
    

    【讨论】:

    • 这对我有用,仔细管理自动验证。它导致代码更加简洁。
    • 我建议仅在用户尝试使用 setState 提交一次表单后才使用 AutovalidateMode.onUserInteraction,否则,用户将开始输入并立即显示错误 - 这不是最佳 UX。
    【解决方案2】:

    2021 年 1 月

    ...
    
    AutovalidateMode _autoValidate = AutovalidateMode.disabled;
    
    Form(
      key: _textKey,
      autovalidateMode: _autovalidate,
      ... 
    )
    
    RaisedButton(
      onPressed: () {
        if (_textKey.currentState.validate()) {
          print('valid');
        } else {
          print('invalid');
          setState(() => _autoValidate = AutovalidateMode.always);
        }
      },
      child: Text("login"),
    )
    

    【讨论】:

    • 这个。完美运行。谢谢
    【解决方案3】:

    这里的问题是errorTextTextFormFieldvalidator 字段自动管理。同时,简单的解决方案是手动处理errorText

    第 1 步:创建

    • 字符串字段,_errorText 初始化为 null。该字段将包含需要显示的错误消息。
    • 布尔字段,_error 初始化为 false。如果有错误,则归档为true,否则为false

    第 2 步:

    • _errorText 分配给TextFormField

    第 3 步(重要):

    • 确保TextFormField validator 返回null 值。

    • 在此处处理验证并将正确的错误消息分配给_errorText

    • 相应更新_error状态。

    第 4 步(重要):

    • 重置_errorText_error。这将在您开始编辑后立即从字段中删除错误。

    第 5 步:

    • onFieldSubmitted 中触发字段验证并管理您的代码流...
    import 'package:flutter/material.dart';
    
    class WorkGround extends StatefulWidget {
      @override
      _WorkGroundState createState() => _WorkGroundState();
    }
    
    class _WorkGroundState extends State<WorkGround> {
      final _formKey = GlobalKey<FormState>();
      final _usernameFocusNode = FocusNode();
      final _phoneNumberFocusNode = FocusNode();
    
      /*
      * Step 1.
      * */
      String _userNameErrorText;
      bool _userNameError = false;
      String _phoneNumberErrorText;
      bool _phoneNumberError = false;
    
      @override
      Widget build(BuildContext context) {
        return Form(
          key: _formKey,
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                TextFormField(
                  focusNode: _usernameFocusNode,
                  decoration: InputDecoration(
                    labelText: 'Username',
                    /*
                    * Step 2
                    * */
                    errorText: _userNameErrorText, // Handling error manually
                  ),
                  textInputAction: TextInputAction.next,
                  /*
                  * Step 3
                  * */
                  validator: (value) {
                    setState(() {
                      if(value.isEmpty) {
                        _userNameError = true;
                        _userNameErrorText = 'Enter Username';
                      }
                    });
                    return null; // Return null to handle error manually.
                  },
                  /*
                  * Step 4
                  * */
                  onChanged: (value) {
                    setState(() {
                      _userNameError = false;
                      _userNameErrorText = null; // Resets the error
                    });
                  },
                  /*
                  * Step 5
                  * */
                  onFieldSubmitted: (value) {
                    _formKey.currentState.validate(); // Trigger validation
                    if(!_userNameError) {
                      FocusScope.of(context).requestFocus(_phoneNumberFocusNode);
                    }
                  },
                ),
                TextFormField(
                  focusNode: _phoneNumberFocusNode,
                  decoration: InputDecoration(
                    labelText: 'Phone Number',
                    /*
                    * Step 2
                    * */
                    errorText: _phoneNumberErrorText, // Handling error manually
                  ),
                  textInputAction: TextInputAction.done,
                  /*
                  * Step 3
                  * */
                  validator: (value) {
                    setState(() {
                      if(value.isEmpty) {
                        _phoneNumberError = true;
                        _phoneNumberErrorText = 'Enter Phone number';
                      } else if( value.length < 10) {
                        _phoneNumberError = true;
                        _phoneNumberErrorText = 'Invalid Phone number';
                      }
                    });
                    return null; // Return null to handle error manually.
                  },
                  /*
                  * Step 4
                  * */
                  onChanged: (value) {
                    setState(() {
                      _phoneNumberError = false;
                      _phoneNumberErrorText = null; // Resets the error
                    });
                  },
                  /*
                  * Step 5
                  * */
                  onFieldSubmitted: (value) {
                    _formKey.currentState.validate(); // Trigger validation
                    if(!_phoneNumberError) {
                      // submit form or whatever your code flow is...
                    }
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    
    

    【讨论】:

      【解决方案4】:

      我已经实现了你的以下两个功能:

      1) 编辑时隐藏错误信息

      2) 按下登录按钮时验证输入字段

      注意:我已经评论了电话号码正则表达式并为 字符串长度

      import 'package:flutter/material.dart';
      import 'package:flutter/foundation.dart';
      
      void main() => runApp(MyApp());
      
      class MyApp extends StatelessWidget {
        // This widget is the root of your application.
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: MyHomePage(),
          );
        }
      }
      
      class MyHomePage extends StatefulWidget {
        @override
        _MyHomePageState createState() => _MyHomePageState();
      
      }
      
      class _MyHomePageState extends State<MyHomePage> {
      
        @override
        void initState(){
          super.initState();
        }
      
        final TextEditingController controller = TextEditingController();
      //  final RegExp _phoneRegex = RegExp(r"^\+{1}\d{10, 15}\$");
        bool isError = false;
        bool isWriting = false;
        bool isLoginPressed = false;
        int counter = 0;
        String myErrorString = "";
        TextSelection currentPosition;
        final _textKey = GlobalKey<FormState>();
      
        @override
        Widget build(BuildContext ctx) {
          return Scaffold(
            appBar: AppBar(
              title: Text('MapSample'),
            ),
            body: Container(
              child: Column(
                children: <Widget>[
                  Form(
                    key: _textKey,
                    child: TextFormField(
                      controller: controller,
                      validator: (str) {
                        myErrorString = "";
                        if(isLoginPressed){
                          isError = true;
                          if (str.isEmpty) {
                            myErrorString = 'err_empty_field';
                            return myErrorString;
                          }
                          else if (str.length < 10) {
                            myErrorString = 'err_invalid_phone';
                            validateMe();
                            return myErrorString;
                          }
                          /*else if (!_phoneRegex.hasMatch(str)) {
                            myErrorString = 'err_invalid_phone';
                            validateMe();
                            return myErrorString;
                          }*/
                          isError = false;
                          myErrorString = "";
                        }else{
                          myErrorString = "";
                        }
                      },
                    ),
      
                    onChanged: () {
                      counter++;
                      if(counter == 9){
                        counter = 0;
                        isLoginPressed = false;
                      }
                      if(isLoginPressed){
      
                      }else{
                        isWriting = true;
                        isLoginPressed = false;
                        myErrorString = "";
                        _textKey.currentState.validate();
                      }
                    },
      
                  ),
                  RaisedButton(
                    onPressed: () {
                      counter = 1;
                      isWriting = false;
                      isLoginPressed = true;
                      _textKey.currentState.validate();
                    },
                    child: Text('login'),
                  )
                ],
              ),
            ),
          );
        }
      
        void validateMe() {
          if(isLoginPressed){
            currentPosition = TextSelection.fromPosition(
                TextPosition(offset: controller.text.length));
            String currentText = controller.text;
            _textKey.currentState.reset();
            controller.text = currentText;
            controller.selection = currentPosition;
            isWriting = false;
            isLoginPressed = true;
          }
        }
      
      }
      

      【讨论】:

      • 感谢您的尝试,但有几个问题。首先 - 这里有一些不必要的字段和操作(例如isErrorisWriting)。此外,我更喜欢不要在代码中使用“魔术”数字。在您的情况下,9 只是经验值,仅在具体条件下有效。如果您使用空字符串检查代码 - 它不起作用,因为 counter 不等于 9。无论如何,感谢您的尝试,它几乎可以工作,但这不是解决方案
      • 感谢您的支持。我将尝试在没有额外变量和幻数的情况下获得更好的解决方案。
      【解决方案5】:

      我找到了更有效且更简单的方法

      final _textKey = GlobalKey<FormState>();
      final TextEditingController _controller = TextEditingController();
      
      Widget _getPhoneInputForm() {
        final RegExp _phoneRegex = RegExp(r"^\+{1}\d{10,17}");
        bool isError = false;
        bool isButtonPressed = false;
      
        return Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.symmetric(horizontal: 36.0),
              child: Form(
                key: _textKey,
                child: TextFormField(
                  keyboardType: TextInputType.phone,
                  decoration: InputDecoration(
                      hintText: hint_enter_phone,
                      contentPadding: EdgeInsets.all(24.0),
                      fillColor: Colors.blueGrey.withOpacity(0.3),
                      filled: true,
                      border: OutlineInputBorder(
                          borderRadius: BorderRadius.all(Radius.circular(16.0)),
                          borderSide: BorderSide(color: Colors.blueGrey))),
                  controller: _controller,
                  validator: (str) {
                    if (!isButtonPressed) {
                      return null;
                    }
                    isError = true;
                    if (str.isEmpty) {
                      return err_empty_field;
                    } else if (!_phoneRegex.hasMatch(str)) {
                      return err_invalid_phone;
                    }
                    isError = false;
                  },
                  onFieldSubmitted: (str) {
                    if (_textKey.currentState.validate()) _phoneLogin();
                  },
                ),
                onChanged: () {
                  isButtonPressed = false;
                  if (isError) {
                    _textKey.currentState.validate();
                  }
                },
              ),
            ),
            RaisedButton(
              color: Colors.teal,
              textColor: Colors.white,
              onPressed: () {
                isButtonPressed = true;
                if (_textKey.currentState.validate()) _phoneLogin();
              },
              child: Text(login),
            )
          ],
        );
      }
      

      【讨论】:

      • 在巴西,这被称为“G A M B I A R R A”。
      • isButtonPRessed 和 errorText 为我工作。我认为在 InputDecoration 中使用 errorText 比使用验证器更容易
      【解决方案6】:

      这是一个例子,我认为没有必要做 onchange() ,函数 validate name 做的工作......

       String validateName(String value) {
          String patttern = r'(^[a-zA-Z ]*$)';
          RegExp regExp = new RegExp(patttern);
          if (value.length == 0) {
            return "Name is Required";
          } else if (!regExp.hasMatch(value)) {
            return "Name must be a-z and A-Z";
          }
          return null;
        }
      
        TextFormField(
                                    controller: _lastname, validator: validateName ,
                                    //initialValue: widget.contact.last_name,
                                    decoration:
                                        InputDecoration(labelText: 'Last name'),
                                  ),
      
      void Save() {
       if (_keyForm.currentState.validate()) {
            // No any error in validation
            _keyForm.currentState.save(); 
      ................
      }
      

      【讨论】:

      • 根本不起作用。如果我从onChanged 调用验证 - 它总是在电话短时显示错误。如果我不这样做 - 错误消息不会消失。
      • 从 TextFormEditing 中删除 onchange()... 使用 textFormEditi 和控制器并进行测试。
      • 你有没有按照你的建议去做?没有onChanged 错误消息根本不会消失。如果您能找到解决方案 - 检查它是否正常工作,然后在此处发布。
      【解决方案7】:

      这种格式对我有用,希望对某人有所帮助....

      validator: (value){
      bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(value);
                                
                 isError = true;
                 if(value.isEmpty){
                                   return "Provide an email";
                  }else if(!emailValid){
                      return "Enter a valid email";
                                    
                         }
                         isError = false;
                                  return null;
                              },
      
      

      【讨论】:

        猜你喜欢
        • 2019-10-18
        • 2021-09-19
        • 2019-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-16
        • 1970-01-01
        相关资源
        最近更新 更多