The text
-
Common Configurations
class TextTest extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: EdgeInsets.all(30), child: Column( children: <Widget>[ Text("hello "), // The maximum number of lines is one, textoverflow. ellipsis: ellipsis instead Text( "hello! I'm 345 " * 5, maxLines: 1, overflow: TextOverflow.ellipsis, ), // Text scaling factor Text("hello 345", textScaleFactor: 2), // Text("hello 345", style: TextStyle( / / color color: Colors.red, // The size is 14 by default fontSize: 18./ / the thickness fontWeight: FontWeight.w800, / / italics fontStyle: FontStyle.italic, //underline: underline, overline, lineThrough: underline decoration: TextDecoration.underline, decorationColor: Colors.black, // Solid: the solid line, double: the dotted line, dotted line, the wavy: the wavy linedecorationStyle: TextDecorationStyle.wavy)) ], ), )); }}Copy the code
TextAlign: Alignment of text; You can choose to align left, right, or center. Notice that the reference frame for alignment is the Text widget itself
-
DefaultTextStyle
In the Widget tree, text styles are inheritable by default, so if you set a default style to a node in the Widget tree, all text in the interface’s subtrees will default to that style
Widget build(BuildContext context) { return Scaffold( body: Padding( padding: EdgeInsets.all(30), child: DefaultTextStyle( style: TextStyle( / / color color: Colors.red, // The size is 14 by default fontSize: 20./ / the thickness fontWeight: FontWeight.w900, ), child: Column( children: [ Text("hello "), Text("hello! I'm 345 " * 5, maxLines: 1,overflow: TextOverflow.ellipsis,), // Replace the default text Text("hello 345",style: TextStyle(fontSize: 25, color: Colors.black)) ], )))); } Copy the code
-
TextSpan
If you need to display different parts of a Text, you can use TextSpan, which represents a fragment of the Text
const TextSpan({ TextStyle style, Sting text, List<TextSpan> children, GestureRecognizer recognizer, }); Copy the code
Style and text represent style and content, and children are an array, which means that TextSpan can contain other TextSpans, and the recognizer recognises that text fragment with gestures
Widget _richText() { var textSpan = TextSpan(text: "hello", children: [ TextSpan(text: "3", style: TextStyle(color: Colors.blueAccent)), TextSpan(text: "4", style: TextStyle(color: Colors.black)), TextSpan(text: "5", style: TextStyle(color: Colors.green)) ]); return Text.rich(textSpan); } Copy the code
We used the text. rich method above to add TextSpan to Text because Text itself is a wrapper around RichText, which is a widget that displays a variety of styles (rich Text) as follows:
-
The font
Using fonts in FLUTTER requires two steps, first declaring them in a pubspec.yaml file and then using the font via the textStyle property
flutter: fonts: - family: Raleway fonts: - asset: assets/fonts/Raleway-Regular.ttf - asset: assets/fonts/Raleway-Medium.ttf weight: 500 - asset: assets/fonts/Raleway-SemiBold.ttf weight: 600 - family: AbrilFatface fonts: - asset: assets/fonts/abrilfatface/AbrilFatface-Regular.ttf Copy the code
Use the font
// Declare a text style const textStyle = const TextStyle( fontFamily: 'Raleway',);// Use text styles var buttonText = const Text( "Use the font for this text", style: textStyle, ); Copy the code
-
button
The Material component library provides many button components, such as RaisedButton, FlatButton, OutlineButton, etc., which are indirect or direct packaging customization of RawMaterialButton components. So most of them are properties just like RawMaterialButton
Additionally, buttons in all Material libraries have the following in common:
1. “Water ripple Animation” will appear when pressed.
Both have an onPressed property to set the callback of the click event. Without this callback, the button will be disabled and the disabled state will not respond to user clicks
All kinds of common buttons
class Button extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
// Float button with shadow and grey background by default
RaisedButton(
child: Text("RaisedButton"),
onPressed: () => print('RaisedButton'),),// Flat button, default background transparent without shadow
FlatButton(
child: Text("flatButton"),
onPressed: () => print('flatButton'),),// There is a border by default, with no shadow and a transparent background
OutlineButton(
child: Text("OutlineButton"),
onPressed: () => print('OutlineButton'),),// Clickable Icon
IconButton(
icon: Icon(Icons.thumb_up_alt),
onPressed: () => print('thumb up'),),// An icon button is created using the icon constructor
RaisedButton.icon(
icon: Icon(Icons.send),
label: Text("Send"),
onPressed: () => print('send'),
),
FlatButton.icon(
icon: Icon(Icons.live_help),
label: Text("Doubt"),
onPressed: () => print('doubt'() [() [() (). }}Copy the code
Some buttons are defined by the icon constructor by default, and the same constructor can easily create a button with an icon, such as RaisedButton, etc
Customize the appearance of buttons
The appearance of a button can be defined by properties. Different button properties are almost the same. Take FlatButton as an example, take a look at the commonly used button properties and see the API for details
const FlatButton({
...
@required this.onPressed, // Button click callback
this.textColor, // Button text color
this.disabledTextColor, // Text color when the button is disabled
this.color, // Button background color
this.disabledColor,// The background color of the button when disabled
this.highlightColor, // The background color when the button is pressed
this.splashColor, // When clicked, the color of the wave in the wave animation
this.colorBrightness,// Button theme, default is light color theme
this.padding, // Button padding
this.shape, / / shape
@required this.child, // The contents of the button
})
Copy the code
Chestnut: Define a submit button
FlatButton(
color: Colors.blue,
child: Text("Submit"),
splashColor: Colors.grey,
highlightColor: Colors.red,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
onPressed: () => print('submit'),Copy the code
There is no setting to remove the background in Flutter. If you want to remove the background, you can do this by setting the background color to transparent. Replace color: color. blue with color: color (0x000000)
FlatButton has no shadow, so it always feels a little bit worse. If you need a shadow, just use RaisedButton
const RaisedButton({
...
this.elevation = 2.0.// Shadow in normal state
this.highlightElevation = 8.0.// Shadow when pressed
this.disabledElevation = 0.0.// Shadow when disabled. }Copy the code
Elevation, which is used to control shadows, is found in many components
The picture
In Flutter, images can be loaded and displayed via the Image component. The loading sources of images can be asset, file, memory, or network
-
ImageProvider
ImageProvider is an abstract class that defines the interface for retrieving images, load. Retrieving images from different data sources requires implementing different ImageProviders. For example, AssetImage implements the ImageProvider for loading images from Asset. NetWorkImage S implements the ImageProvider to load images from the network.
-
Image
The Image Widget has a mandatory parameter that corresponds to an ImageProvider
-
Loading pictures
Load images and other resources refer to this article
1. Load the pictures in Assets
Image( image: AssetImage("images/avatar.png"), width: 100.0 ); Image.asset("images/avatar.png", width: 100.0.)Copy the code
2. Load network images
Image( image: NetworkImage( "https://avatars2.githubusercontent.com/u/20411648?s=460&v=4"), width: 100.0, ) Image.network( "https://avatars2.githubusercontent.com/u/20411648?s=460&v=4", width: 100.0.)Copy the code
3. Load the local image file
Image.file Copy the code
4. Load the memory image
Image.memory Copy the code
-
parameter
const Image({ ... this.width, // Width of the image this.height, // Image height this.color, // The mixed color value of the image this.colorBlendMode, // Mix mode this.fit,// Zoom mode this.alignment = Alignment.center, // Alignment this.repeat = ImageRepeat.noRepeat, // Repeat mode. })Copy the code
-
Width, height: Sets the width and height of the image. If not specified, the image will display its original size as much as possible according to the limits of the current parent container. If only one of the images is set, the other one will be scaled, but you can use the fit property to fit the rules
-
Fit: Used to specify the fit mode of an image when the display space of the image differs from the size of the image itself
fit nature BoxFit.fill Fill, ignoring the original aspect ratio, until filled BoxFit.contain Contain, do not change the original scale of the container to contain the entire image, the rest of the container to fill the background BoxFit.cover Overwrite, do not change the original proportion, let the picture fill the whole container, the redundant part of the picture cut BoxFit.fitWidth Image landscape fill BoxFit.fitHeight Image portrait fill BoxFit.none There is no style, the original size is centered, and if the image is larger than the display space, only the middle part of the image will be displayed BoxFit.scaleDown Contain none if the size of an image is smaller than its container -
Color and colorBlendMode: you can mix the colors of each pixel when drawing the picture. Color specifies the blend color and colorBlenMode specifies the blend mode
Image( image:NetworkImage("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.houpao.com%2Fdy%2Fdyrs%2F20200711%2F94fb713a5985aa0c0c6f29a20 357880 f.jpeg&refer=http%3a%2f%2fimg.houpao.com & app = 2002 & size = f9999, 10000 & q = a80 & n = 0 & g = 0 n & FMT = jpeg? = 1614828035 & t = cf5430 the SEC f8cc9a51b7000cde9c9cc30b5a"), width: 500, height: 300, fit: BoxFit.cover, color: Colors.red, colorBlendMode: BlendMode.difference, ) Copy the code
-
Repeat: Specifies a repeat rule for the image when the image itself is smaller than the display space
-
-
The Image cache
The Flutter framework has a cache (memory) for loading images. By default, the maximum cache size is 1000 and the maximum cache space is 100M
-
Commonly used picture components
-
CircleAvatar
CircleAvatar( backgroundImage:NetworkImage("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.houpao.com%2Fdy%2Fdyrs%2F20200711%2F94fb713a5985aa0c0c6f29a20 357880 f.jpeg&refer=http%3a%2f%2fimg.houpao.com & app = 2002 & size = f9999, 10000 & q = a80 & n = 0 & g = 0 n & FMT = jpeg? = 1614828035 & t = cf5430 the SEC f8cc9a51b7000cde9c9cc30b5a"), radius: 50,),Copy the code
Circular images
-
FadeInImage
FadeInImage( image: NetworkImage("https://gimg2.baidu.com/i......."), placeholder: AssetImage("images/icon.png"),Copy the code
Loading images directly from the network and then displaying them would be a bit awkward. Using FadeInImage will display a placeholder during the image loading process and fade in after the image is loaded
-
ICON
In Flutter, font ICONS can be used directly. They are made into font files and created into different images by specifying different characters
In font files, each character corresponds to a code, and each code corresponds to a display glyph. Different fonts mean different glyph, and characters correspond to different glyph. Iconfont, on the other hand, just makes the glyphs corresponding to the bitcodes into ICONS, so different characters end up rendering different ICONS
Iconfont has the following advantages over images in Flutter
1. Small volume
2, vector icon, magnification will not affect the clarity
3, you can apply text style, you can change font icon color, size alignment and so on like text
4. You can mix TextSpan with text
Use the Material Design font icon
Flutter by default contains a library of Font ICONS for Material Design, as configured in the pubspec.yaml file
flutter:
uses-material-design: true
Copy the code
Look at a simple chestnut
String icons = "";
icons += "\uE814";
icons += " \uE200";
icons += " \uE80D";
Text(
icons,
style: TextStyle(
fontFamily: "MaterialIcons", fontSize: 40, color: Colors.green),
)
Copy the code
As can be seen above, ICONS are used just like text, but this requires a code point for each Icon, which is not developer-friendly. Therefore, Flutter encapsulates IconData and Icon to display font ICONS
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.accessible, color: Colors.green),
Icon(Icons.error, color: Colors.green),
Icon(Icons.fingerprint, color: Colors.green)
],
)
Copy the code
Use custom icon libraries
1. Import the TTF file
fonts:
- family: myIcon # specify a font name
fonts:
- asset: fonts/iconfont.ttf
Copy the code
2. Custom icon class, which functions like the Icons above, defining all the files in the font file as static variables
class MyIcons{
static const IconData book = const IconData(
0xe614,
fontFamily: 'myIcon',
matchTextDirection: true
);
static const IconData wechat = const IconData(
0xec7d,
fontFamily: 'myIcon',
matchTextDirection: true
);
}
Copy the code
3, the use of
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(MyIcons.book,color: Colors.purple,),
Icon(MyIcons.wechat,color: Colors.green,),
],
)
Copy the code
Radio buttons and check boxes
The Material component library provides radio switches and checkboxes, which themselves are inherited from StatefulWidget. They do not store the current selection state themselves. The selected state is managed by the parent component.
When the Switch or CheckBox is clicked, the onChanged callback is triggered, and we can change the logic in the callback
class SwitchAndCheckboxTest extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return_SwitchAndCheckboxTest(); }}class _SwitchAndCheckboxTest extends State<SwitchAndCheckboxTest> {
bool _switchSelected = true; // Single mode
bool _checkboxSelected = true; // Check box status
var groupValue = 0; // The default value is selected
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Radio switches and check boxes"),
),
body: Column(
children: [
Switch(
value: _switchSelected,
activeColor: Colors.red,
onChanged: (value) {
setState(() {
_switchSelected = value;
});
},
),
Checkbox(
value: _checkboxSelected,
activeColor: Colors.red,
onChanged: (value) => setState(() => _checkboxSelected = value),
),
Row(
children: [
Radio(
activeColor: Colors.red,
// The value bound by this single box
value: 0.// Click the state change callback
onChanged: (value) => setState(() => this.groupValue = value),
// The selected value in the current component
groupValue: groupValue,
),
Radio(
// The value bound by this single box
activeColor: Colors.red,
value: 1.// Click the state change callback
onChanged: (value) => setState(() => this.groupValue = value),
// The selected value in the current component
groupValue: groupValue,
),
Radio(
activeColor: Colors.red,
// The value bound by this single box
value: 2.// Click the state change callback
onChanged: (value) => setState(() => this.groupValue = value),
// The selected value in the current componentgroupValue: groupValue, ) ], ) ], ), ); }}Copy the code
In the above code, you need to maintain the state of the component, so inherit from the StatefulWidget. In build, you build the checkBox and Switch and Radio, modify the state when clicked, and then rebuild the UI
attribute
- The common property activeColor sets the color of the active state
- Width height: The Checkbox cannot be customized, and the Switch can only be customized
- Checkbox has a tristate attribute that indicates whether the Checkbox is tristate. The default value is false. If true, valude is automatically added to the value of null
conclusion
Switch, Checkbox, and Radio do not maintain state themselves, but require the parent component to manage state. When the user clicks, the parent component is notified of the state through events, so whether or not the user data is selected is associated with the user data, and the user data is not their private state. Therefore, we should consider which approach makes the most sense when customizing components
Input fields and forms
The Material component library provides the input field component TextField and the form component From, which we’ll look at in detail
TextField
For text input, it provides a number of attributes, but first let’s take a quick look at the key attributes
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
-
Controller: The controller of the edit box, through which you can set/obtain the content of the edit box, select the content of the edit box, and listen to the text change event of the edit box. In most cases we need to explicitly provide a Controller to interact with the TextField; if not, TextField will automatically create one
-
FocusNode: Controls whether the TextField takes focus of the current keyboard input. It is a handle (handler) for our interaction with the keyboard.
-
InputDecoration: Used to control the appearance of the TextField, such as prompt text, background color, border, etc
-
KeyboardType: sets the keyboard input type of the input box. The value can be:
TextInputType enumerated values meaning text Text input keyboard multiline Multi-line text, used with maxLines (set to null or greater than 1) number Digital; A numeric keypad will pop up phone Optimized phone number input keyboard; Numeric keypad will pop up and display “* #” datetime Optimized date input keyboard; Android will display “: -“ emailAddress Optimized email address; Will display “@.” url Optimized URL input keyboard; Will display “/.” -
TextInputAction: keyboard action button icon, it is an enumerated value, there are multiple optional values, the specific can be viewed API
-
Style: The style of the text being edited
-
TextAlign: Horizontal alignment of edited text in an input box
-
Autofocus: Indicates whether to automatically obtain the focus.
-
ObscureText: Whether to hide the text being edited, such as entering a password.
-
MaxLines: Indicates the maximum number of input lines. The default value is 1. If the value is null, the value is unlimited maxLength and maxLengthEnforced. The latter determines whether to block input if its length exceeds maxLength
-
OnChange: Callback to change the content of the input box, also monitored by controller
-
OnEditingComplete and onSubmitted: Both of these are triggered when typing is complete, such as keyboard completion, or search, etc. The difference is that the latter callback is ValueChanged
and the former takes no arguments
-
InputFormatters: Specifies the input format. When the input changes, it is validated against the specified format
-
Enable: If the value is false, the input box is disabled
-
CursorWidth, cursorRadius, and cursorColor: defines the cursorWidth, rounded corners, and color
chestnuts
class InputText extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return_InputText(); }}class _InputText extends State<InputText> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Input field"),
),
body: Column(
children: [
TextField(
autocorrect: true,
decoration: InputDecoration(
labelText: "Username",
hintText: "Username or email address",
prefixIcon: Icon(Icons.person)),
),
TextField(
decoration: InputDecoration(
labelText: "Password",
hintText: "Your login password",
prefixIcon: Icon(Icons.lock)),
obscureText: true() [(), (); }}Copy the code
-
Get input
1, define two variables, then save when onChange is triggered
2. Obtain the IP address from controler
// Define a controller TextEditingController _nameController = TextEditingController(); Copy the code
TextField( autofocus: true, controller: _nameController, / / set the controller...).Copy the code
// You can print it directly print(_nameController.text) Copy the code
Controller can also be used to set default values, select text, and so on
_nameController.text="hello world!"; _nameController.selection=TextSelection( baseOffset: 2, extentOffset: _nameController.text.length ); TextField( controller: _nameController, ) Copy the code
-
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.
-
Simple focus state change events
/ / create focusNode FocusNode focusNode = newFocusNode(); .// focusNode binds the input boxTextField(focusNode: focusNode); .// Listen for focus changes focusNode.addListener((){ print(focusNode.hasFocus); }); Copy the code
-
Custom styles
-
Hidden text
TextField( obscureText: true.)Copy the code
After hiding, the input content will not be visible and become the password type
-
The keyboard type
TextField( keyboardType: TextInputType.number, ), Copy the code
For example, “number” can only be entered as a number, and there are many other values, such as the following, which you can check for yourself
-
The keyboard button
That is, the lower right corner of the keyboard button, common such as finish, is a tick button, etc
-
case
Control the case of English letters, such as uppercase letters, etc
TextField( textCapitalization: TextCapitalization.words, ), Copy the code
TextCapitalization options
1, Words: capitalize the first letter of a word
2. Capitalize the first letter of the sentence
3, characters: capitalize all letters
4, None: default none
-
The cursor
TextField( cursorColor: Colors.orange,/ / color cursorWidth: 15./ / width cursorRadius: Radius.circular(15),/ / the rounded ), Copy the code
-
counter
TextField( maxLength: 11, ), Copy the code
Set the maximum length counter to display
Custom counters/ICONS
TextField( autocorrect: true, maxLength: 11, controller: _nameController, decoration: InputDecoration( labelText: "Username", hintText: "Username or email address", counter: Text("Counter 0/100"), prefixIcon: Icon(Icons.person)), ), Copy the code
Implement custom counters through counter
PrefixIcon allows you to set the left inner icon, and icon allows you to set the left outer icon
decoration: InputDecoration( suffixIcon: IconButton( icon: Icon(Icons.close), onPressed: () { controller.clear(); },),Copy the code
SuffixIcon allows you to set the inside icon on the right and click events
-
Error text prompt
TextField( controller: controller, decoration: InputDecoration( errorText: "Please enter the content",),),Copy the code
-
Remove underline
decoration: InputDecoration.collapsed(hintText: "Username or email address")), Copy the code
-
A border
decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(15)),/ / the rounded borderSide: BorderSide(color: Colors.red, width: 2.0)),// Color, width ), Copy the code
The color is theme color, //TODO Settings do not take effect, will be resolved later
-
Form the Form
In practice, Flutter validates the data in the input field before making a request to the interface. It would be cumbersome to validate each TextField. To this end, Flutter provides a Form component that groups the input fields together and performs operations such as content validation, resetting, and saving
Form inherits from the StatefulWidget class and corresponds to a FormState, defined as follows:
Form({
@required Widget child,
bool autovalidate = false,
WillPopCallback onWillPop,
VoidCallback onChanged,
})
Copy the code
- Autovalidate: Indicates whether the input is automatically validated. If this parameter is true, each FormField automatically validates the input and displays an error message. Otherwise, pass
FormState.validate()
To check manually - OnWillPop: decision
Form
If the Future is false, the current route will not return. If true, the previous route will return. This property is usually used to block buttons - OnChange: Any word of Form
FormField
This callback is triggered whenever the content changes
FormField
FormField is an abstract class with several attributes through which FormState performs operations. The FormField section is defined as follows:
const FormField({
...
FormFieldSetter<T> onSaved, // Save the callback
FormFieldValidator<T> validator, // Validate the callback
T initialValue, / / initial value
bool autovalidate = false.// Whether to check automatically.
})
Copy the code
For ease of use, Flutter provides a TextFormField component that inherits from the FormField class and is also a wrapper class, so it also includes the properties of TextField in addition to the FormField
FormState
FormState is the Form’s State class, which can be obtained via form.of () or Globalkey, which we can use to operate on FormField, the Form’s descendant.
- Formstate.validate () : this method calls the 1 validate callback of the Form descendant FormFile, which returns false if one of the validations fails and an error message if any validations fail
- Formstate.save () : This method calls the save callback of the Form descendant FormField to save the Form content
- Formsata.reset () : This method clears the descendant FormField
chestnuts
class InputText extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return_InputText(); }}class _InputText extends State<InputText> {
// Define a controller
TextEditingController _nameController = TextEditingController();
GlobalKey _formKey = new GlobalKey<FormState>();
@override
void initState() {
super.initState();
_nameController.addListener(() => print("Account number:" + _nameController.text));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Input field"),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
child: Form(
key: _formKey,// Set globalKey to get FormState later
autovalidate: true.// Enable automatic verification
child: Column(
children: [
TextFormField(
autocorrect: true,
maxLength: 11,
controller: _nameController,
decoration: InputDecoration.collapsed(hintText: "Username or email address"),
validator: (v) {
return v.trim().length > 0 ? null : "User name cannot be empty";
},
),
TextFormField(
decoration: InputDecoration.collapsed(hintText: "Your login password"),
validator: (v) {
return v.trim().length > 5 ? null : "Password must be no less than six characters.";
},
),
Padding(
padding: const EdgeInsets.only(top: 28),
child: Row(
children: [
Expanded(
child: RaisedButton(
padding: EdgeInsets.all(15),
child: Text("Login"),
color: Theme.of(context).primaryColor,
textColor: Colors.white,
onPressed: () {
// Get formState, call validate,
if ((_formKey.currentState as FormState)
.validate()) {
print('Verification successful'); }},),),),),),),)); }}Copy the code
Form. Of (context) cannot be retrieved in the login button onPressed method because the context is the InputText context**, Form.of(context) looks at the root of the specified context, and FormState is in the InputText subtree, so it doesn’t work. 支那
The correct way to build the login button is through the Builder, which takes the widget node’s context as a callback argument:
Expanded(
child: Builder(builder: (context) {
return RaisedButton(
padding: EdgeInsets.all(15),
child: Text("Login"),
color: Theme.of(context).primaryColor,
textColor: Colors.white,
onPressed: () {
// Get formState, call validate,
if ((Form.of(context)).validate()) {
print('Verification successful'); }}); }),)Copy the code
Use this method
Refer to self Flutter actual combat