Basically, I am looking for FormFieldState.isValid
but for FormState
.
So the challenge is that I want to enable/disable my form submit button on the fly depending on whether all the form fields are valid, without calling FormState.validate()
until the submit button is actually clicked because that will report errors for the yet invalid fields.
CodePudding user response:
Can you try reseach about AutovalidateMode
on FormField
? I think They will some option for this case.
/// Used to configure the auto validation of [FormField] and [Form] widgets.
enum AutovalidateMode {
/// No auto validation will occur.
disabled,
/// Used to auto-validate [Form] and [FormField] even without user interaction.
always,
/// Used to auto-validate [Form] and [FormField] only after each user
/// interaction.
onUserInteraction,
}
CodePudding user response:
There is package forme 3.2.2 3
that support the async validator
.
You can specify an asyncValidator
on FormeField.
asyncValidator:(field,value,isValid){
return Future.delayed(const Duration(seconds:2),(){
if(isUnexceptedValue(value)) {
if(isValid()){
updateUI();
}
return 'invalid';
}
return null;
});
}
For more information, you can refer to official documentation on https://pub.dev/packages/forme
CodePudding user response:
Please refer to below code
Solution 1: AutovalidateMode is enabled always
class HomeScreen extends StatelessWidget {
HomeScreen({Key key}) : super(key: key);
final TextEditingController addressController = TextEditingController();
final FocusNode addressFocus = FocusNode();
final TextEditingController stateController = TextEditingController();
final FocusNode stateFocus = FocusNode();
final _validationKey = GlobalKey<FormState>();
int validateAddress(String address) {
String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
RegExp regExp = new RegExp(patttern);
if (address.isEmpty || address.length == 0) {
return 1;
} else if (address.length < 10) {
return 3;
} else {
return 0;
}
}
int validateState(String state) {
String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
RegExp regExp = new RegExp(patttern);
if (state.isEmpty || state.length == 0) {
return 1;
} else {
return 0;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlue,
automaticallyImplyLeading: true,
leading: Icon(
Icons.arrow_back,
),
title: Text("Example"),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Form(
key: _validationKey,
child: Column(
children: [
/* State */
TextFormField(
autovalidateMode: AutovalidateMode.always,
/* autovalidate is set to true */
controller: stateController,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
FilteringTextInputFormatter.deny(RegExp(
r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
],
keyboardType: TextInputType.text,
maxLength: 160,
onChanged: (val) {},
maxLines: 1,
validator: (value) {
int res = validateAddress(value);
if (res == 1) {
return "Please enter state";
} else {
return null;
}
},
focusNode: stateFocus,
autofocus: false,
decoration: InputDecoration(
errorMaxLines: 3,
counterText: "",
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
)),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
),
),
hintText: "Enter state" ?? "",
),
),
SizedBox(
height: 15.0,
),
/* Address */
TextFormField(
autovalidateMode: AutovalidateMode.always,
/* autovalidate is enabled */
controller: addressController,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
FilteringTextInputFormatter.deny(RegExp(
r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
],
keyboardType: TextInputType.text,
maxLength: 60,
onChanged: (val) {},
maxLines: 1,
validator: (value) {
int res = validateAddress(value);
if (res == 1) {
return "Please enter address";
} else if (res == 3) {
return "Please enter minimum 10 characters";
} else {
return null;
}
},
focusNode: addressFocus,
autofocus: false,
decoration: InputDecoration(
errorMaxLines: 3,
counterText: "",
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
)),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
),
),
hintText: "Enter address" ?? "",
),
),
],
),
),
SizedBox(
height: 15.0,
),
],
),
),
);
}
Solution 2: Validation is enabled only after user interaction
class MainScreen extends StatelessWidget {
MainScreen({Key key}) : super(key: key);
final TextEditingController addressController = TextEditingController();
final FocusNode addressFocus = FocusNode();
final TextEditingController stateController = TextEditingController();
final FocusNode stateFocus = FocusNode();
final _validationKey = GlobalKey<FormState>();
int validateAddress(String address) {
String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
RegExp regExp = new RegExp(patttern);
if (address.isEmpty || address.length == 0) {
return 1;
} else if (address.length < 10) {
return 3;
} else {
return 0;
}
}
int validateState(String state) {
String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
RegExp regExp = new RegExp(patttern);
if (state.isEmpty || state.length == 0) {
return 1;
} else {
return 0;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlue,
automaticallyImplyLeading: true,
leading: Icon(
Icons.arrow_back,
),
title: Text("Example"),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Form(
key: _validationKey,
child: Column(
children: [
/* State */
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
/* autovalidate is disabled */
controller: stateController,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
FilteringTextInputFormatter.deny(RegExp(
r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
],
keyboardType: TextInputType.text,
maxLength: 160,
onChanged: (val) {},
maxLines: 1,
validator: (value) {
int res = validateAddress(value);
if (res == 1) {
return "Please enter state";
} else {
return null;
}
},
focusNode: stateFocus,
autofocus: false,
decoration: InputDecoration(
errorMaxLines: 3,
counterText: "",
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
)),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
),
),
hintText: "Enter state" ?? "",
),
),
SizedBox(
height: 15.0,
),
/* Address */
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
/* autovalidate is disabled */
controller: addressController,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
FilteringTextInputFormatter.deny(RegExp(
r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
],
keyboardType: TextInputType.text,
maxLength: 60,
onChanged: (val) {},
maxLines: 1,
validator: (value) {
int res = validateAddress(value);
if (res == 1) {
return "Please enter address";
} else if (res == 3) {
return "Please enter minimum 10 characters";
} else {
return null;
}
},
focusNode: addressFocus,
autofocus: false,
decoration: InputDecoration(
errorMaxLines: 3,
counterText: "",
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
)),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
),
),
hintText: "Enter address" ?? "",
),
),
],
),
),
SizedBox(
height: 15.0,
),
OutlinedButton(
onPressed: () {
_validationKey.currentState.validate();
if (stateController.text.isEmpty) {
stateFocus.requestFocus();
} else if (addressController.text.isEmpty ||
addressController.text.length < 10) {
addressFocus.requestFocus();
}
},
child: Text("Validate"),
)
],
),
),
);
}
}
Solution 3: Autovalidate can also be used but currently its deprecated
class MainScreen extends StatelessWidget {
MainScreen({Key key}) : super(key: key);
final TextEditingController addressController = TextEditingController();
final FocusNode addressFocus = FocusNode();
int validateAddress(String address) {
String patttern = r'(^[a-zA-Z0-9 ,.-]*$)';
RegExp regExp = new RegExp(patttern);
if (address.isEmpty || address.length == 0) {
return 1;
} else if (address.length < 10) {
return 3;
} else {
return 0;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlue,
automaticallyImplyLeading: true,
leading: Icon(
Icons.arrow_back,
),
title: Text("Example"),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.all(15.0),
child: Center(
child: TextFormField(
autovalidate: true,
controller: addressController,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r"\s\s")),
FilteringTextInputFormatter.deny(RegExp(
r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])')),
],
keyboardType: TextInputType.text,
maxLength: 60,
onChanged: (val) {},
maxLines: 1,
validator: (value) {
int res = validateAddress(value);
if (res == 1) {
return "Please enter address";
} else if (res == 3) {
return "Please enter minimum 10 characters";
} else {
return null;
}
},
focusNode: addressFocus,
autofocus: false,
decoration: InputDecoration(
errorMaxLines: 3,
counterText: "",
filled: true,
fillColor: Colors.white,
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Color(0xffE5E5E5),
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
)),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: Colors.red,
),
),
hintText: "Hint Text" ?? "",
),
),
)),
);
}
}