Update TextFormField's error outside of the validator Update TextFormField's error outside of the validator dart dart

Update TextFormField's error outside of the validator


You can use flutter_form_bloc.

Each field have the method addError and you can call anywhere, in your case it would be in the onSubmitting method after receiving the response from the server.

class MyFormBloc extends FormBloc<String, String> {  final email = TextFieldBloc();  MyFormBloc() {    addFieldBlocs(fieldBlocs: [email]);  }  @override  void onSubmitting() async {   // Awesome logic...   username.addError('That email is taken. Try another.');  }}

You can also have asynchronous validators with debounce time

class MyFormBloc extends FormBloc<String, String> {  final username = TextFieldBloc(    asyncValidatorDebounceTime: Duration(milliseconds: 300),  );  MyFormBloc() {    addFieldBlocs(fieldBlocs: [username]);    username.addAsyncValidators([_checkUsername]);  }  Future<String> _checkUsername(String username) async {    await Future.delayed(Duration(milliseconds: 500));    if (username.toLowerCase() != 'flutter dev') {      return 'That username is already taken';    }    return null;  }}

Here is a small demo that you can run and the tutorial is in the form bloc website

pubspec.yaml

dependencies:  flutter_form_bloc: ^0.11.0

main.dart

import 'package:flutter/material.dart';import 'package:flutter_form_bloc/flutter_form_bloc.dart';void main() => runApp(App());class App extends StatelessWidget {  const App({Key key}) : super(key: key);  @override  Widget build(BuildContext context) {    return MaterialApp(      debugShowCheckedModeBanner: false,      home: SubmissionErrorToFieldForm(),    );  }}class SubmissionErrorToFieldFormBloc extends FormBloc<String, String> {  final username = TextFieldBloc();  SubmissionErrorToFieldFormBloc() {    addFieldBlocs(      fieldBlocs: [        username,      ],    );  }  @override  void onSubmitting() async {    print(username.value);    await Future<void>.delayed(Duration(milliseconds: 500));    if (username.value.toLowerCase() == 'dev') {      username.addError(        'Cached - That username is taken. Try another.',        isPermanent: true,      );      emitFailure(failureResponse: 'Cached error was added to username field.');    } else {      username.addError('That username is taken. Try another.');      emitFailure(failureResponse: 'Error was added to username field.');    }  }}class SubmissionErrorToFieldForm extends StatelessWidget {  @override  Widget build(BuildContext context) {    return BlocProvider(      create: (context) => SubmissionErrorToFieldFormBloc(),      child: Builder(        builder: (context) {          final formBloc =              BlocProvider.of<SubmissionErrorToFieldFormBloc>(context);          return Theme(            data: Theme.of(context).copyWith(              inputDecorationTheme: InputDecorationTheme(                border: OutlineInputBorder(                  borderRadius: BorderRadius.circular(20),                ),              ),            ),            child: Scaffold(              appBar: AppBar(title: Text('Submission Error to Field')),              body: FormBlocListener<SubmissionErrorToFieldFormBloc, String,                  String>(                onSubmitting: (context, state) {                  LoadingDialog.show(context);                },                onSuccess: (context, state) {                  LoadingDialog.hide(context);                  Navigator.of(context).pushReplacement(                      MaterialPageRoute(builder: (_) => SuccessScreen()));                },                onFailure: (context, state) {                  LoadingDialog.hide(context);                  Scaffold.of(context).showSnackBar(                      SnackBar(content: Text(state.failureResponse)));                },                child: SingleChildScrollView(                  physics: ClampingScrollPhysics(),                  child: Padding(                    padding: const EdgeInsets.all(8.0),                    child: Column(                      children: <Widget>[                        TextFieldBlocBuilder(                          textFieldBloc: formBloc.username,                          keyboardType: TextInputType.multiline,                          decoration: InputDecoration(                            labelText: 'Username',                            prefixIcon: Icon(Icons.sentiment_very_satisfied),                          ),                        ),                        Padding(                          padding: const EdgeInsets.all(8.0),                          child: Text('"dev" will add a cached error'),                        ),                        RaisedButton(                          onPressed: formBloc.submit,                          child: Text('SUBMIT'),                        ),                      ],                    ),                  ),                ),              ),            ),          );        },      ),    );  }}class LoadingDialog extends StatelessWidget {  static void show(BuildContext context, {Key key}) => showDialog<void>(        context: context,        useRootNavigator: false,        barrierDismissible: false,        builder: (_) => LoadingDialog(key: key),      ).then((_) => FocusScope.of(context).requestFocus(FocusNode()));  static void hide(BuildContext context) => Navigator.pop(context);  LoadingDialog({Key key}) : super(key: key);  @override  Widget build(BuildContext context) {    return WillPopScope(      onWillPop: () async => false,      child: Center(        child: Card(          child: Container(            width: 80,            height: 80,            padding: EdgeInsets.all(12.0),            child: CircularProgressIndicator(),          ),        ),      ),    );  }}class SuccessScreen extends StatelessWidget {  SuccessScreen({Key key}) : super(key: key);  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: <Widget>[            Icon(Icons.tag_faces, size: 100),            SizedBox(height: 10),            Text(              'Success',              style: TextStyle(fontSize: 54, color: Colors.black),              textAlign: TextAlign.center,            ),            SizedBox(height: 10),            RaisedButton.icon(              onPressed: () => Navigator.of(context).pushReplacement(                  MaterialPageRoute(                      builder: (_) => SubmissionErrorToFieldForm())),              icon: Icon(Icons.replay),              label: Text('AGAIN'),            ),          ],        ),      ),    );  }}


Add a bool check that you would change depending on the server call

TextFormField(               validator: (value) {                   if (hasErrorAfterServerCall)                          return 'Your Error Message';                   else                          return null;                },);

After the server call is done you can validate the form again

_formKey.currentState!.validate();


Why don't you add server call inside a validator function.Use a validator inside a TextFormField as given below,

TextFormField(                 validator: _validateEmail,                  onSaved: (String value) {                    email = value;                  },                ),  String _validateEmail(String value) async {//call to a server inside a validator function        dynamic response = await someServerCall();         String  _token="";        if (response.token) {            // Valid, use token           setState((){       _token = response.token            });            return null;        } else {            // INVALID, update error text somehow            return "error";        }  }

if validator gets null then it will not show any error, but if it gets any string then it will display that string as an error.Now for the button

RaisedButtton(    child: Text('SUBMIT'),    onPressed: (){       if (_formKey.currentState.validate()){} _formKey.currentState.save();})