The official documentation
Stateful & stateless
Flutter contains two types of widgets: a StatefulWidget and a StatelessWidget. For example, a Checkbox is a StatefulWidget (a widget with mutable state), and a Text widget is a StatelessWidget (a widget that doesn’t need mutable state).
When we customize the StatefulWidget, we override the createState() method to create a State object. In State, we can use setState((){… }); Method to change the state of the current Widget, whereas the StatelessWidget does not.
You can create an instance of a StatefulWidget as follows:
class FavoriteWidget extends StatefulWidget {
// Create a State object
@override
State<StatefulWidget> createState() {
return_FavoriteWidgetState(); }}// Used to manage the status of FavoriteWidget
class _FavoriteWidgetState extends State<FavoriteWidget> {
bool _isFavorited = true;
void _toggleFavorite() {
// Change the statesetState(() { _isFavorited = ! _isFavorited }); }@override
Widget build(BuildContext context) {
return MaterialApp(
// You can change the status of some widgets according to _isFavorited
...
);
}
}
Copy the code
In Dart, a member variable or class name begins with an underscore to indicate that the member or class is private. The official documentation
Manage the state of widgets
Flutter provides several ways to manage the state of widgets
widget
The state is managed by itself- By the parent
widget
managementwidget
The state of the - Mixed management
widget
State (part managed by itself and part managed by the parent)
The widget state is managed by itself
This one is simpler and manages its own state directly within the widget
// Widgets with mutable state
class TapboxA extends StatefulWidget {
TapboxA({Key key}) : super(key: key) {
print('TapboxA init');
}
// Create State to manage the State of the current widget
@override
_TapboxAState createState() => _TapboxAState();
}
// State
class _TapboxAState extends State<TapboxA> {
_TapboxAState() {
print('boxA state init');
}
bool _active = false;
void _handleTap() {
print('boxA state handleTap is call');
// Set the statesetState(() { _active = ! _active; }); } Widget build(BuildContext context) {print('boxA state build is call');
return GestureDetector(
onTap: _handleTap, // Click the event
child: Container(
child: Center(
// Set different Text values according to _active
child: Text(
_active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: _active ? Colors.lightGreen[700] : Colors.grey[600],),),); }}//------------------------- MyApp ----------------------------------
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'), ), body: Center( child: TapboxA(), ), ), ); }}Copy the code
The effect is as follows:
Initialization sequence:
flutter: TapboxA init
flutter: boxA state init
flutter: boxA state build is call
Copy the code
When the click event is triggered:
flutter: boxA state handleTap is call
flutter: boxA state build is call
Copy the code
Managed by the parent widget
In this case, TapboxB is a StatelessWidget and ParentWidget is a StatefulWidget.
We need to use ParentWidget to change the state of TapboxB.
//------------------------- parent widget ----------------------------------
class ParentWidget extends StatefulWidget {
ParentWidget() {
print('ParentWidget init');
}
@override
State<StatefulWidget> createState() {
return_ParentWidgetState(); }}class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
_ParentWidgetState() {
print('ParentWidgetState init');
}
void _handleTapboxChanged(bool newValue) {
print('parent _handleTapboxChanged is call : $newValue');
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
print('ParentWidgetState build is call');
return MaterialApp(
title: 'ParentWidget',
theme: ThemeData(
primaryColor: Colors.redAccent
),
home: Scaffold(
appBar: AppBar(
title: Text('ParentWidget'), ), body: Center( child: TapboxB(onChanged: _handleTapboxChanged, active: _active,), ), ), ); }}//------------------------- child widget ----------------------------------
class TapboxB extends StatelessWidget {
TapboxB({Key key, this.active: false.@required this.onChanged})
: super(key: key) {
print('Tap boxB init : The ${this.active}');
}
final bool active;
final ValueChanged<bool> onChanged;
void _handleTap() {
print('child _handleTap : $active'); onChanged(! active); }@override
Widget build(BuildContext context) {
print('Tap boxB build method');
return GestureDetector(
onTap: _handleTap,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600]
),
child: Center(
child: Text(active ? 'Android' : 'Flutter',
style: TextStyle(fontSize: 32.0, color: Colors.white),), ), ), ); }}Copy the code
As you can see, the TapboxB constructor has an onChanged parameter and an active parameter; The onChanged parameter is ValueChanged
which is a callback method that takes one parameter.
/// Signature for callbacks that report that an underlying value has changed.
///
/// See also [ValueSetter].
typedef void ValueChanged<T>(T value);
Copy the code
In TapboxB, this callback method is executed when the click event is triggered, which is the _handleTap() method in the code above. Active toggles the current TapboxB state.
Note: Both onChanged and Active are passed by the parent widget
The effect is as follows:
Then let’s look at the output:
Initialization output is as follows:
Performing hot restart...
flutter: ParentWidget init
Restarted app in 1.976ms.
flutter: ParentWidgetState init
flutter: ParentWidgetState build is call
flutter: Tap boxB init : false
flutter: Tap boxB build method
Copy the code
The execution sequence is as follows:
Let’s look at the output of the child widget (TapboxB) when the click event is triggered
flutter: child _handleTap : false
flutter: parent _handleTapboxChanged is call : true
flutter: ParentWidgetState build is call
flutter: Tap boxB init : true
flutter: Tap boxB build method
Copy the code
The execution sequence is as follows:
Note: When we trigger the click event on the child widget, the _handleTap() method is executed, and the _handleTap() method is executed onChanged(…). The parent callback method _handleTapboxChanged(…) is then executed. , pay attention to _handleTapboxChanged (…). SetState (() {… }), the state is switched in this method, and the build method is re-called to re-render the child.
setState(() {… }) causes the widget to be redrawn, similar to how the Invalidate () method of the View is called in Android
Hybrid management of widget state
Hybrid management means that some states are managed by themselves and some states are managed by the parent.
The following example is an example of mixed state management. The widget TabboxC has three state transitions when clicked: background color, text, and border.
In the example, the background color and text state is managed by the parent widget (similar to the previous example), while the border state is managed by itself.
Since both parent and child widgets can manage state, they inherit from the StatefulWidget class.
// ------------parent widget-----------
class ParentWidget2 extends StatefulWidget {
ParentWidget2() {
print('Parent init');
}
@override
State<StatefulWidget> createState() {
return_ParentWidgetState2(); }}class _ParentWidgetState2 extends State<ParentWidget2> {
_ParentWidgetState2() {
print('_Parent State init');
}
bool _active = false;
void _handleTapboxChanged(bool newValue) {
print('_Parent _handleTapboxChanged method is called');
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
print('_Parent State build is called');
returnTabboxC(onChanged: _handleTapboxChanged, active: _active,); }}// ------------child widget-----------
class TabboxC extends StatefulWidget {
// constructor
TabboxC({
Key key,
this.active: false.@required this.onChanged
}) : super(key: key) {
print('TabboxC init');
}
final bool active;
final ValueChanged<bool> onChanged;
@override
State<StatefulWidget> createState() {
return_TapboxCState(); }}class _TapboxCState extends State<TabboxC> {
bool _highlight = false;
_TapboxCState() {
print('_TapboxC State init');
}
void _handleTapDown(TapDownDetails details) {
print('_TapboxC tap down');
setState(() {
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
print('_TapboxC tap up');
setState(() {
_highlight = false;
});
}
void _handleTapCancel() {
print('_TapboxC tap cancel');
setState(() {
_highlight = false;
});
}
void _handleTap() {
print('_TapboxC tap clicked'); widget.onChanged(! widget.active); }@override
Widget build(BuildContext context) {
print('_TapboxCState build is called');
return MaterialApp(
title: 'mix',
theme: ThemeData(
primaryColor: Colors.redAccent
),
home: Scaffold(
appBar: AppBar(
title: Text('mix'),
),
body: Center(
child: GestureDetector(
// down
onTapDown: _handleTapDown,
// up
onTapUp: _handleTapUp,
// cancel
onTapCancel: _handleTapCancel,
// click
onTap: _handleTap,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
// Box color parent control (via callback method)
color: widget.active ? Colors.lightGreen[700] : Colors
.grey[600].// Control the border color
border: _highlight ? Border.all(
color: Colors.teal[700], width: 10.0) : null
),
child: Center(
child: Text(widget.active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),), ), ), ), ) ), ); }}Copy the code
The effect is as follows:
The initialization sequence is similar. Let’s look at the order in which the click event is triggered:
flutter: _TapboxC tap down
flutter: _TapboxCState build is call
flutter: _TapboxC tap up
flutter: _TapboxC tap clicked
flutter: _Parent _handleTapboxChanged method is call
flutter: _Parent State build is call
flutter: TabboxC init
flutter: _TapboxCState build is call
Copy the code
The execution process is as follows:
You may notice that the child widget calls setState(…) in the Down event. Method, and then performs a build operation; SetState (…) is also called in the Up event. Method, but why the click operation was performed instead of the build operation. The onClick method is also executed in ACTION_UP in the View’s onTouchEvent method.
If there are any mistakes, please also point out, thank you!