如何使用Form和GlobalKey验证alertDialog上的文本输入? [英] How to validate text input on alertDialog using Form and GlobalKey?

查看:91
本文介绍了如何使用Form和GlobalKey验证alertDialog上的文本输入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在alertDialog上有一个textfield,它可以接受Email并要对其进行验证.点击forgot password按钮后,alertDialog将在当前登录屏幕的前面打开. 我已经实现了登录验证,并试图使用类似的逻辑来实现上述目的.为了进行登录验证,我使用了效果很好的GlobalKey(_ formKey)和Form小部件.我正在使用另一个名为_resetKeyGlobalKey来获取验证的currentState,然后保存其状态.尽管这种方法有效,但我看到验证消息也显示在EmailPassword字段上.即,如果我点击打开对话框的忘记密码",然后点击send email,它会正确显示验证消息,但同时,在点击alertdialog中的取消按钮后,也会触发登录屏幕的验证消息.像这样:

I've a textfield on alertDialog which accepts Email and want to validate it. The alertDialog opens in front of current login screen after tapping on forgot password button. I've implemented login validation and was trying to use similar logic to achieve above. For login validation, I used GlobalKey(_formKey) and Form widget which works perfectly. I am using another GlobalKey named _resetKey to get the currentState of validation and then saving it's state. Although this approach is working, I see that the validation message is also displayed on Email and Password fields too. ie, If I tap on 'forgot password' that opens dialog, and tap on send email, it correctly shows the validation message, but at the same time, the validation message for login screen is triggered too after tapping on cancel button from alertdialog. Something like this:

对于alertDialog验证,下面是我的代码:

For alertDialog validation, below is my code:

// Creates an alertDialog for the user to enter their email
  Future<String> _resetDialogBox() {
    final resetEmailController = TextEditingController();

    return showDialog<String>(
      context: context,
      barrierDismissible: false, // user must tap button!
      builder: (BuildContext context) {
        return AlertDialog(
          title: new Text('Reset Password'),
          content: new SingleChildScrollView(
              child: new Form(
                key: _resetKey,
                autovalidate: _validate,
                child: ListBody(
                  children: <Widget>[
                    new Text(
                      'Enter the Email Address associated with your account.',
                      style: TextStyle(fontSize: 14.0),),
                    Padding(
                      padding: EdgeInsets.all(10.0),
                    ),
                    Row(
                      children: <Widget>[
                        new Padding(
                          padding: EdgeInsets.only(top: 8.0),
                          child: Icon(
                            Icons.email, size: 20.0,
                          ),
                        ),
                        new Expanded(
                          child: TextFormField(
                            validator: validateEmail,
                            onSaved: (String val) {
                              resetEmail = val;
                            },

new FlatButton(
              child: new Text(
                'SEND EMAIL', style: TextStyle(color: Colors.black),),
              onPressed: () {
                setState(() {
                  _sendResetEmail();
                });

void _sendResetEmail() {
    final resetEmailController = TextEditingController();
    resetEmail = resetEmailController.text;

    if (_resetKey.currentState.validate()) {
      _resetKey.currentState.save();

      try {
        Fluttertoast.showToast(
            msg: "Sending password-reset email to: $resetEmail",
            toastLength: Toast.LENGTH_LONG,
            bgcolor: "#e74c3c",
            textcolor: '#ffffff',
            timeInSecForIos: 4);

        _auth.sendPasswordResetEmail(email: resetEmail);
      } catch (exception) {
        print(exception);

        Fluttertoast.showToast(
            msg: "${exception.toString()}",
            toastLength: Toast.LENGTH_LONG,
            bgcolor: "#e74c3c",
            textcolor: '#ffffff',
            timeInSecForIos: 4);
      }
    }
    else {
      setState(() {
        _validate = true;
      });
    }
  }

使用_formKey要点的登录验证如下:

The login validation using _formKey gist is as below:

// Creates the email and password text fields
  Widget _textFields() {
    return Form(
        key: _formKey,
        autovalidate: _validate,
        child: Column(
          children: <Widget>[
            Container(
              decoration: new BoxDecoration(
                border: new Border(
                  bottom: new BorderSide(width: 0.5, color: Colors.grey),
                ),
              ),
              margin: const EdgeInsets.symmetric(
                  vertical: 25.0, horizontal: 65.0),

              // Email text field
              child: Row(
                children: <Widget>[
                  new Padding(
                    padding: EdgeInsets.symmetric(
                        vertical: 10.0, horizontal: 15.0),
                    child: Icon(
                      Icons.email,
                      color: Colors.white,
                    ),
                  ),
                  new Expanded(
                    child: TextFormField(
                      validator: validateEmail,
                      onSaved: (String val) {
                        email = val;
                      },

我认为它必须使用2个键来做一些事情,因为alertDialog显示在当前活动的前面. _formKey如何实现?或者还有其他方法吗?

I think it has to do something with the 2 keys, since the alertDialog is displayed in front of the current activity. How can I achieve with _formKey or is there any other approach ?

************需要完整的代码************

************ required full code ************

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: LoginScreen(),
      ),
    );
  }
}


class LoginScreen extends StatefulWidget {
  @override
  LoginScreenState createState() => new LoginScreenState();
}

class LoginScreenState extends State<LoginScreen> {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  final _formKey = GlobalKey<FormState>();
  final _resetKey = GlobalKey<FormState>();
  bool _validate = false;
  String email;
  String password;
  String resetEmail;

  // The controller for the email field
  final _emailController = TextEditingController();

  // The controller for the password field
  final _passwordController = TextEditingController();

  // Creates the 'forgot password' and 'create account' buttons
  Widget _accountButtons() {
    return Container(
      child: Expanded(
          child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: <Widget>[
                      Container(
                        child: new FlatButton(
                          padding: const EdgeInsets.only(
                              top: 50.0, right: 150.0),
                          onPressed: () => sendPasswordResetEmail(),
                          child: Text("Forgot Password",
                              style: TextStyle(color: Colors.white)),
                        ),
                      )
                    ]),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: <Widget>[
                    Container(
                      child: new FlatButton(
                        padding: const EdgeInsets.only(top: 50.0),
                        onPressed: () =>
                            Navigator.push(
                                context,
                                MaterialPageRoute(
                                    builder: (context) => CreateAccountPage())),
                        child: Text(
                          "Register",
                          style: TextStyle(color: Colors.white),
                        ),
                      ),
                    )
                  ],
                )
              ])),
    );
  }

  // Creates the email and password text fields
  Widget _textFields() {
    return Form(
        key: _formKey,
        autovalidate: _validate,
        child: Column(
          children: <Widget>[
            Container(
              decoration: new BoxDecoration(
                border: new Border(
                  bottom: new BorderSide(width: 0.5, color: Colors.grey),
                ),
              ),
              margin: const EdgeInsets.symmetric(
                  vertical: 25.0, horizontal: 65.0),

              // Email text field
              child: Row(
                children: <Widget>[
                  new Padding(
                    padding: EdgeInsets.symmetric(
                        vertical: 10.0, horizontal: 15.0),
                    child: Icon(
                      Icons.email,
                      color: Colors.white,
                    ),
                  ),
                  new Expanded(
                    child: TextFormField(
                      validator: validateEmail,
                      onSaved: (String val) {
                        email = val;
                      },
                      keyboardType: TextInputType.emailAddress,
                      autofocus: true,
                      // cursorColor: Colors.green,
                      controller: _emailController,
                      decoration: InputDecoration(
                        border: InputBorder.none,
                        hintText: 'Email',

                        //  contentPadding: EdgeInsets.fromLTRB(45.0, 10.0, 20.0, 1.0),
                        contentPadding: EdgeInsets.only(left: 55.0, top: 15.0),
                        hintStyle: TextStyle(color: Colors.white),
                      ),
                      style: TextStyle(color: Colors.white),
                    ),
                  )
                ],
              ),
            ),

            // Password text field
            Container(
              decoration: new BoxDecoration(
                border: new Border(
                  bottom: new BorderSide(
                    width: 0.5,
                    color: Colors.grey,
                  ),
                ),
              ),
              margin: const EdgeInsets.symmetric(
                  vertical: 10.0, horizontal: 65.0),
              child: Row(
                children: <Widget>[
                  new Padding(
                    padding: EdgeInsets.symmetric(
                        vertical: 10.0, horizontal: 15.0),
                    child: Icon(
                      Icons.lock,
                      color: Colors.white,
                    ),
                  ),
                  new Expanded(
                    child: TextFormField(
                        validator: _validatePassword,
                        onSaved: (String val) {
                          password = val;
                        },
                        //  cursorColor: Colors.green,
                        controller: _passwordController,
                        decoration: InputDecoration(
                          border: InputBorder.none,
                          hintText: 'Password',
                          contentPadding: EdgeInsets.only(
                              left: 50.0, top: 15.0),
                          hintStyle: TextStyle(color: Colors.white),
                        ),
                        style: TextStyle(color: Colors.white),

                        // Make the characters in this field hidden
                        obscureText: true),
                  )
                ],
              ),
            )
          ],
        )
    );
  }

  // Creates the button to sign in
  Widget _signInButton() {
    return new Container(
        width: 200.0,
        margin: const EdgeInsets.only(top: 20.0),
        padding: const EdgeInsets.only(left: 20.0, right: 20.0),
        child: new Row(
          children: <Widget>[
            new Expanded(
              child: RaisedButton(
                  shape: new RoundedRectangleBorder(
                      borderRadius: new BorderRadius.circular(30.0)),
                  splashColor: Colors.white,
                  color: Colors.green,
                  child: new Row(
                    children: <Widget>[
                      new Padding(
                        padding: const EdgeInsets.only(left: 35.0),
                        child: Text(
                          "Sign in",
                          style: TextStyle(color: Colors.white, fontSize: 18.0),
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ],
                  ),
                  onPressed: () {
                    setState(() {
                      _signIn();
                    });
                  }),
            ),
          ],
        ));
  }

  // Signs in the user
  void _signIn() async {
    // Grab the text from the text fields
    final email = _emailController.text;
    final password = _passwordController.text;

    if (_formKey.currentState.validate()) {
      _formKey.currentState.save();
      try {
        Fluttertoast.showToast(
            msg: "Signing in...",
            toastLength: Toast.LENGTH_LONG,
            bgcolor: "#e74c3c",
            textcolor: '#ffffff',
            timeInSecForIos: 2);

        firebaseUser = await _auth.signInWithEmailAndPassword(
            email: email, password: password);

        // If user successfully signs in, go to the pro categories page
        Navigator.pushReplacement(context,
            MaterialPageRoute(
                builder: (context) => ProCategories(firebaseUser)));
      } catch (exception) {
        print(exception.toString());

        Fluttertoast.showToast(
            msg: "${exception.toString()}",
            toastLength: Toast.LENGTH_LONG,
            bgcolor: "#e74c3c",
            textcolor: '#ffffff',
            timeInSecForIos: 3);
      }
    }
    else {
      setState(() {
        _validate = true;
      });
    }
  }

  // Creates an alertDialog for the user to enter their email
  Future<String> _resetDialogBox() {
    final resetEmailController = TextEditingController();

    return showDialog<String>(
      context: context,
      barrierDismissible: false, // user must tap button!
      builder: (BuildContext context) {
        return AlertDialog(
          title: new Text('Reset Password'),
          content: new SingleChildScrollView(
              child: new Form(
                  key: _resetKey,
                  autovalidate: _validate,
                child: ListBody(
                  children: <Widget>[
                    new Text(
                      'Enter the Email Address associated with your account.',
                      style: TextStyle(fontSize: 14.0),),
                    Padding(
                      padding: EdgeInsets.all(10.0),
                    ),
                    Row(
                      children: <Widget>[
                        new Padding(
                          padding: EdgeInsets.only(top: 8.0),
                          child: Icon(
                            Icons.email, size: 20.0,
                          ),
                        ),
                        new Expanded(
                          child: TextFormField(
                               validator: validateEmail,
                               onSaved: (String val) {
                                 resetEmail = val;
                               },
                            keyboardType: TextInputType.emailAddress,
                            autofocus: true,
                            decoration: new InputDecoration(
                                border: InputBorder.none,
                                hintText: 'Email',
                                contentPadding: EdgeInsets.only(
                                    left: 70.0, top: 15.0),
                                hintStyle: TextStyle(
                                    color: Colors.black, fontSize: 14.0)
                            ),
                            style: TextStyle(color: Colors.black),
                          ),
                        )
                      ],
                    ),
                    new Column(
                        children: <Widget>[
                          Container(
                            decoration: new BoxDecoration(
                                border: new Border(
                                    bottom: new BorderSide(
                                        width: 0.5, color: Colors.black)
                                )
                            ),
                          )
                        ]
                    ),
                  ],
                ),
              )
          ),

          actions: <Widget>[
            new FlatButton(
              child: new Text('CANCEL', style: TextStyle(color: Colors.black),),
              onPressed: () {
                Navigator.of(context).pop("");
              },
            ),
            new FlatButton(
              child: new Text(
                'SEND EMAIL', style: TextStyle(color: Colors.black),),
              onPressed: () {
                setState(() {
                    _sendResetEmail();
                });
                Navigator.of(context).pop(resetEmail);
              },
            ),
          ],
        );
      },
    );
  }

  // Sends a password-reset link to the given email address
  void sendPasswordResetEmail() async {
    String resetEmail = await _resetDialogBox();

    // When this is true, the user pressed 'cancel', so do nothing
    if (resetEmail == "") {
      return;
    }

    try {
      Fluttertoast.showToast(
          msg: "Sending password-reset email to: $resetEmail",
          toastLength: Toast.LENGTH_LONG,
          bgcolor: "#e74c3c",
          textcolor: '#ffffff',
          timeInSecForIos: 4);

      _auth.sendPasswordResetEmail(email: resetEmail);
    } catch (exception) {
      print(exception);

      Fluttertoast.showToast(
          msg: "${exception.toString()}",
          toastLength: Toast.LENGTH_LONG,
          bgcolor: "#e74c3c",
          textcolor: '#ffffff',
          timeInSecForIos: 4);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // prevent pixel overflow when typing
      resizeToAvoidBottomPadding: false,
      body: Container(
        decoration: BoxDecoration(
            image: DecorationImage(
                image: AssetImage(
                  "",
                ),
                fit: BoxFit.cover)),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            // QuickCarl logo at the top
            Image(
              alignment: Alignment.bottomCenter,
              image: AssetImage(""),
              width: 180.0,
              height: 250.0,
            ),
            new Text('',
                style: TextStyle(
                    fontStyle: FontStyle.italic,
                    fontSize: 12.0,
                    color: Colors.white)
            ),
            _textFields(),
            _signInButton(),
            _accountButtons()
          ],
        ),
      ),
    );
  }

  String validateEmail(String value) {
    String pattern = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
    RegExp regExp = new RegExp(pattern);
    if (value.length == 0) {
      return "Email is required";
    } else if (!regExp.hasMatch(value)) {
      return "Invalid Email";
    } else {
      return null;
    }
  }

  String _validatePassword(String value) {
    if (value.length == 0) {
      return 'Password is required';
    }

    if (value.length < 4) {
      return 'Incorrect password';
    }
  }

  void _sendResetEmail() {
    final resetEmailController = TextEditingController();
    resetEmail = resetEmailController.text;

    if (_resetKey.currentState.validate()) {
      _resetKey.currentState.save();

      try {
        Fluttertoast.showToast(
            msg: "Sending password-reset email to: $resetEmail",
            toastLength: Toast.LENGTH_LONG,
            bgcolor: "#e74c3c",
            textcolor: '#ffffff',
            timeInSecForIos: 4);

        _auth.sendPasswordResetEmail(email: resetEmail);
      } catch (exception) {
        print(exception);

        Fluttertoast.showToast(
            msg: "${exception.toString()}",
            toastLength: Toast.LENGTH_LONG,
            bgcolor: "#e74c3c",
            textcolor: '#ffffff',
            timeInSecForIos: 4);
      }
    }
    else {
      setState(() {
        _validate = true;
      });
    }
  }
}

推荐答案

好吧,主要有两个问题:

Well, there are mainly two issues:

  • 第一个是您需要使用对话框本地的另一个"validate"变量.否则,将其设置为true并调用setState()时,将重建整个页面,并对照validate值检查所有字段.

  • The first one is that you need to use another 'validate' variable local to the dialog. Otherwise, when you set it to true and call setState() the whole page is rebuilt and all the fields are checked against the validate value.

但是即使执行此操作,对话框中的validate也不会产生任何结果,因为调用setState()时不会重新创建Form小部件,并且不会更改更改的validate值.被注入作为参数.

But even if you do that, the validate in the dialog does not produce any result because when you call setState() the Form widget is not recreated and the changed value of validate does not get injected as a parameter.

要了解此问题,请转到

To understand this problem, please head over to this article in Medium that I wrote some time ago.

根据本文的解释,解决这两个问题的解决方案是创建一个全新的有状态小部件.因此,在调用setState()时,将重建Form并考虑validate的新值.

The solution to solve both problems, according to the explanation in the article is to create a completely new stateful widget. So when calling setState() the Form is rebuilt and the new value for validate taken into account.

这是使其工作的代码:

  // Creates an alertDialog for the user to enter their email
  Future<String> _resetDialogBox() {

    return showDialog<String>(
      context: context,
      barrierDismissible: false, // user must tap button!
      builder: (BuildContext context) {
        return CustomAlertDialog(
          title: "Reset email",
          auth: _auth,
        );
      },
    );
  }


class CustomAlertDialog extends StatefulWidget {
  final String title;
  final FirebaseAuth auth;

  const CustomAlertDialog({Key key, this.title, this.auth})
      : super(key: key);

  @override
  CustomAlertDialogState createState() {
    return new CustomAlertDialogState();
  }
}

class CustomAlertDialogState extends State<CustomAlertDialog> {

  final _resetKey = GlobalKey<FormState>();
  final _resetEmailController = TextEditingController();
  String _resetEmail;
  bool _resetValidate = false;

  StreamController<bool> rebuild = StreamController<bool>();

  bool _sendResetEmail() {
    _resetEmail = _resetEmailController.text;

    if (_resetKey.currentState.validate()) {
      _resetKey.currentState.save();

      try {
        // You could consider using async/await here
        widget.auth.sendPasswordResetEmail(email: _resetEmail);
        return true;
      } catch (exception) {
        print(exception);
      }
    } else {
      setState(() {
        _resetValidate = true;
      });
      return false;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: AlertDialog(
        title: new Text(widget.title),
        content: new SingleChildScrollView(
            child: Form(
              key: _resetKey,
              autovalidate: _resetValidate,
              child: ListBody(
                children: <Widget>[
                  new Text(
                    'Enter the Email Address associated with your account.',
                    style: TextStyle(fontSize: 14.0),
                  ),
                  Padding(
                    padding: EdgeInsets.all(10.0),
                  ),
                  Row(
                    children: <Widget>[
                      new Padding(
                        padding: EdgeInsets.only(top: 8.0),
                        child: Icon(
                          Icons.email,
                          size: 20.0,
                        ),
                      ),
                      new Expanded(
                        child: TextFormField(
                          validator: validateEmail,
                          onSaved: (String val) {
                            _resetEmail = val;
                          },
                          controller: _resetEmailController,
                          keyboardType: TextInputType.emailAddress,
                          autofocus: true,
                          decoration: new InputDecoration(
                              border: InputBorder.none,
                              hintText: 'Email',
                              contentPadding:
                              EdgeInsets.only(left: 70.0, top: 15.0),
                              hintStyle:
                              TextStyle(color: Colors.black, fontSize: 14.0)),
                          style: TextStyle(color: Colors.black),

                        ),
                      )
                    ],
                  ),
                  new Column(children: <Widget>[
                    Container(
                      decoration: new BoxDecoration(
                          border: new Border(
                              bottom: new BorderSide(
                                  width: 0.5, color: Colors.black))),
                    )
                  ]),
                ],
              ),
            ),
        ),
        actions: <Widget>[
          new FlatButton(
            child: new Text(
              'CANCEL',
              style: TextStyle(color: Colors.black),
            ),
            onPressed: () {
              Navigator.of(context).pop("");
            },
          ),
          new FlatButton(
            child: new Text(
              'SEND EMAIL',
              style: TextStyle(color: Colors.black),
            ),
            onPressed: () {
              if (_sendResetEmail()) {
                Navigator.of(context).pop(_resetEmail);
              }
            },
          ),
        ],
      ),
    );
  }
}

String validateEmail(String value) {
  String pattern =
      r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
  RegExp regExp = new RegExp(pattern);
  if (value.length == 0) {
    return "Email is required";
  } else if (!regExp.hasMatch(value)) {
    return "Invalid Email";
  } else {
    return null;
  }
}

我必须提取validateEmail()方法以使其可用于新的小部件.

I had to extract the validateEmail() method to make it available to the new widget.

这篇关于如何使用Form和GlobalKey验证alertDialog上的文本输入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆