In flutter, a TextFormField for an example, has a validator which can be used for validation:
TextFormField(validator: validator ...
Then in your form, you can call validate on the Form widget to make all children with these validators validate:
_formKey.currentState!.validate()
If a validation fails, the TextFormField will display an error text along with a position and color transition.
I have my own custom photo widget, and I would like to make it able to support the same validation functionality. That is, give it a validator, hook it up to the validate()
event, and if the user hasn´t added any photo, the validation fails and shows the error text the validator returns.
But I cannot figure out how to implement the validate eventlistener on a custom widget. So how would you go around this?
@user18309290 pointed me in the direction of extending my widget from FormField. But the problem is that my widget has internal functions and properties I need to access in the instance/layout tree. But I can´t figure out the right way to do it. I could put all the stuff in the build method, but that means that all of my "heavy" logic and properties would be reinstantiated every time the widget rebuilds if I understand correctly. So how do I extend from FormField to have validation support (validation fails if image list is empty), but still have access to my methods and properties?
This is my simplified widget:
class MyPhotoComponent extends FormField<List<File>> {
late String title;
List<File> images = [];
openCamera() {
print('This and other methods, has alot of logic');
images.add(File('filepath'));
}
String internalTitle = 'Internal title';
MyPhotoComponent({required String title, required FormFieldSetter<List<File>> onSaved, FormFieldValidator<List<File>>? validator, required List<File> initialValue, Key? key})
: super(
onSaved: onSaved,
validator: validator,
initialValue: initialValue,
key: key,
builder: (FormFieldState<List<File>> state) {
return Column(
children: [
Builder(builder: (BuildContext context) {
return Column(
children: [
Text(internalTitle), //Error: The instance member 'internalTitle' can't be accessed in an initializer.
MyOtherPhotoGalleryComponent(images: images), //Error: The instance member 'images' can't be accessed in an initializer.
ElevatedButton.icon(
onPressed: openCamera, //Error: The instance member 'openCamera' can't be accessed in an initializer.
icon: Icon(Icons.add_a_photo),
label: Text('Take photo'),
),
],
);
}),
if (state.hasError) Builder(builder: (BuildContext context) => Text('Validation error'))
],
);
},
);
}
Inherit a custom widget from FormField. Each individual form field should be wrapped in a FormField widget like TextFormField.
Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
CustomFormField(
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Please select something';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {}
},
child: const Text('Submit'),
),
],
),
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With