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'),
                ),
I have a form for user, and I want to check if the user already exists in the firestore datebase.
Future checkUser() async {
var user = await Firestore.instance
    .collection('users')
    .document(userNameController.text)
    .get();
return user.exists;
}
This is my function to check if the user document already exists in the database. But validator gives me this error.
[dart] The argument type '(String) → Future' can't be assigned to the parameter type '(String) → String'.
How should I fix this issue?
At this time I think that you can't associate a Future to a validator.
What you can do is this verifying the data on a button click or in another way and set the state on the validator response var.
 @override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
    body: Form(
        key: _formKey,
        child: Column(children: [
          new TextFormField(
              validator: (value) {
                return usernameValidator;
              },
              decoration: InputDecoration(hintText: 'Username')),
          RaisedButton(
            onPressed: () async {
              var response = await checkUser();
              setState(() {
                this.usernameValidator = response;
              });
              if (_formKey.currentState.validate()) {}
            },
            child: Text('Submit'),
          )
        ])));
}
                        I needed to do this for username validation recently (to check if a username already exists in firebase) and this is how I achieved async validation on a TextFormField ( without installation of any additional packages). I have a "users" collection where the document name is the unique username ( Firebase can't have duplicate document names in a collection but watch out for case sensitivity)
//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();
  }
}
Then in my TextFormField widget
  TextFormField(
      keyboardType: TextInputType.text,
      focusNode: _usernameFocusNode,
      textInputAction: TextInputAction.next,
      controller: _usernameController,
      key: _usernameFormFieldKey,
      onEditingComplete: _usernameEditingComplete,
      validator: (value) => _isUsernameTaken ? "Username already taken" : _usernameErrorString,)
Listen for focus changes on the widget i.e when it loses focus. You can also do something similar for "onEditingComplete" method
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();
      }
    }
  }
For completeness, this is my username validator class
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;
  }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With