Probe into Flutter State Management
Recently, I learned a cross-platform solution based on an APP that wants to implement a collection of commonly used personal functions. Then began to study Flutter, through the article to record the learning gains.
The main content of this article is to introduce the way of state management in Flutter and the choice of different ways.
This article was originally written in Chinese and translated into English by a translation software without proofreading. So please understand the possible inaccuracies and rigor in the article.
State management
There are three common state management methods, which are managed by the Widget itself, managed by the parent Widget and mixedly managed by the Widget itself and the parent Widget.
When analyzing three common state management methods, we defined three Tapboxes, each of which can indicate activation and inactivation. This Tapbox is a stateful Widget.
Managed by Widget itself
//------------------------- TapboxA ----------------------------------
class TapboxA extends StatefulWidget {
TapboxA({Key key}) : super(key: key);
@override
_TapboxAState createState() => _TapboxAState();
}
class _TapboxAState extends State<TapboxA> {
bool _active = false;
void _handleTap() {
setState(() {
_active = !_active;
});
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
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],
),
),
);
}
}
The state management by the Widget itself is very simple, and the state management by TapboxA’s state class. Use _active
to determine the current value and color and use _handleTap
to change its value.
Managed by parent Widget
//------------------------ ParentWidget --------------------------------
class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
//------------------------- TapboxB ----------------------------------
class TapboxB extends StatelessWidget {
TapboxB({Key key, this.active: false, @required this.onChanged})
: super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
void _handleTap() {
onChanged(!active);
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
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],
),
),
);
}
}
Mainly managed by the parent Widget are:
The parent widget manages TapboxB’s
_active
state.Implement
_handleTapboxChanged ()
in the parent Widget’s status class.
The main features of sub-Widgets are:
Inherit the
StatelessWidget
class because its state is managed by the parent Widget.Notify the parent Widget when it is clicked instead of managing its own status.
Mixed management by Widget itself and parent Widget
//---------------------------- ParentWidget ----------------------------
class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: TapboxC(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
//----------------------------- TapboxC ------------------------------
class TapboxC extends StatefulWidget {
TapboxC({Key key, this.active: false, @required this.onChanged})
: super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
_TapboxCState createState() => _TapboxCState();
}
class _TapboxCState extends State<TapboxC> {
bool _highlight = false;
void _handleTapDown(TapDownDetails details) {
setState(() {
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
setState(() {
_highlight = false;
});
}
void _handleTapCancel() {
setState(() {
_highlight = false;
});
}
void _handleTap() {
widget.onChanged(!widget.active);
}
Widget build(BuildContext context) {
// This example adds a green border on tap down.
// On tap up, the square changes to the opposite state.
return GestureDetector(
onTapDown: _handleTapDown, // Handle the tap events in the order that
onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
onTap: _handleTap,
onTapCancel: _handleTapCancel,
child: Container(
child: Center(
child: Text(widget.active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white)),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color:
widget.active ? Colors.lightGreen[700] : Colors.grey[600],
border: _highlight
? Border.all(
color: Colors.teal[700],
width: 10.0,
)
: null,
),
),
);
}
}
It can be seen that this model only combines the first two models. In the example, the state of adding a border on the edge of the Tapbox is increased by _highlight
, which is managed by the Widget itself, while the _active
state is managed by the parent Widget.
Choice of state management methods
The selection of state management methods can be summarized in the following ways:
If the state in question is user data, such as the checked or unchecked mode of the check box, or the position of the slider, it is best to manage the state by the parent widget.
If the state in question is a state such as animation, it is better to manage the state by the Widget itself.
This post was originally published on my personal blog wangchucheng.com.
Original link: https://wangchucheng.com/en/posts/flutter-state-management/
Posts in this blog are original unless otherwise stated, and are licensed under a CC BY-NC-SA 4.0 License. Use beyond the lincense please contact the author for authorization.