mirror of
https://github.com/flutter/samples.git
synced 2025-11-11 07:18:15 +00:00
[Gallery] Fix directory structure (#312)
This commit is contained in:
357
gallery/lib/demos/material/text_field_demo.dart
Normal file
357
gallery/lib/demos/material/text_field_demo.dart
Normal file
@@ -0,0 +1,357 @@
|
||||
// Copyright 2019 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/gestures.dart' show DragStartBehavior;
|
||||
|
||||
import 'package:gallery/l10n/gallery_localizations.dart';
|
||||
|
||||
// BEGIN textFieldDemo
|
||||
|
||||
class TextFieldDemo extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(GalleryLocalizations.of(context).demoTextFieldTitle),
|
||||
),
|
||||
body: TextFormFieldDemo(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextFormFieldDemo extends StatefulWidget {
|
||||
const TextFormFieldDemo({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
TextFormFieldDemoState createState() => TextFormFieldDemoState();
|
||||
}
|
||||
|
||||
class PersonData {
|
||||
String name = '';
|
||||
String phoneNumber = '';
|
||||
String email = '';
|
||||
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() => _PasswordFieldState();
|
||||
}
|
||||
|
||||
class _PasswordFieldState extends State<PasswordField> {
|
||||
bool _obscureText = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
key: widget.fieldKey,
|
||||
obscureText: _obscureText,
|
||||
cursorColor: Theme.of(context).cursorColor,
|
||||
maxLength: 8,
|
||||
onSaved: widget.onSaved,
|
||||
validator: widget.validator,
|
||||
onFieldSubmitted: widget.onFieldSubmitted,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
hintText: widget.hintText,
|
||||
labelText: widget.labelText,
|
||||
helperText: widget.helperText,
|
||||
suffixIcon: GestureDetector(
|
||||
dragStartBehavior: DragStartBehavior.down,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_obscureText = !_obscureText;
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
_obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
semanticLabel: _obscureText
|
||||
? GalleryLocalizations.of(context)
|
||||
.demoTextFieldShowPasswordLabel
|
||||
: GalleryLocalizations.of(context)
|
||||
.demoTextFieldHidePasswordLabel,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
PersonData person = PersonData();
|
||||
|
||||
void showInSnackBar(String value) {
|
||||
_scaffoldKey.currentState.hideCurrentSnackBar();
|
||||
_scaffoldKey.currentState.showSnackBar(SnackBar(
|
||||
content: Text(value),
|
||||
));
|
||||
}
|
||||
|
||||
bool _autoValidate = false;
|
||||
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
final GlobalKey<FormFieldState<String>> _passwordFieldKey =
|
||||
GlobalKey<FormFieldState<String>>();
|
||||
final _UsNumberTextInputFormatter _phoneNumberFormatter =
|
||||
_UsNumberTextInputFormatter();
|
||||
|
||||
void _handleSubmitted() {
|
||||
final form = _formKey.currentState;
|
||||
if (!form.validate()) {
|
||||
_autoValidate = true; // Start validating on every change.
|
||||
showInSnackBar(
|
||||
GalleryLocalizations.of(context).demoTextFieldFormErrors,
|
||||
);
|
||||
} else {
|
||||
form.save();
|
||||
showInSnackBar(GalleryLocalizations.of(context)
|
||||
.demoTextFieldNameHasPhoneNumber(person.name, person.phoneNumber));
|
||||
}
|
||||
}
|
||||
|
||||
String _validateName(String value) {
|
||||
if (value.isEmpty) {
|
||||
return GalleryLocalizations.of(context).demoTextFieldNameRequired;
|
||||
}
|
||||
final nameExp = RegExp(r'^[A-Za-z ]+$');
|
||||
if (!nameExp.hasMatch(value)) {
|
||||
return GalleryLocalizations.of(context)
|
||||
.demoTextFieldOnlyAlphabeticalChars;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String _validatePhoneNumber(String value) {
|
||||
final phoneExp = RegExp(r'^\(\d\d\d\) \d\d\d\-\d\d\d\d$');
|
||||
if (!phoneExp.hasMatch(value)) {
|
||||
return GalleryLocalizations.of(context).demoTextFieldEnterUSPhoneNumber;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String _validatePassword(String value) {
|
||||
final passwordField = _passwordFieldKey.currentState;
|
||||
if (passwordField.value == null || passwordField.value.isEmpty) {
|
||||
return GalleryLocalizations.of(context).demoTextFieldEnterPassword;
|
||||
}
|
||||
if (passwordField.value != value) {
|
||||
return GalleryLocalizations.of(context).demoTextFieldPasswordsDoNotMatch;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cursorColor = Theme.of(context).cursorColor;
|
||||
const sizedBoxSpace = SizedBox(height: 24);
|
||||
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
autovalidate: _autoValidate,
|
||||
child: Scrollbar(
|
||||
child: SingleChildScrollView(
|
||||
dragStartBehavior: DragStartBehavior.down,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
sizedBoxSpace,
|
||||
TextFormField(
|
||||
textCapitalization: TextCapitalization.words,
|
||||
cursorColor: cursorColor,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
icon: Icon(Icons.person),
|
||||
hintText: GalleryLocalizations.of(context)
|
||||
.demoTextFieldWhatDoPeopleCallYou,
|
||||
labelText:
|
||||
GalleryLocalizations.of(context).demoTextFieldNameField,
|
||||
),
|
||||
onSaved: (value) {
|
||||
person.name = value;
|
||||
},
|
||||
validator: _validateName,
|
||||
),
|
||||
sizedBoxSpace,
|
||||
TextFormField(
|
||||
cursorColor: cursorColor,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
icon: Icon(Icons.phone),
|
||||
hintText: GalleryLocalizations.of(context)
|
||||
.demoTextFieldWhereCanWeReachYou,
|
||||
labelText: GalleryLocalizations.of(context)
|
||||
.demoTextFieldPhoneNumber,
|
||||
prefixText: '+1 ',
|
||||
),
|
||||
keyboardType: TextInputType.phone,
|
||||
onSaved: (value) {
|
||||
person.phoneNumber = value;
|
||||
},
|
||||
maxLength: 14,
|
||||
maxLengthEnforced: false,
|
||||
validator: _validatePhoneNumber,
|
||||
// TextInputFormatters are applied in sequence.
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
WhitelistingTextInputFormatter.digitsOnly,
|
||||
// Fit the validating format.
|
||||
_phoneNumberFormatter,
|
||||
],
|
||||
),
|
||||
sizedBoxSpace,
|
||||
TextFormField(
|
||||
cursorColor: cursorColor,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
icon: Icon(Icons.email),
|
||||
hintText: GalleryLocalizations.of(context)
|
||||
.demoTextFieldYourEmailAddress,
|
||||
labelText:
|
||||
GalleryLocalizations.of(context).demoTextFieldEmail,
|
||||
),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
onSaved: (value) {
|
||||
person.email = value;
|
||||
},
|
||||
),
|
||||
sizedBoxSpace,
|
||||
TextFormField(
|
||||
cursorColor: cursorColor,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
hintText: GalleryLocalizations.of(context)
|
||||
.demoTextFieldTellUsAboutYourself,
|
||||
helperText: GalleryLocalizations.of(context)
|
||||
.demoTextFieldKeepItShort,
|
||||
labelText:
|
||||
GalleryLocalizations.of(context).demoTextFieldLifeStory,
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
sizedBoxSpace,
|
||||
TextFormField(
|
||||
cursorColor: cursorColor,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
GalleryLocalizations.of(context).demoTextFieldSalary,
|
||||
suffixText:
|
||||
GalleryLocalizations.of(context).demoTextFieldUSD,
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
sizedBoxSpace,
|
||||
PasswordField(
|
||||
fieldKey: _passwordFieldKey,
|
||||
helperText:
|
||||
GalleryLocalizations.of(context).demoTextFieldNoMoreThan,
|
||||
labelText:
|
||||
GalleryLocalizations.of(context).demoTextFieldPassword,
|
||||
onFieldSubmitted: (value) {
|
||||
setState(() {
|
||||
person.password = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
sizedBoxSpace,
|
||||
TextFormField(
|
||||
cursorColor: cursorColor,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
labelText: GalleryLocalizations.of(context)
|
||||
.demoTextFieldRetypePassword,
|
||||
),
|
||||
maxLength: 8,
|
||||
obscureText: true,
|
||||
validator: _validatePassword,
|
||||
),
|
||||
sizedBoxSpace,
|
||||
Center(
|
||||
child: RaisedButton(
|
||||
child: Text(
|
||||
GalleryLocalizations.of(context).demoTextFieldSubmit),
|
||||
onPressed: _handleSubmitted,
|
||||
),
|
||||
),
|
||||
sizedBoxSpace,
|
||||
Text(
|
||||
GalleryLocalizations.of(context).demoTextFieldRequiredField,
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
sizedBoxSpace,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Format incoming numeric text to fit the format of (###) ###-#### ##
|
||||
class _UsNumberTextInputFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue,
|
||||
TextEditingValue newValue,
|
||||
) {
|
||||
final newTextLength = newValue.text.length;
|
||||
final newText = StringBuffer();
|
||||
int selectionIndex = newValue.selection.end;
|
||||
int usedSubstringIndex = 0;
|
||||
if (newTextLength >= 1) {
|
||||
newText.write('(');
|
||||
if (newValue.selection.end >= 1) selectionIndex++;
|
||||
}
|
||||
if (newTextLength >= 4) {
|
||||
newText.write(newValue.text.substring(0, usedSubstringIndex = 3) + ') ');
|
||||
if (newValue.selection.end >= 3) selectionIndex += 2;
|
||||
}
|
||||
if (newTextLength >= 7) {
|
||||
newText.write(newValue.text.substring(3, usedSubstringIndex = 6) + '-');
|
||||
if (newValue.selection.end >= 6) selectionIndex++;
|
||||
}
|
||||
if (newTextLength >= 11) {
|
||||
newText.write(newValue.text.substring(6, usedSubstringIndex = 10) + ' ');
|
||||
if (newValue.selection.end >= 10) selectionIndex++;
|
||||
}
|
||||
// Dump the rest.
|
||||
if (newTextLength >= usedSubstringIndex) {
|
||||
newText.write(newValue.text.substring(usedSubstringIndex));
|
||||
}
|
||||
return TextEditingValue(
|
||||
text: newText.toString(),
|
||||
selection: TextSelection.collapsed(offset: selectionIndex),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// END
|
||||
Reference in New Issue
Block a user