I am trying to validate my form on login button press. It works perfectly fine on client side when all fields are empty however with my switch/case it does not appear to work. I believe it may be related to my validation function but I cannot pinpoint it. I believe the booleans are being set correctly in MySOAPRequest since when I press the login button then update the company or pin field the error message shows on the username field but it should show on login button press.
Login page with text fields and login button
CustomTextField(
key: _customTextFieldKey,
companyController: _companyController,
usernameController: _usernameController,
pinController: _pinController,
),
const SizedBox(height: 20),
OutlinedButton(
onPressed: () {
FocusManager.instance.primaryFocus?.unfocus();
String lcomp = Globals().lcomp =
_companyController.text.trim();
String luser = Globals().luser =
_usernameController.text.trim();
String lpin =
Globals().lpin = _pinController.text.trim();
if (lcomp.isNotEmpty &&
luser.isNotEmpty &&
lpin.isNotEmpty &&
!_customTextFieldKey.currentState!
.hasError()) {
MySOAPRequest(
lcomp: lcomp,
luser: luser,
lpin: lpin,
onLoginSuccess: _saveCredentials,
).makeSOAPRequest(context);
} else {
_customTextFieldKey.currentState?.validate();
}
},
child: const Text('Login'),
),
MySOAPRequest page code
final String lcomp;
final String luser;
final String lpin;
final Function(bool) onLoginSuccess; // define the onLoginSuccess parameter
MySOAPRequest({
required this.lcomp,
required this.luser,
required this.lpin,
required this.onLoginSuccess,
});
switch (logonState) {
case '1':
// Navigate to the Landing page if the logon state is 1
// ignore: use_build_context_synchronously
loginFlushbar(context);
// ignore: use_build_context_synchronously
navigateToSubPages(context);
onLoginSuccess(true);
break;
case '3':
// ignore: use_build_context_synchronously
flushbar(context, 'Incorrect User Name',
'Please try again or contact your administrator if this issue persists.');
onLoginSuccess(false);
break;
default:
// ignore: use_build_context_synchronously
flushbar(context, 'Unknown Error',
'An unknown error occurred. Please try again or contact your administrator.');
Globals().usernameHasError = true;
Globals().errorText = 'Incorrect User Name';
Globals().customTextFieldKey.currentState?.validate();
onLoginSuccess(false);
break;
}
Username field text
class CustomTextFieldState extends State<CustomTextField> {
final _formKey = GlobalKey<FormState>();
//final _usernameKey = Globals().customTextFieldKey;
String _company = '';
String _username = '';
String _pin = '';
bool _companyHasError = false;
bool _usernameHasError = false;
bool _pinHasError = false;
bool hasError() {
return _usernameHasError || _companyHasError || _pinHasError;
}
void validate() {
setState(() {
if (_formKey.currentState != null) {
_companyHasError =
!_formKey.currentState!.validate() || _company.isEmpty;
_usernameHasError =
!_formKey.currentState!.validate() || _username.isEmpty;
_pinHasError = !_formKey.currentState!.validate() || _pin.isEmpty;
}
});
}
@override
Widget build(BuildContext context) {
String lcomp = widget.companyController.text.trim();
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
//!--- COMPANY TEXT FIELD --- //
child: TextFormField(
controller: widget.companyController,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(left: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: ThemeClass.primaryColor,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Color(0x00232323)),
),
suffixIcon: const Icon(Icons.corporate_fare_outlined,
color: ThemeClass.primaryColor),
labelText: "Company Name",
labelStyle: const TextStyle(
fontSize: 14.0,
decoration: TextDecoration.none,
),
hintText: 'Enter Company Name',
hintStyle: const TextStyle(
fontSize: 14.0,
),
errorText: _companyHasError
? 'Please enter a valid company name!'
: ((_company.isNotEmpty &&
!Globals().resultList.contains(lcomp))
? 'Please enter a valid company name!'
: null),
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your company name!';
}
return null;
},
onChanged: (value) {
setState(() {
_company = value;
_companyHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_companyHasError = !_formKey.currentState!.validate();
});
},
),
),
//!--- END COMPANY TEXT FIELD --- //
//!--- USERNAME TEXT FIELD --- //
TextFormField(
controller: widget.usernameController,
//key: _usernameKey,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(left: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: ThemeClass.primaryColor,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Color(0x00232323)),
),
suffixIcon: const Icon(Icons.person_outlined,
color: ThemeClass.primaryColor),
labelText: "Username",
labelStyle: const TextStyle(
fontSize: 14.0,
decoration: TextDecoration.none,
),
hintText: 'Enter username',
hintStyle: const TextStyle(
fontSize: 14.0,
),
errorText: _usernameHasError
? 'Please enter a valid username!'
: Globals().usernameHasError
? Globals().errorText
: null,
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your username!';
}
return null;
},
onChanged: (value) {
setState(() {
_username = value;
_usernameHasError = false;
Globals().usernameHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_usernameHasError = !_formKey.currentState!.validate();
});
},
),
//!--- END USERNAME TEXT FIELD --- //
const SizedBox(height: 20),
//!--- PIN TEXT FIELD --- //
TextFormField(
controller: widget.pinController,
inputFormatters: [
LengthLimitingTextInputFormatter(6),
FilteringTextInputFormatter.digitsOnly
],
obscureText: true,
keyboardType: TextInputType.number,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(left: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: ThemeClass.primaryColor,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(
color: Color(0xff185C91),
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Colors.red),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: const BorderSide(color: Color(0x00232323)),
),
suffixIcon: const Icon(Icons.pin_outlined,
color: ThemeClass.primaryColor),
labelText: "PIN",
labelStyle: const TextStyle(
fontSize: 14.0,
decoration: TextDecoration.none,
),
hintText: 'Enter PIN',
hintStyle: const TextStyle(
fontSize: 14.0,
),
errorText: _pinHasError
? 'Please enter a valid PIN!'
: _pin.length < 4 && _pin.isNotEmpty
? 'PIN is too short!'
: null,
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your PIN!';
}
return null;
},
onChanged: (value) {
setState(() {
_pin = value;
_pinHasError = false;
});
},
onFieldSubmitted: (value) {
setState(() {
_pinHasError = !_formKey.currentState!.validate();
});
},
),
//!--- END PIN TEXT FIELD --- //
],
),
);
}
}
2
Answers
i just saw something bad you are doing on the switch case,
there is a reason why you should not call the build context asynchronously, it may have been unmounted since you passed it aroud
if you are using a stateful widget, which appears to be so, you can define your context dependendant functions outside the build widget like this
Wrap your
TextFormField
in aForm
widget and provide aGlobalKey<FormState>
to theForm
as its key. Then you can validate it with_formKey.currentState!.validate()
.