Home > Software design >  How to disable a button until all form fields are valid
How to disable a button until all form fields are valid

Time:04-07

I have the following StatefulWidget:

class ProfileScreen extends StatefulWidget {
  const ProfileScreen({Key? key}) : super(key: key);

  @override
  State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  bool formIsValid = false;
  String fullName = '';
  String email = '';

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Form(
        key: _formKey,
        onChanged: () =>
          setState(() => formIsValid = _formKey.currentState!.validate()),
        child: Container(
          children: [
            TextFormField(
              textAlign: TextAlign.left,
              onChanged: (value) {
                setState(() {
                    fullName = value;
                });
              },
              validator: (String? value) {
                return (value == null || value.isEmpty)
                  ? 'Please enter a name'
                  : null;
              },
            ),
            TextFormField(
              textAlign: TextAlign.left,
              onChanged: (value) {
                setState(() {
                    email = value;
                });
              },
              validator: 
              validator: (String? value) {
                return (value == null || value.isEmpty)
                  ? 'Please enter an email address'
                  : null;
              },
            ),
            SizedBox(
              height: 38,
              width: 126,
              child: ElevatedButton(
                onPressed: formIsValid,
                style: ElevatedButton.styleFrom(
                  primary: Colors.blue,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(3),
                  ),
                ),
                child: Text(
                  'UPDATE',
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 14.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

It works in that the "UPDATE" button is disabled until both fields are valid. However, the TextFormField errors appear immediately after entering a single character in any field.

How do I only enable the button when all fields are valid without prematurely displaying errors?

NOTE: I don't want to use the reactive_forms package.

CodePudding user response:

you could try checking validation this way

class ProfileScreen extends StatefulWidget {
  const ProfileScreen({Key? key}) : super(key: key);

  @override
  State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final _fullName = TextEditingController();
  final _email = TextEditingController();
  bool _validate = false;
  bool formIsValid = false;

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Form(
        key: _formKey,
        onChanged: () =>
            setState(() => formIsValid = _formKey.currentState!.validate()),
        child: Column(
          children: [
            TextFormField(
              controller: _fullName,
              textAlign: TextAlign.left,
              decoration: InputDecoration(
                hintText: 'Name',
                errorText: _validate ? 'Please enter a name' : null, //here
              ),
            ),
            TextFormField(
              controller: _email,
              textAlign: TextAlign.left,
              decoration: InputDecoration(
                hintText: 'Email',
                errorText:
                    _validate ? 'Please enter an email address' : null, //here
              ),
            ),
            SizedBox(
              height: 38,
              width: 126,
              child: ElevatedButton(
                onPressed: () {
                  setState(() {
                    if (_fullName.text.isEmpty || _email.text.isEmpty) {
                      _validate = true;
                      print('Not Updated');
                    } else {
                      _validate = false;
                      print('Information Updated');
                    }
                  });
                },
                style: ElevatedButton.styleFrom(
                  primary: Colors.blue,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(3),
                  ),
                ),
                child: const Text(
                  'UPDATE',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 14.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

CodePudding user response:

You can create a function that will check and validate all the TextField and then return a bool according to the validation. Put that function in Button to enable or disable the button as the validation goes.

Function:

bool isOk() {
      if (emailId.isNotEmpty) {
        if(pass.isNotEmpty){
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
  }

Button:

ElevatedButton(
    onPressed: isOk() ? nextClick : null,
    child: const Text('Button Text'),
),

CodePudding user response:

You can use a validation function like

bool isValid(){
   return fullName!=''&&email != ''
}

Then in your button code use

child: ElevatedButton(
            onPressed: isValid()?formIsValid:null,
            style: ElevatedButton.styleFrom(
              primary: Colors.blue,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(3),
              ),
            ),
            child: Text(
              'UPDATE',
              style: const TextStyle(
                color: Colors.white,
                fontSize: 14.0,
                fontWeight: FontWeight.bold,
              ),
            ),
          ),

And you can also specify the button style based on the isValid() bool value

  • Related