Updated the gallery text fields demo (#15362)
This commit is contained in:
parent
c345c1bbea
commit
b6cca3923a
@ -23,6 +23,60 @@ class PersonData {
|
|||||||
String password = '';
|
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> {
|
class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
@ -36,6 +90,7 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
|||||||
|
|
||||||
bool _autovalidate = false;
|
bool _autovalidate = false;
|
||||||
bool _formWasEdited = false;
|
bool _formWasEdited = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
|
||||||
final GlobalKey<FormFieldState<String>> _passwordFieldKey = new GlobalKey<FormFieldState<String>>();
|
final GlobalKey<FormFieldState<String>> _passwordFieldKey = new GlobalKey<FormFieldState<String>>();
|
||||||
final _UsNumberTextInputFormatter _phoneNumberFormatter = new _UsNumberTextInputFormatter();
|
final _UsNumberTextInputFormatter _phoneNumberFormatter = new _UsNumberTextInputFormatter();
|
||||||
@ -64,7 +119,7 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
|||||||
_formWasEdited = true;
|
_formWasEdited = true;
|
||||||
final RegExp phoneExp = new RegExp(r'^\(\d\d\d\) \d\d\d\-\d\d\d\d$');
|
final RegExp phoneExp = new RegExp(r'^\(\d\d\d\) \d\d\d\-\d\d\d\d$');
|
||||||
if (!phoneExp.hasMatch(value))
|
if (!phoneExp.hasMatch(value))
|
||||||
return '(###) ###-#### - Please enter a valid US phone number.';
|
return '(###) ###-#### - Enter a US phone number.';
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,9 +127,9 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
|||||||
_formWasEdited = true;
|
_formWasEdited = true;
|
||||||
final FormFieldState<String> passwordField = _passwordFieldKey.currentState;
|
final FormFieldState<String> passwordField = _passwordFieldKey.currentState;
|
||||||
if (passwordField.value == null || passwordField.value.isEmpty)
|
if (passwordField.value == null || passwordField.value.isEmpty)
|
||||||
return 'Please choose a password.';
|
return 'Please enter a password.';
|
||||||
if (passwordField.value != value)
|
if (passwordField.value != value)
|
||||||
return 'Passwords don\'t match';
|
return 'The passwords don\'t match';
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,103 +173,111 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
|||||||
key: _formKey,
|
key: _formKey,
|
||||||
autovalidate: _autovalidate,
|
autovalidate: _autovalidate,
|
||||||
onWillPop: _warnUserAboutInvalidData,
|
onWillPop: _warnUserAboutInvalidData,
|
||||||
child: new ListView(
|
child: new SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
children: <Widget>[
|
child: new Column(
|
||||||
new TextFormField(
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
decoration: const InputDecoration(
|
children: <Widget>[
|
||||||
icon: const Icon(Icons.person),
|
const SizedBox(height: 24.0),
|
||||||
hintText: 'What do people call you?',
|
new TextFormField(
|
||||||
labelText: 'Name *',
|
decoration: const InputDecoration(
|
||||||
),
|
border: const UnderlineInputBorder(),
|
||||||
onSaved: (String value) { person.name = value; },
|
filled: true,
|
||||||
validator: _validateName,
|
icon: const Icon(Icons.person),
|
||||||
),
|
hintText: 'What do people call you?',
|
||||||
new TextFormField(
|
labelText: 'Name *',
|
||||||
decoration: const InputDecoration(
|
|
||||||
icon: const Icon(Icons.phone),
|
|
||||||
hintText: 'Where can we reach you?',
|
|
||||||
labelText: 'Phone Number *',
|
|
||||||
prefixText: '+1'
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.phone,
|
|
||||||
onSaved: (String value) { person.phoneNumber = value; },
|
|
||||||
validator: _validatePhoneNumber,
|
|
||||||
// TextInputFormatters are applied in sequence.
|
|
||||||
inputFormatters: <TextInputFormatter> [
|
|
||||||
WhitelistingTextInputFormatter.digitsOnly,
|
|
||||||
// Fit the validating format.
|
|
||||||
_phoneNumberFormatter,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
new TextFormField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
icon: const Icon(Icons.email),
|
|
||||||
hintText: 'Your email address',
|
|
||||||
labelText: 'E-mail',
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.emailAddress,
|
|
||||||
onSaved: (String value) { person.email = value; },
|
|
||||||
),
|
|
||||||
new TextFormField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'Tell us about yourself',
|
|
||||||
helperText: 'Keep it short, this is just a demo',
|
|
||||||
labelText: 'Life story',
|
|
||||||
),
|
|
||||||
maxLines: 3,
|
|
||||||
),
|
|
||||||
new TextFormField(
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Salary',
|
|
||||||
prefixText: '\$',
|
|
||||||
suffixText: 'USD',
|
|
||||||
suffixStyle: const TextStyle(color: Colors.green)
|
|
||||||
),
|
|
||||||
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,
|
|
||||||
onSaved: (String value) { person.password = value; },
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16.0),
|
onSaved: (String value) { person.name = value; },
|
||||||
new Expanded(
|
validator: _validateName,
|
||||||
child: new TextFormField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'How do you log in?',
|
|
||||||
labelText: 'Re-type Password *',
|
|
||||||
),
|
|
||||||
obscureText: true,
|
|
||||||
onFieldSubmitted: (String value) { _handleSubmitted(); },
|
|
||||||
validator: _validatePassword,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
new Container(
|
|
||||||
padding: const EdgeInsets.all(20.0),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: new RaisedButton(
|
|
||||||
child: const Text('SUBMIT'),
|
|
||||||
onPressed: _handleSubmitted,
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 24.0),
|
||||||
new Container(
|
new TextFormField(
|
||||||
padding: const EdgeInsets.only(top: 20.0),
|
decoration: const InputDecoration(
|
||||||
child: new Text('* indicates required field', style: Theme.of(context).textTheme.caption),
|
border: const UnderlineInputBorder(),
|
||||||
),
|
filled: true,
|
||||||
],
|
icon: const Icon(Icons.phone),
|
||||||
|
hintText: 'Where can we reach you?',
|
||||||
|
labelText: 'Phone Number *',
|
||||||
|
prefixText: '+1',
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.phone,
|
||||||
|
onSaved: (String value) { person.phoneNumber = value; },
|
||||||
|
validator: _validatePhoneNumber,
|
||||||
|
// TextInputFormatters are applied in sequence.
|
||||||
|
inputFormatters: <TextInputFormatter> [
|
||||||
|
WhitelistingTextInputFormatter.digitsOnly,
|
||||||
|
// Fit the validating format.
|
||||||
|
_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',
|
||||||
|
),
|
||||||
|
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.',
|
||||||
|
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',
|
||||||
|
suffixStyle: const TextStyle(color: Colors.green)
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
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(height: 24.0),
|
||||||
|
new TextFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
filled: true,
|
||||||
|
labelText: 'Re-type password',
|
||||||
|
),
|
||||||
|
maxLength: 8,
|
||||||
|
onFieldSubmitted: (String value) { person.password = value; },
|
||||||
|
obscureText: true,
|
||||||
|
validator: _validatePassword,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24.0),
|
||||||
|
new Center(
|
||||||
|
child: new RaisedButton(
|
||||||
|
child: const Text('SUBMIT'),
|
||||||
|
onPressed: _handleSubmitted,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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 *');
|
final Finder nameField = find.widgetWithText(TextFormField, 'Name *');
|
||||||
expect(nameField, findsOneWidget);
|
expect(nameField, findsOneWidget);
|
||||||
|
|
||||||
|
final Finder passwordField = find.widgetWithText(TextFormField, 'Password *');
|
||||||
|
expect(passwordField, findsOneWidget);
|
||||||
|
|
||||||
await tester.enterText(nameField, '');
|
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.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('Name is required.'), findsOneWidget);
|
||||||
expect(find.text('Please enter only alphabetical characters.'), findsNothing);
|
expect(find.text('Please enter only alphabetical characters.'), findsNothing);
|
||||||
|
|
||||||
await tester.enterText(nameField, '#');
|
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.tap(submitButton);
|
||||||
await tester.pump();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('Name is required.'), findsNothing);
|
expect(find.text('Name is required.'), findsNothing);
|
||||||
expect(find.text('Please enter only alphabetical characters.'), findsOneWidget);
|
expect(find.text('Please enter only alphabetical characters.'), findsOneWidget);
|
||||||
|
|
||||||
await tester.enterText(nameField, 'Jane Doe');
|
await tester.enterText(nameField, 'Jane Doe');
|
||||||
await tester.tap(submitButton);
|
await tester.tap(submitButton);
|
||||||
await tester.pump();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('Name is required.'), findsNothing);
|
expect(find.text('Name is required.'), findsNothing);
|
||||||
expect(find.text('Please enter only alphabetical characters.'), findsNothing);
|
expect(find.text('Please enter only alphabetical characters.'), findsNothing);
|
||||||
});
|
});
|
||||||
|
@ -60,7 +60,9 @@ class TextFormField extends FormField<String> {
|
|||||||
bool autofocus: false,
|
bool autofocus: false,
|
||||||
bool obscureText: false,
|
bool obscureText: false,
|
||||||
bool autocorrect: true,
|
bool autocorrect: true,
|
||||||
|
bool maxLengthEnforced: true,
|
||||||
int maxLines: 1,
|
int maxLines: 1,
|
||||||
|
int maxLength,
|
||||||
ValueChanged<String> onFieldSubmitted,
|
ValueChanged<String> onFieldSubmitted,
|
||||||
FormFieldSetter<String> onSaved,
|
FormFieldSetter<String> onSaved,
|
||||||
FormFieldValidator<String> validator,
|
FormFieldValidator<String> validator,
|
||||||
@ -71,7 +73,9 @@ class TextFormField extends FormField<String> {
|
|||||||
assert(autofocus != null),
|
assert(autofocus != null),
|
||||||
assert(obscureText != null),
|
assert(obscureText != null),
|
||||||
assert(autocorrect != null),
|
assert(autocorrect != null),
|
||||||
|
assert(maxLengthEnforced != null),
|
||||||
assert(maxLines == null || maxLines > 0),
|
assert(maxLines == null || maxLines > 0),
|
||||||
|
assert(maxLength == null || maxLength > 0),
|
||||||
super(
|
super(
|
||||||
key: key,
|
key: key,
|
||||||
initialValue: controller != null ? controller.text : (initialValue ?? ''),
|
initialValue: controller != null ? controller.text : (initialValue ?? ''),
|
||||||
@ -91,7 +95,9 @@ class TextFormField extends FormField<String> {
|
|||||||
autofocus: autofocus,
|
autofocus: autofocus,
|
||||||
obscureText: obscureText,
|
obscureText: obscureText,
|
||||||
autocorrect: autocorrect,
|
autocorrect: autocorrect,
|
||||||
|
maxLengthEnforced: maxLengthEnforced,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
|
maxLength: maxLength,
|
||||||
onChanged: field.onChanged,
|
onChanged: field.onChanged,
|
||||||
onSubmitted: onFieldSubmitted,
|
onSubmitted: onFieldSubmitted,
|
||||||
inputFormatters: inputFormatters,
|
inputFormatters: inputFormatters,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user