The Material component library of Flutter provides the input field component TextField and the Form component Form.
Input box (TextField)
TextField is primarily used for text input.
The source code example
Constructors are as follows:
const TextField({ ... TextEditingController controller, FocusNode focusNode, InputDecoration decoration = const InputDecoration(), TextInputType keyboardType, TextInputAction textInputAction, TextStyle style, TextAlign textAlign = TextAlign.start, bool autofocus = false, bool obscureText = false, int maxLines = 1, int maxLength, bool maxLengthEnforced = true, ValueChanged<String> onChanged, VoidCallback onEditingComplete, ValueChanged<String> onSubmitted, List<TextInputFormatter> inputFormatters, bool enabled, this.cursorWidth = 2.0, this.cursorradius, this.cursorcolor, . })Copy the code
What’s not important is not sticky
Attribute interpretation
controller
: controller of the edit box, through which you can set/get the content of the edit box, select the edit content, listen to edit text change events, etc.
In most cases we need to explicitly provide a Controller to interact with the text box. If no Controller is provided, one is automatically created inside the TextField.
-
FocusNode: Controls whether TextField takes the input focus of the current keyboard. It’s a handle, “handle,” that we interact with the keyboard.
-
InputDecoration: Controls the appearance of the TextField, such as prompt text, background color, and border.
Source code example (give the source code, go down to try it, without too much explanation)
const InputDecoration({
this.icon,
this.labelText,
this.labelStyle,
this.helperText,
this.helperStyle,
this.hintText,
this.hintStyle,
this.hintMaxLines,
this.errorText,
this.errorStyle,
this.errorMaxLines,
this.hasFloatingPlaceholder = true,
this.isDense,
this.contentPadding,
this.prefixIcon,
this.prefix,
this.prefixText,
this.prefixStyle,
this.suffixIcon,
this.suffix,
this.suffixText,
this.suffixStyle,
this.counter,
this.counterText,
this.counterStyle,
this.filled,
this.fillColor,
this.errorBorder,
this.focusedBorder,
this.focusedErrorBorder,
this.disabledBorder,
this.enabledBorder,
this.border,
this.enabled = true,
this.semanticCounterText,
this.alignLabelWithHint,
})
Copy the code
-
KeyboardType: used to set the default keyboard input type for the input box. The TextInputType enumeration values are as follows:
- Text: keyboard for text input
- Multiline: multiple lines of text
maxLines
Used together (set to null or greater than 1) - Number C. A numeric keypad will pop up
- Phone: optimized phone number input keyboard; A numeric keypad will pop up and display
* #
- Datetime: optimized date input keyboard; Android will show you
: -
- EmailAddress: indicates the optimized emailAddress. Will be displayed
@.
- Url: optimized URL input keyboard; Will be displayed
/.
-
TextInputAction: Keyboard action button icon (i.e. enter key icon), which is an enumerated value with multiple optional values. For all values, see the official document api.flutter. Dev
Example: When the value is textinputAction. search, the native Android keyboard looks like this:
-
Style: the attributes related to the TextStyle being edited are set in TextStyle
-
TextAlign: Horizontal alignment of edited text in an input box.
For example, textAlign: textalign. right is displayed from back to left
-
Autofocus: Indicates whether to automatically obtain the focus. The value can be true or false
-
ObscureText: Indicates whether to hide the text being edited, for example, the password input scenario. The text content is replaced by “•”.
-
MaxLines: The maximum number of lines in the input box, default is 1; If null, there is no limit on the number of rows.
-
MaxLength and maxLengthEnforced: maxLength indicates the maximum length of the input text. The input text count is displayed in the lower right corner of the input text box. MaxLengthEnforced Determines whether to block input if the length of the input text exceeds maxLength, if true, it will block input, if false it will not block input but the input box will turn red.
-
OnChanged: the callback function when the input box content changes; Note: Content change events can also be listened for via controllers.
-
OnEditingComplete and onSubmitted: Both of these callbacks are triggered when the input field is complete, such as when the keyboard’s finish key (checkmark icon) or search key (🔍 icon) is pressed. The difference is that the two callback signatures are different. The onSubmitted callback is of type ValueChanged
and receives the current input as a parameter, whereas onEditingComplete does not.
-
InputFormatters: Used to specify the input format; When user input changes, it is verified against the specified format.
-
CursorWidth, cursorRadius, and cursorColor: these three properties are used to customize the input box cursorWidth, rounded corners, and color.
For example: cursorColor: color.green, cursorWidth: 10, and cursorRadius: radio.circular (4),
Code sample
TextField(
keyboardType: TextInputType.url,
decoration: InputDecoration(
labelText: 'xxx',
prefix: Icon(Icons.lock),
// enabled: false
),
textInputAction: TextInputAction.next,
style: TextStyle(color: Colors.red),
textAlign: TextAlign.right,
autofocus: false,
// obscureText: true,
maxLines: null,
maxLength: 10,
maxLengthEnforced: true,
cursorColor: Colors.green,
cursorWidth: 10,
cursorRadius: Radius.circular(4),
),
Copy the code
Operation effect:
Important content
Column(children: <Widget>[TextField(Autofocus: true, decoration: InputDecoration(labelText: "username ", hintText: "User name or email ", prefixIcon: Icon(Icons. Person)), TextField(decoration: InputDecoration(labelText:" password ", hintText: "Your login password ", prefixIcon: Icon(Icons. Lock)), obscureText: true,),],);Copy the code
Operation effect:
Get input
There are two ways to get input:
-
Define two variables to hold the username and password, and then each to save the input when onChanged is triggered.
-
Get it directly from controller.
The first way is relatively simple, so I will not say, focus on the second way, to the user name input box for example:
- To define a
controller
:
// Define a controller TextEditingController _unameController = TextEditingController();Copy the code
- Set input field
controller
:
TextField(
autofocus: true,
controller: _unameController, //设置controller
...
)
Copy the code
- through
controller
Get the contents of the input box:
print(_unameController.text)
Copy the code
Code examples:
import 'package:flutter/material.dart'; class CategoryPage extends StatefulWidget { @override _CategoryPageState createState() => _CategoryPageState(); } class _CategoryPageState extends State<CategoryPage> {// Define a controller TextEditingController _unameController = TextEditingController(); @override Widget build(BuildContext context) {return Scaffold(appBar: appBar (title: Text(" input "),), body: Column(children: <Widget>[TextField(Autofocus: true, controller: _unameController, // setting decoration: InputDecoration(labelText: "user name ", hintText:" user name or email ", prefixIcon: Icon(Icons. Person),), onChanged: (String) => {// Trigger onChanged print(_unamecontroller.text) // Print the contents of the input box},), TextField(decoration: InputDecoration(labelText: "password ", hintText:" your login password ", prefixIcon: Icon(Icons. Lock)), obscureText: true,),); }}Copy the code
Operation effect:
Listen for text changes
There are also two ways to listen for text changes:
- Set up the
onChanged
Callbacks, such as:
TextField( autofocus: true, onChanged: (v) { print("onChange: $v"); })Copy the code
- through
controller
Monitor, for example:
@ override void initState () {/ / listening input change _unameController addListener (() {print (_unameController. Text); }); }Copy the code
Compared to the two methods, onChanged is specifically used to listen for text changes, while controller has more functions. In addition to listening for text changes, it can also set default values and select text.
Example:
- To create a
controller
:
TextEditingController _selectionController = TextEditingController();
Copy the code
- Set the default values and select the following characters from the third character:
_selectionController.text="hello world!" ; _selectionController.selection=TextSelection( baseOffset: 2, extentOffset: _selectionController.text.length );Copy the code
- Set up the
controller
:
TextField(
controller: _selectionController,
)
Copy the code
Code:
class _CategoryPageState extends State<CategoryPage> { TextEditingController _selectionController = TextEditingController(); @override Widget build(BuildContext context) {_selectionController.text = "I love you!" ; _selectionController.selection = TextSelection( baseOffset: 2, extentOffset: _selectionController.text.length); Return Scaffold(appBar: appBar (title: Text(" input "),), body: Column(children: <Widget>[TextField(Controller: _selectionController, ), ], ), ); }}Copy the code
Operation effect:
Control the focus
Focus can be controlled by FocusNode and FocusScopeNode. By default, focus is managed by FocusScope, which represents the focus control range within which FocusScopeNode can be used to move focus between input boxes, set the default focus, and so on. We can get the default FocusScope.of(context) in the Widget tree.
The example requires: create two Textfields, the first automatically gets focus, and then create two buttons: click the first button to move focus from the first TextField to the second TextField, and click the second button to close the keyboard.
The code is as follows:
class FocusTestRoute extends StatefulWidget { @override _FocusTestRouteState createState() => new _FocusTestRouteState(); } class _FocusTestRouteState extends State<FocusTestRoute> { FocusNode focusNode1 = new FocusNode(); FocusNode focusNode2 = new FocusNode(); FocusScopeNode focusScopeNode; @override Widget build(BuildContext context) {return Padding(Padding: EdgeInsets. All (16.0), child: Column(children: <Widget>[TextField(Autofocus: true, focusNode: focusNode1,// Associate focusNode1 decoration: InputDecoration(labelText: "Input1"),), TextField(focusNode: focusNode2,// Associated focusNode2 decoration: InputDecoration(labelText: "input2" ), ), Builder(builder: (ctx) { return Column( children: <Widget>[ RaisedButton( child: Text(" move focus "), onPressed: Focusscope.of (context).requestFocus(focusNode2); // Change the focus from the first TextField to the second TextField. // This is the second way to write if(null == focusScopeNode){focusScopeNode = focusscope.of (context); } focusScopeNode. RequestFocus (focusNode2);}), RaisedButton (child: Text (" hidden keyboard "), onPressed: () {/ / when all edit box loses focus keyboard will fold focusNode1. Unfocus (); focusNode2. Unfocus ();},),,);},),,,); }}Copy the code
Because my emulator doesn’t show the keyboard, it shows a keyboard underneath when the input field is in focus (I don’t show it).
Listen for focus state change events
FocusNode inherits from ChangeNotifier. You can listen for focus change events using FocusNode, for example:
. // Create a focusNode. FocusNode focusNode = new focusNode (); . // focusNode binds the input box TextField(focusNode: focusNode); . Focusnode.addlistener ((){print(focusNode.hasfocus); });Copy the code
Focusnode. hasFocus is true when focus is gained and false when focus is lost.
Form (Form)
The input fields described above can be operated individually, but often in development, multiple input fields need to be operated simultaneously, such as clearing the contents of all input fields.
To this end, Flutter provides a Form component that groups input fields and performs uniform operations, such as validation, resetting, and saving input fields.
The Form inherits from the StatefulWidget object, and its corresponding state class is FormState.
The source code example
Constructors are as follows:
Form({
@required Widget child,
bool autovalidate = false,
WillPopCallback onWillPop,
VoidCallback onChanged,
})
Copy the code
Attribute interpretation
-
Autovalidate: Whether the input is automatically verified. When true, each subformField is automatically validated and displays an error message when its content changes. Otherwise, you need to check manually by calling formstate.validate ().
-
OnWillPop: Determines whether the route from the Form can return directly (e.g. by clicking the return button). This callback returns a Future object. If the Future is false, the current route will not return. If true, the last route is returned. This property is typically used to intercept the back button.
-
OnChanged: This callback is triggered when the contents of any of the FormField children of the Form change.
FormField
Form’s descendants must be of type FormField, which is an abstract class that defines several properties through which FormState internally performs operations,
The FormField partial constructor looks like this:
const FormField({ ... FormFieldSetter<T> onSaved, // Save the callback FormFieldValidator<T> // Initial value bool autovalidate = false, // Whether automatic verification is enabled. })Copy the code
For ease of use, Flutter provides a TextFormField component that inherits from the FormField class and is also a wrapper class for TextField, so it also includes the properties of TextField in addition to the properties defined by FormField.
FormState
FormState is the State class for the Form, available via form.of () or GlobalKey. We can use it to operate uniformly on a Form descendant, FormField.
Three commonly used methods:
-
Formstate.validate () : This method calls the validate callback of the descendant FormField, which returns false if any validations fail, and all validations return an error message.
-
Formstate.save () : After calling this method, the save callback of the Form descendant FormField is called to save the Form content.
-
Formstate.reset () : This method clears the descendant FormField.
Code sample
Modify the user login example above to validate before committing:
- The user name cannot be empty. If the user name is empty, a message “User name cannot be empty” is displayed.
- The password must contain at least six characters. If the password is less than six, the system displays the password must contain at least six characters.
Code:
import 'package:flutter/material.dart'; class CategoryPage extends StatefulWidget { @override _CategoryPageState createState() => _CategoryPageState(); } class _CategoryPageState extends State<CategoryPage> { TextEditingController _unameController = new TextEditingController(); TextEditingController _pwdController = new TextEditingController(); GlobalKey _formKey = new GlobalKey<FormState>(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Form Test"), ), body: Symmetric (vertical: 16.0, horizontal: 24.0), child: Form(key: _formKey, // Set globalKey to FormState autovalidate: true, // enable automatic validation child: Column(children: <Widget>[ TextFormField( autofocus: true, controller: _unameController, decoration: InputDecoration( labelText: "User name ", hintText:" user name ", icon: icon (icon.person)), // Validator: (v) {return v.rim ().length > 0? Null: }), TextFormField(Controller: _pwdController, decoration: InputDecoration(labelText: "password ", hintText: "Your login password ", icon: icon (Icons. Lock)), obscureText: true, // validator: (v) {return v.tabrim ().length > 5? Null: }), // PaddING-back button (padding-back: const EdgeInsets. Only (top: 28.0), child: Row(children: <Widget>[Expanded(child: RaisedButton(padding: edgeinsets. all(15.0)), child: Text(" register "), color: Theme.of(context).primaryColor, textColor: Colors.white, onPressed: //print(form.of (context)); // After _formkey.currentState is used to obtain FormState, // call validate() to verify that the user name and password are valid. If ((_formKey. CurrentState as FormState). The validate ()) {/ / verification by submitting data}},),),),),),),),),); }}Copy the code
Operation effect:
Note that the onPressed method of the login button cannot be retrieved from form. of(context) because the context is the context for FormTestRoute and form. of(context) looks at the root based on the specified context. And FormState is in the subtree of form stroute, so no. The correct way to build the login button is through the Builder, which takes the widget node’s context as a callback argument:
Expanded(// Use Builder to get the real context(Element) of the widget tree in which RaisedButton is located (context){ return RaisedButton( ... OnPressed: () {// Since this widget is also a child of the Form widget, FormState if(form.of (context).validate()){// Validate by submitting data}},) can be retrieved as follows; }))Copy the code
Be carefulcontext
Is pointing to the problem.