Updated the gallery text fields demo (#15362)
This commit is contained in:
parent
c345c1bbea
commit
b6cca3923a
@ -23,6 +23,60 @@ class PersonData {
|
||||
String password = '';
|
||||
}
|
||||
|
||||
class PasswordField extends StatefulWidget {
|
||||
const PasswordField({
|
||||
this.fieldKey,
|
||||
this.hintText,
|
||||
this.labelText,
|
||||
this.helperText,
|
||||
this.onSaved,
|
||||
this.validator,
|
||||
this.onFieldSubmitted,
|
||||
});
|
||||
|
||||
final Key fieldKey;
|
||||
final String hintText;
|
||||
final String labelText;
|
||||
final String helperText;
|
||||
final FormFieldSetter<String> onSaved;
|
||||
final FormFieldValidator<String> validator;
|
||||
final ValueChanged<String> onFieldSubmitted;
|
||||
|
||||
@override
|
||||
_PasswordFieldState createState() => new _PasswordFieldState();
|
||||
}
|
||||
|
||||
class _PasswordFieldState extends State<PasswordField> {
|
||||
bool _obscureText = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new TextFormField(
|
||||
key: widget.fieldKey,
|
||||
obscureText: _obscureText,
|
||||
maxLength: 8,
|
||||
onSaved: widget.onSaved,
|
||||
validator: widget.validator,
|
||||
onFieldSubmitted: widget.onFieldSubmitted,
|
||||
decoration: new InputDecoration(
|
||||
border: const UnderlineInputBorder(),
|
||||
filled: true,
|
||||
hintText: widget.hintText,
|
||||
labelText: widget.labelText,
|
||||
helperText: widget.helperText,
|
||||
suffixIcon: new GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_obscureText = !_obscureText;
|
||||
});
|
||||
},
|
||||
child: new Icon(_obscureText ? Icons.visibility : Icons.visibility_off),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
@ -36,6 +90,7 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
|
||||
bool _autovalidate = false;
|
||||
bool _formWasEdited = false;
|
||||
|
||||
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
|
||||
final GlobalKey<FormFieldState<String>> _passwordFieldKey = new GlobalKey<FormFieldState<String>>();
|
||||
final _UsNumberTextInputFormatter _phoneNumberFormatter = new _UsNumberTextInputFormatter();
|
||||
@ -64,7 +119,7 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
_formWasEdited = true;
|
||||
final RegExp phoneExp = new RegExp(r'^\(\d\d\d\) \d\d\d\-\d\d\d\d$');
|
||||
if (!phoneExp.hasMatch(value))
|
||||
return '(###) ###-#### - Please enter a valid US phone number.';
|
||||
return '(###) ###-#### - Enter a US phone number.';
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -72,9 +127,9 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
_formWasEdited = true;
|
||||
final FormFieldState<String> passwordField = _passwordFieldKey.currentState;
|
||||
if (passwordField.value == null || passwordField.value.isEmpty)
|
||||
return 'Please choose a password.';
|
||||
return 'Please enter a password.';
|
||||
if (passwordField.value != value)
|
||||
return 'Passwords don\'t match';
|
||||
return 'The passwords don\'t match';
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -118,11 +173,16 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
key: _formKey,
|
||||
autovalidate: _autovalidate,
|
||||
onWillPop: _warnUserAboutInvalidData,
|
||||
child: new ListView(
|
||||
child: new SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: new Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 24.0),
|
||||
new TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
border: const UnderlineInputBorder(),
|
||||
filled: true,
|
||||
icon: const Icon(Icons.person),
|
||||
hintText: 'What do people call you?',
|
||||
labelText: 'Name *',
|
||||
@ -130,12 +190,15 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
onSaved: (String value) { person.name = value; },
|
||||
validator: _validateName,
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
new TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
border: const UnderlineInputBorder(),
|
||||
filled: true,
|
||||
icon: const Icon(Icons.phone),
|
||||
hintText: 'Where can we reach you?',
|
||||
labelText: 'Phone Number *',
|
||||
prefixText: '+1'
|
||||
prefixText: '+1',
|
||||
),
|
||||
keyboardType: TextInputType.phone,
|
||||
onSaved: (String value) { person.phoneNumber = value; },
|
||||
@ -147,8 +210,11 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
_phoneNumberFormatter,
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
new TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
border: const UnderlineInputBorder(),
|
||||
filled: true,
|
||||
icon: const Icon(Icons.email),
|
||||
hintText: 'Your email address',
|
||||
labelText: 'E-mail',
|
||||
@ -156,17 +222,21 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
onSaved: (String value) { person.email = value; },
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
new TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: 'Tell us about yourself',
|
||||
helperText: 'Keep it short, this is just a demo',
|
||||
helperText: 'Keep it short, this is just a demo.',
|
||||
labelText: 'Life story',
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
new TextFormField(
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: 'Salary',
|
||||
prefixText: '\$',
|
||||
suffixText: 'USD',
|
||||
@ -174,50 +244,43 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
new Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new Expanded(
|
||||
child: new TextFormField(
|
||||
key: _passwordFieldKey,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'How do you log in?',
|
||||
labelText: 'New Password *',
|
||||
),
|
||||
obscureText: true,
|
||||
const SizedBox(height: 24.0),
|
||||
new PasswordField(
|
||||
fieldKey: _passwordFieldKey,
|
||||
helperText: 'No more than 8 characters.',
|
||||
labelText: 'Password *',
|
||||
onSaved: (String value) { person.password = value; },
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16.0),
|
||||
new Expanded(
|
||||
child: new TextFormField(
|
||||
const SizedBox(height: 24.0),
|
||||
new TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'How do you log in?',
|
||||
labelText: 'Re-type Password *',
|
||||
border: const UnderlineInputBorder(),
|
||||
filled: true,
|
||||
labelText: 'Re-type password',
|
||||
),
|
||||
maxLength: 8,
|
||||
onFieldSubmitted: (String value) { person.password = value; },
|
||||
obscureText: true,
|
||||
onFieldSubmitted: (String value) { _handleSubmitted(); },
|
||||
validator: _validatePassword,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
new Container(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
alignment: Alignment.center,
|
||||
const SizedBox(height: 24.0),
|
||||
new Center(
|
||||
child: new RaisedButton(
|
||||
child: const Text('SUBMIT'),
|
||||
onPressed: _handleSubmitted,
|
||||
),
|
||||
),
|
||||
new Container(
|
||||
padding: const EdgeInsets.only(top: 20.0),
|
||||
child: new Text('* indicates required field', style: Theme.of(context).textTheme.caption),
|
||||
const SizedBox(height: 24.0),
|
||||
new Text(
|
||||
'* indicates required field',
|
||||
style: Theme.of(context).textTheme.caption
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -16,21 +16,36 @@ void main() {
|
||||
final Finder nameField = find.widgetWithText(TextFormField, 'Name *');
|
||||
expect(nameField, findsOneWidget);
|
||||
|
||||
final Finder passwordField = find.widgetWithText(TextFormField, 'Password *');
|
||||
expect(passwordField, findsOneWidget);
|
||||
|
||||
await tester.enterText(nameField, '');
|
||||
// The submit button isn't initially visible. Drag it into view so that
|
||||
// it will see the tap.
|
||||
await tester.drag(nameField, const Offset(0.0, -1200.0));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(submitButton);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Now drag the password field (the submit button will be obscured by
|
||||
// the snackbar) and expose the name field again.
|
||||
await tester.drag(passwordField, const Offset(0.0, 1200.0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Name is required.'), findsOneWidget);
|
||||
expect(find.text('Please enter only alphabetical characters.'), findsNothing);
|
||||
|
||||
await tester.enterText(nameField, '#');
|
||||
|
||||
// Make the submit button visible again (by dragging the name field), so
|
||||
// it will see the tap.
|
||||
await tester.drag(nameField, const Offset(0.0, -1200.0));
|
||||
await tester.tap(submitButton);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Name is required.'), findsNothing);
|
||||
expect(find.text('Please enter only alphabetical characters.'), findsOneWidget);
|
||||
|
||||
await tester.enterText(nameField, 'Jane Doe');
|
||||
await tester.tap(submitButton);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Name is required.'), findsNothing);
|
||||
expect(find.text('Please enter only alphabetical characters.'), findsNothing);
|
||||
});
|
||||
|
@ -60,7 +60,9 @@ class TextFormField extends FormField<String> {
|
||||
bool autofocus: false,
|
||||
bool obscureText: false,
|
||||
bool autocorrect: true,
|
||||
bool maxLengthEnforced: true,
|
||||
int maxLines: 1,
|
||||
int maxLength,
|
||||
ValueChanged<String> onFieldSubmitted,
|
||||
FormFieldSetter<String> onSaved,
|
||||
FormFieldValidator<String> validator,
|
||||
@ -71,7 +73,9 @@ class TextFormField extends FormField<String> {
|
||||
assert(autofocus != null),
|
||||
assert(obscureText != null),
|
||||
assert(autocorrect != null),
|
||||
assert(maxLengthEnforced != null),
|
||||
assert(maxLines == null || maxLines > 0),
|
||||
assert(maxLength == null || maxLength > 0),
|
||||
super(
|
||||
key: key,
|
||||
initialValue: controller != null ? controller.text : (initialValue ?? ''),
|
||||
@ -91,7 +95,9 @@ class TextFormField extends FormField<String> {
|
||||
autofocus: autofocus,
|
||||
obscureText: obscureText,
|
||||
autocorrect: autocorrect,
|
||||
maxLengthEnforced: maxLengthEnforced,
|
||||
maxLines: maxLines,
|
||||
maxLength: maxLength,
|
||||
onChanged: field.onChanged,
|
||||
onSubmitted: onFieldSubmitted,
|
||||
inputFormatters: inputFormatters,
|
||||
|
Loading…
x
Reference in New Issue
Block a user