So I managed to solved this.
My problem should actual be break down into 3 problems.
- Validate textformfield, radio buttons, all custom inputs.
- Create a new model class that can be passed translate to JSON.
- Submit the form from a button within another widget (Appbar).
In order to solve the first one, I wrapped everything in SurveyForm into a Form widget. This part is simple, there are many tutorials online. In order to do validation on custom inputs, I wrapped the input in a FormField
widget, and trigger the didChange
method of FormFieldState
. This allows me to use validator
and onSaved
method of FormField
using methods callback
. Below is my working code for a datepicker.
class Q1<T> extends StatefulWidget {
final FormFieldValidator<DateTime> validator;
final FormFieldSetter<DateTime> onSaved;
const Q1({
Key key,
@required this.onSaved,
@required this.validator,
}) : assert(validator != null),
assert(onSaved != null),
super(
key: key,
);
@override
_Q1State createState() => _Q1State();
}
class _Q1State extends State<Q1> {
DateTime _completionDate;
@override
Widget build(BuildContext context) {
return FormField(
validator: (val) {
return widget.validator(val);
},
onSaved: (val) {
widget.onSaved(val);
},
builder: (FormFieldState<dynamic> field) {
return Row(
children: [
Expanded(
flex: 1,
child: Text('1. Completion Date'),
),
Expanded(
flex: 2,
child: Row(
children: [
Container(
padding: EdgeInsets.all(
SizeConfig().getProportionateScreenWidth(
kDatePickerIconTextBoxPadding),
),
decoration: BoxDecoration(
border: Border.all(
color: kDatePickerIconTextBoxBorderColor,
),
borderRadius: BorderRadius.all(Radius.circular(
SizeConfig().getProportionateScreenWidth(
kDatePickerIconTextBoxRadius),
) // <--- border radius here
),
),
child: Text(_completionDate == null
? 'Pick a date'
: DateFormat(kDateFormat).format(_completionDate)),
),
IconButton(
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
).then((date) {
setState(() {
field.didChange(date);
_completionDate = date;
});
});
},
icon: FaIcon(FontAwesomeIcons.calendarAlt)),
],
),
),
],
);
},
);
}
}
And I just called the widget with
Q1(
validator: (val) {
if (val == null) {
return 'Completion Date is missing';
}
return null;
},
onSaved: (value) {
setState(() {
widget.questionnaire.completion_date = DateFormat(kDateFormat).format(value);
});
},
),
In order to solve the 2nd one. I used json_serializable
, where the object can also be passed around between HomeApp and SurveyForm widget, so I don't need to pass around 50+ variables.
And for the 3rd problem, I initiated final _formKey = GlobalKey<FormState>();
in the HomeApp, and passed it down to the SurveyForm widget, and use onPressed
callback in the Appbar to trigger validation/submit in the SurveyForm.
Bloc will be used for submitting JSON and load animation, but that's beyond this.
I'm new to flutter (Been using this for a month), please let me know if anyone have a better solution.