I have a Flutter Form with many dropdownformfield and textformfield widgets, validating these is trivial using the validate: method. Validation for visual fields is obvious.
However, in many forms a non-visible element may need to be validated. For instance, if taking a picture in a Form, there will only be a button to take the picture which will input the resulting filename into a String var. I would need to validate the String var in this case and return the validation result to the button (i.e. display a "Required field" below the button), but of course the String var is not held in any formfield widget.
This being said, how can I either "wrap" the button in a widget which contains a validator: method, or how can I add a validator to the button itself AND then display the appropriate validation message to the user in the UI?
Thank you!
CodePudding user response:
You could create your own FormField
:
class TakePictureFormField extends FormField<String> {
/// Creates a [FormField] that contains an [ElevatedButton] to take a picture
/// with the phone camera.
///
/// The [String] value corresponds to the path of the picture taken.
TakePictureFormField({
Key? key,
String? initialValue,
FormFieldSetter<String>? onSaved,
FormFieldValidator<String>? validator,
bool enabled = true,
AutovalidateMode? autovalidateMode,
ButtonStyle? buttonStyle,
void Function(String)? onChanged,
}) : super(
key: key,
initialValue: initialValue,
onSaved: onSaved,
validator: validator,
enabled: enabled,
autovalidateMode: autovalidateMode,
builder: (FormFieldState<String> field) {
final currentValue = field.value;
return InputDecorator(
decoration: InputDecoration(
border: InputBorder.none,
errorText: field.errorText,
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(field.context).errorColor,
),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
style: buttonStyle,
onPressed: () async {
// Fake implementation to take a picture.
final value = await Future<String>.delayed(
const Duration(microseconds: 300),
() => 'my_path/to/image');
field.didChange(value);
if (onChanged != null) {
onChanged(value);
}
},
child: const Text('Take a Picture'),
),
if (currentValue != null) Text(currentValue),
],
),
);
},
);
}
And then use it inside a Form
like you would for any other FormField
widget:
TakePictureFormField(
validator: (val) =>
val == null || val.isEmpty ? 'Error invalid picture' : null,
)