Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set error state manually? #123

Closed
wsakka opened this issue Sep 2, 2019 · 11 comments
Closed

How to set error state manually? #123

wsakka opened this issue Sep 2, 2019 · 11 comments
Labels
enhancement New feature or request good first issue Good for newcomers
Milestone

Comments

@wsakka
Copy link

wsakka commented Sep 2, 2019

Greetings,

Was building my own builder library but saw this one so much better! Anyway, assuming I'm using a form, and I get a response from the server (This email address is already in use) for instance, how to display that error message?

Thanks,

@danvick danvick added enhancement New feature or request good first issue Good for newcomers labels Sep 11, 2019
@danvick
Copy link
Collaborator

danvick commented Sep 11, 2019

HI @wsakka,
I'm afraid Flutter doesn't currently offer an easy way to do this. I am however looking for a way to solve this in the next version of the package

@wsakka
Copy link
Author

wsakka commented Sep 14, 2019

Hi @danvick, thanks for your update.

I might have misstated the question -- how do I set the errorText for a FormBuilderTextField for instance?

@wsakka
Copy link
Author

wsakka commented Sep 14, 2019

So there's 2 mechanisms that would be of interest, one is to pass an error state for individual fields, such as:

_fbKey.currentState.setError("email", "This email address is already in use");

or passing a map of values:
mapErrors["email"] = "This email address is already in use"; _fbKey.currentState.setErrors(mapErrors);

@akulubala
Copy link

This future is very useful, any updates?

@akulubala
Copy link

For now, there is an alternative way to do this, hope this could help someone.

use custom validate method :

(dynamic _) {
       if (errors.isNotEmpty) {
              return errors['errors']['product'][0];
        }
        return null;
}

when errors from backend api put errors to a ChangeNotifier.

class HttpClientErrors extends ChangeNotifier {
  Map<String, dynamic> _errors = <String, dynamic>{};

  Map<String, dynamic> get errors => _errors;

  void setState(Map<String, dynamic> errors) {
    _errors = errors;
    notifyListeners();
  }
}

then just listening changes and validate form again.

final Map<String, dynamic> errors =
        Provider.of<HttpClientErrors>(context).errors;
    if (fbKey.currentState != null) {
      fbKey.currentState.validate();
    }

@danvick
Copy link
Collaborator

danvick commented Jul 21, 2020

https://pub.dev/packages/flutter_form_builder/versions/4.0.0-alpha.6#programmatically-inducing-an-error

@RodolfoSilva
Copy link
Contributor

LGTM! Thanks @danvick 😁

@fajusto
Copy link

fajusto commented Oct 12, 2020

If you want to show below the TextField, there’s an InputDecoration attribute called errorText. You just need to get your message and set there.

@cedricDevWP
Copy link
Contributor

Hello I am having a problem for asynchronous control.

In my view I have a login form with two inputs based on the same error variable :

FormBuilder(
        key: _con.formKey,
        child: Column(
          children: [
            CInput(
              errorText: _con.inputError,
              name: _con.inputEmail,
              type: TextInputType.emailAddress,
              hint: "Ex : ...",
              label: "E-mail",
              isRequired: true,
            ),
            CInput(
              errorText: _con.inputError,
              name: _con.inputPassword,
              type: TextInputType.text,
              label: "Mot de passe",
              isRequired: true,
              isObscure: true,
            ),
          ],
        ),
      ),

I have a submit button to reset the value of the error input on each click :

CButton(
        backgroundColor: _con.hasErrorApi ? CTheme.cWhite : CTheme.cGreen,
        borderColor: _con.hasErrorApi ? CTheme.cOrange : CTheme.cGreen,
        content: CText(
          val: "Se connecter",
          color: _con.hasErrorApi ? CTheme.cOrange : CTheme.cWhite,
          size: 19,
        ),
        onPressed: () async {
          FocusScope.of(context).requestFocus(FocusNode());
          setState(() {
            _con.changeStateLoader();
            _con.removeError();
          });

          await submitForm();

          setState(() {
            _con.changeStateLoader();
          });

          if (_con.user != null) {
            Navigator.of(context).pushReplacementNamed('/home');
          }
        },
      )

More precisely the removeError function:

removeError() {
    _inputError = null;
 }

More precisely the submitForm function:

Future<void> submitForm() async {
    print(_formKey.currentState.validate());
    _formKey.currentState.save();

    if (_formKey.currentState.validate()) {

      Map<String, dynamic> formValues = _formKey.currentState.value;
      String email = formValues[inputEmail];
      String password = formValues[inputPassword];

      Response apiResponse = await api().authentication(email, password);

      Map apiResponseBody = json.decode(apiResponse.body);

      if (apiResponse.statusCode != 200) {
        this._hasErrorApi = true;
        this._messageError = apiResponseBody['message'];
        this._inputError = '';
      } else {
        if (Config.userCanLogin(apiResponseBody['data']['roles'])) {
          List<String> roles = apiResponseBody['data']['roles'];

          if (roles.contains(Config.roleOwner)) {
            print(Owner.fromJsonMin(apiResponseBody['data']).toString());
            globals.currentUser = Owner.fromJsonMin(apiResponseBody['data']);
          }
        }

        this._hasErrorApi = true;
        this._messageError = "Vous ne disposez pas du rôle adéquat pour utiliser l'application";
        this._inputError = '';
      }
    }
  }

But when i set inputError to null the function _formKey.currentState.validate() return false...

have you ever met cares ?

@danvick @fajusto

@cedricDevWP
Copy link
Contributor

In the doc : https://pub.dev/packages/flutter_form_builder/versions/4.0.0-pre.6

i try this :

RaisedButton(
  child: Text('Submit'),
  onPressed: () async {
    setState(() => _emailError = null);
    if(checkIfEmailExists()){
      setState(() => _emailError = 'Email already taken.');
    }
  },
),

but if we print : formKey.currentState.saveAndValidate() it's not sync, when

_emailError == null : formKey.currentState.saveAndValidate() == false
_emailError == "xxx" : formKey.currentState.saveAndValidate() == true

@danvick
Copy link
Collaborator

danvick commented Dec 21, 2020

You may have to await the response of

In the doc : https://pub.dev/packages/flutter_form_builder/versions/4.0.0-pre.6

i try this :

RaisedButton(
  child: Text('Submit'),
  onPressed: () async {
    setState(() => _emailError = null);
    if(checkIfEmailExists()){
      setState(() => _emailError = 'Email already taken.');
    }
  },
),

but if we print : formKey.currentState.saveAndValidate() it's not sync, when

_emailError == null : formKey.currentState.saveAndValidate() == false
_emailError == "xxx" : formKey.currentState.saveAndValidate() == true

You may have to await the response from checkIfEmailExists() before checking form validity. Like so:

RaisedButton(
  child: Text('Submit'),
  onPressed: () async {
    setState(() => _emailError = null);
    if( await checkIfEmailExists()){
      setState(() => _emailError = 'Email already taken.');
    }
    if(formKey.currentState.saveAndValidate()){
        // Do stuff
    }
  },
),

@danvick danvick closed this as completed Dec 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

6 participants