Skip to content

Rethink Forms. #6569

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 95 additions & 78 deletions examples/flutter_gallery/lib/demo/expansion_panels_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,23 +189,25 @@ class _ExpansionPanelsDemoState extends State<ExpasionPanelsDemo> {
});
}

return new CollapsibleBody(
margin: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Form(
child: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Input(
hintText: item.hint,
labelText: item.name,
value: new InputValue(text: item.value),
formField: new FormField<String>(
setter: (String val) { item.value = val; }
return new Form(
child: new Builder(
builder: (BuildContext context) {
return new CollapsibleBody(
margin: const EdgeInsets.symmetric(horizontal: 16.0),
onSave: () { Form.of(context).save(); close(); },
onCancel: () { Form.of(context).reset(); close(); },
child: new Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new InputFormField(
hintText: item.hint,
labelText: item.name,
initialValue: new InputValue(text: item.value),
onSaved: (InputValue val) { item.value = val.text; },
),
),
),
),
),
onSave: close,
onCancel: close
);
}
)
);
}
),
Expand All @@ -221,54 +223,61 @@ class _ExpansionPanelsDemoState extends State<ExpasionPanelsDemo> {
});
}

void changeLocation(_Location newLocation) {
setState(() {
item.value = newLocation;
});
}

return new CollapsibleBody(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Radio<_Location>(
value: _Location.Bahamas,
groupValue: item.value,
onChanged: changeLocation
),
new Text('Bahamas')
]
),
new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Radio<_Location>(
value: _Location.Barbados,
groupValue: item.value,
onChanged: changeLocation
),
new Text('Barbados')
]
),
new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Radio<_Location>(
value: _Location.Bermuda,
groupValue: item.value,
onChanged: changeLocation
),
new Text('Bermuda')
]
)
]
),
onSave: close,
onCancel: close
return new Form(
child: new Builder(
builder: (BuildContext context) {
return new CollapsibleBody(
onSave: () { Form.of(context).save(); close(); },
onCancel: () { Form.of(context).reset(); close(); },
child: new FormField<_Location>(
initialValue: item.value,
onSaved: (_Location result) { item.value = result; },
builder: (FormFieldState<_Location> field) {
return new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Radio<_Location>(
value: _Location.Bahamas,
groupValue: field.value,
onChanged: field.onChanged,
),
new Text('Bahamas')
]
),
new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Radio<_Location>(
value: _Location.Barbados,
groupValue: field.value,
onChanged: field.onChanged,
),
new Text('Barbados')
]
),
new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Radio<_Location>(
value: _Location.Bermuda,
groupValue: field.value,
onChanged: field.onChanged,
),
new Text('Bermuda')
]
)
]
);
}
),
);
}
)
);
}
),
Expand All @@ -284,22 +293,30 @@ class _ExpansionPanelsDemoState extends State<ExpasionPanelsDemo> {
});
}

return new CollapsibleBody(
child: new Slider(
value: item.value,
min: 0.0,
max: 100.0,
divisions: 5,
activeColor: Colors.orange[100 + (item.value * 5.0).round()],
label: '${item.value.round()}',
onChanged: (double value) {
setState(() {
item.value = value;
});
return new Form(
child: new Builder(
builder: (BuildContext context) {
return new CollapsibleBody(
onSave: () { Form.of(context).save(); close(); },
onCancel: () { Form.of(context).reset(); close(); },
child: new FormField<double>(
initialValue: item.value,
onSaved: (double value) { item.value = value; },
builder: (FormFieldState<double> field) {
return new Slider(
min: 0.0,
max: 100.0,
divisions: 5,
activeColor: Colors.orange[100 + (field.value * 5.0).round()],
label: '${field.value.round()}',
value: field.value,
onChanged: field.onChanged,
);
},
),
);
}
),
onSave: close,
onCancel: close
)
);
}
)
Expand Down
74 changes: 39 additions & 35 deletions examples/flutter_gallery/lib/demo/text_field_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,37 +30,39 @@ class TextFieldDemoState extends State<TextFieldDemo> {
));
}

GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
GlobalKey<FormFieldState<InputValue>> _passwordFieldKey = new GlobalKey<FormFieldState<InputValue>>();
void _handleSubmitted() {
// TODO(mpcomplete): Form could keep track of validation errors?
if (_validateName(person.name) != null ||
_validatePhoneNumber(person.phoneNumber) != null ||
_validatePassword(person.password) != null) {
FormState form = _formKey.currentState;
if (form.hasErrors) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

showInSnackBar('Please fix the errors in red before submitting.');
} else {
form.save();
showInSnackBar('${person.name}\'s phone number is ${person.phoneNumber}');
}
}

String _validateName(String value) {
if (value.isEmpty)
String _validateName(InputValue value) {
if (value.text.isEmpty)
return 'Name is required.';
RegExp nameExp = new RegExp(r'^[A-za-z ]+$');
if (!nameExp.hasMatch(value))
if (!nameExp.hasMatch(value.text))
return 'Please enter only alphabetical characters.';
return null;
}

String _validatePhoneNumber(String value) {
String _validatePhoneNumber(InputValue value) {
RegExp phoneExp = new RegExp(r'^\d\d\d-\d\d\d\-\d\d\d\d$');
if (!phoneExp.hasMatch(value))
if (!phoneExp.hasMatch(value.text))
return '###-###-#### - Please enter a valid phone number.';
return null;
}

String _validatePassword(String value) {
if (person.password == null || person.password.isEmpty)
String _validatePassword(InputValue value) {
FormFieldState<InputValue> passwordField = _passwordFieldKey.currentState;
if (passwordField.value == null || passwordField.value.text.isEmpty)
return 'Please choose a password.';
if (person.password != value)
if (passwordField.value.text != value.text)
return 'Passwords don\'t match';
return null;
}
Expand All @@ -73,55 +75,57 @@ class TextFieldDemoState extends State<TextFieldDemo> {
title: new Text('Text fields')
),
body: new Form(
key: _formKey,
child: new Block(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: <Widget>[
new Input(
hintText: 'What do people call you?',
labelText: 'Name',
formField: new FormField<String>(
// TODO(mpcomplete): replace with person#name=
setter: (String val) { person.name = val; },
validator: _validateName
)
// It's simpler to use an InputFormField, as below, but a FormField
// that builds an Input is equivalent.
new FormField<InputValue>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not an InputFormField here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment - I want to demonstrate the equivalence of the 2 ways of doing it.

initialValue: InputValue.empty,
onSaved: (InputValue val) { person.name = val.text; },
validator: _validateName,
builder: (FormFieldState<InputValue> field) {
return new Input(
hintText: 'What do people call you?',
labelText: 'Name',
value: field.value,
onChanged: field.onChanged,
errorText: field.errorText
);
},
),
new Input(
new InputFormField(
hintText: 'Where can we reach you?',
labelText: 'Phone Number',
keyboardType: TextInputType.phone,
formField: new FormField<String>(
setter: (String val) { person.phoneNumber = val; },
validator: _validatePhoneNumber
)
onSaved: (InputValue val) { person.phoneNumber = val.text; },
validator: _validatePhoneNumber,
),
new Input(
new InputFormField(
hintText: 'Tell us about yourself (optional)',
labelText: 'Life story',
maxLines: 3,
formField: new FormField<String>()
),
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Flexible(
child: new Input(
child: new InputFormField(
key: _passwordFieldKey,
hintText: 'How do you log in?',
labelText: 'New Password',
hideText: true,
formField: new FormField<String>(
setter: (String val) { person.password = val; }
)
onSaved: (InputValue val) { person.password = val.text; }
)
),
new SizedBox(width: 16.0),
new Flexible(
child: new Input(
child: new InputFormField(
hintText: 'How do you log in?',
labelText: 'Re-type Password',
hideText: true,
formField: new FormField<String>(
validator: _validatePassword
)
validator: _validatePassword,
)
)
]
Expand Down
Loading