颤振异步验证表单

时间:2018-06-24 17:22:59

标签: firebase asynchronous dart google-cloud-firestore flutter

new TextFormField(
                  validator: (value) async{
                    if (value.isEmpty) {
                      return 'Username is required.';
                    }
                    if (await checkUser()) {
                      return 'Username is already taken.';
                    }
                  },
                  controller: userNameController,
                  decoration: InputDecoration(hintText: 'Username'),
                ),

我有一个供用户使用的表单,我想检查用户在Firestore的日期数据库中是否已经存在。

Future checkUser() async {
var user = await Firestore.instance
    .collection('users')
    .document(userNameController.text)
    .get();
return user.exists;

}

这是我检查用户文档是否已存在于数据库中的功能。 但是验证器给了我这个错误。

  

[dart]无法将参数类型“(字符串)→未来”分配给参数类型“(字符串)→字符串”。

我应该如何解决此问题?

5 个答案:

答案 0 :(得分:4)

目前,我认为您无法将unshield.exeFuture关联起来。

您可以做的是通过单击按钮或以其他方式验证数据,然后在验证程序响应变量上设置状态。

validator

答案 1 :(得分:1)

这是一个进行异步表单验证的示例: async form validation with modal progress indicator

您可以在此处找到源代码: https://github.com/mmcc007/modal_progress_hud/tree/master/example

答案 2 :(得分:0)

下面的示例只是一个草图,您应该创建用于检查案例登录和状态管理的函数。 但简而言之-您可以将TextFormField中的数据传递给函数,并以任何方式同时验证它们。

import 'package:flutter/material.dart';

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

class ScratchApp extends StatefulWidget {
  @override
  _ScratchAppState createState() => _ScratchAppState();
}

class _ScratchAppState extends State<ScratchApp> {
  bool _validLogin;
  bool _isLoading;
  String _loginError;
  String _login;
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _loginInputController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          child: TextFormField(
            keyboardType: TextInputType.phone,
            validator: _validateLogin,
            onSaved: (value) => _login = value,
            onFieldSubmitted: (value) => _login = value,
            controller: _loginInputController,
          ),
        ),
        Container(
          child: RaisedButton(
            onPressed: () async {
              _formKey.currentState?.validate();
              if (_formKey.currentState.validate()) {
                setState(() {
//                  start your Circularprogress indicator
                  _isLoading = true;
                });
                _formKey.currentState.save();

//                function that checks, for example, Firebase login
                _validLogin = await verifyLogin(_login);

                if (_validLogin)
                  Navigator.pushNamed(context, goSomeWhereInYourApp);
              }
            },
            child: Text('Login'),
          ),
        ),
      ],
    );
  }

  String _validateLogin(loginVal) {
    if (loginVal.isEmpty) {
      return 'Empty login';
    } else if (loginVal.length < 3) {
      return 'Login too short';
    }
    if (!_validLogin) {
      _validLogin = true;
      setState(() {
        _isLoading = false;
      });
      return _loginError;
    }
    return null;
  }

  @override
  void dispose() {
    super.dispose();
  }
}

答案 3 :(得分:0)

我最近需要执行此操作以进行用户名验证(以检查Firebase中是否已存在用户名),这就是我如何在TextFormField上实现异步验证(不安装任何其他程序包)。我有一个“用户”集合,其中文档名称是唯一的用户名(Firebase集合中不能有重复的文档名称,但要注意区分大小写)

//In my state class
class _MyFormState extends State<MyForm> {
  final _usernameFormFieldKey = GlobalKey<FormFieldState>();

  //Create a focus node
  FocusNode _usernameFocusNode;

  //Create a controller
  final TextEditingController _usernameController = new TextEditingController();

  bool _isUsernameTaken = false;
  String _usernameErrorString;

 @override
  void initState() {
    super.initState();

    _usernameFocusNode = FocusNode();

    //set up focus node listeners
    _usernameFocusNode.addListener(_onUsernameFocusChange);

  }

  @override
  void dispose() {
    _usernameFocusNode.dispose();

    _usernameController.dispose();

    super.dispose();
  }
}

然后在我的TextFormField小部件中

  TextFormField(
      keyboardType: TextInputType.text,
      focusNode: _usernameFocusNode,
      textInputAction: TextInputAction.next,
      controller: _usernameController,
      key: _usernameFormFieldKey,
      onEditingComplete: _usernameEditingComplete,
      validator: (value) => _isUsernameTaken ? "Username already taken" : _usernameErrorString,)

在小部件上失去焦点时监听焦点变化。您也可以为“ onEditingComplete”方法做类似的事情

void _onUsernameFocusChange() {
    if (!_usernameFocusNode.hasFocus) {

      
      String message = UsernameValidator.validate(_usernameController.text.trim());

      //First make sure username is in valid format, if it is then check firebase
      if (message == null) {
        Firestore.instance.collection("my_users").document(_usernameController.text.trim()).get().then((doc) {

          if (doc.exists) {
            setState(() {
              _isUsernameTaken = true;
              _usernameErrorString = null;
            });
          } else {
            setState(() {
              _isUsernameTaken = false;
              _usernameErrorString = null;
            });
          }
          _usernameFormFieldKey.currentState.validate();
        }).catchError((onError) {
          setState(() {
            _isUsernameTaken = false;
            _usernameErrorString = "Having trouble verifying username. Please try again";
          });
          _usernameFormFieldKey.currentState.validate();
        });
      } else {
        setState(() {
          _usernameErrorString = message;
        });
        _usernameFormFieldKey.currentState.validate();
      }
    }
  }

为完整性起见,这是我的用户名验证器类

class UsernameValidator {
  static String validate(String value) {
    final regexUsername = RegExp(r"^[a-zA-Z0-9_]{3,20}$");

    String trimmedValue = value.trim();

    if (trimmedValue.isEmpty) {
      return "Username can't be empty";
    }
    if (trimmedValue.length < 3) {
      return "Username min is 3 characters";
    }

    if (!regexUsername.hasMatch(trimmedValue)) {
      return "Usernames should be a maximum of 20 characters with letters, numbers or underscores only. Thanks!";
    }

    return null;
  }
}

答案 4 :(得分:0)

在使用Firebase的实时数据库时,我遇到了同样的问题,但是我发现了一个非常不错的解决方案,类似于Zroq的解决方案。此函数创建一个简单的弹出表单,以使用户输入名称。本质上,我试图查看特定用户的特定名称是否已经在数据库中,如果为true,则显示验证错误。我创建了一个名为“ duplicate”的局部变量,只要用户单击“确定”按钮即可完成更改。然后,如果有错误,我可以再次调用验证器,验证器将显示它。

void add(BuildContext context, String email) {
String _name;
bool duplicate = false;
showDialog(
    context: context,
    builder: (_) {
      final key = GlobalKey<FormState>();
      return GestureDetector(
        onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
        child: AlertDialog(
          title: Text("Add a Workspace"),
          content: Form(
            key: key,
            child: TextFormField(
              autocorrect: true,
              autofocus: false,
              decoration: const InputDecoration(
                labelText: 'Title',
              ),
              enableInteractiveSelection: true,
              textCapitalization: TextCapitalization.sentences,
              onSaved: (value) => _name = value.trim(),
              validator: (value) {
                final validCharacters =
                    RegExp(r'^[a-zA-Z0-9]+( [a-zA-Z0-9]+)*$');
                if (!validCharacters.hasMatch(value.trim())) {
                  return 'Alphanumeric characters only.';
                } else if (duplicate) {
                  return 'Workspace already exists for this user';
                }
                return null;
              },
            ),
          ),
          actions: <Widget>[
            FlatButton(
              child: const Text("Ok"),

              onPressed: () async {
                duplicate = false;
                if (key.currentState.validate()) {
                  key.currentState.save();
                  if (await addToDatabase(_name, email) == false) {
                    duplicate = true;
                    key.currentState.validate();
                  } else {
                    Navigator.of(context).pop(true);
                  }
                }
              },
            ),
            FlatButton(
              child: const Text('Cancel'),

              onPressed: () {
                Navigator.of(context).pop(false);
              },
            ),
          ],
        ),
      );
    });
  }