状态管理的常见方法
管理状态的最常见的方法有:Widget
管理自己的状态,Widget
管理子 Widget
状态,混合管理(父 Widget
和子 Widget
都管理状态)。
Widget
管理自己的状态
如果状态是有关界面外观效果的,例如颜色、动画,那么状态最好由 Widget 本身来管理。用之前的有状态组件列子改造:
_StatefulElementState
类:
- 管理
StatefulElement
的状态。 - 定义
_active
:确定盒子的当前颜色的布尔值。 - 该函数在点击该盒子时更新
_active
,并调用setState()
更新UI。 - 实现
widget
的所有交互式行为。
//StatefulElement管理自身状态
class StatefulElement extends StatefulWidget{
const StatefulElement({super.key});
@override
State<StatefulWidget> createState()=>_StatefulElementState();
}
class _StatefulElementState extends State<StatefulElement>{
bool _active = false;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: <Widget>[
GestureDetector(
onTap: (){
setState(() {
_active = !_active;
});
},
child: Container(
width: 250,
height: 250,
decoration: BoxDecoration(
color: _active?Colors.lightBlue:Colors.green[200],
),
child: Center(
child: Text(
_active?'Active':'Inactive',
style: const TextStyle(fontSize: 30.0,color: Colors.red),
),
),
),
)
],
),
),
),
);
}
}
父Widget管理子Widget的状态
对于父Widget
来说,管理状态并告诉其子Widget
何时更新通常是比较好的方式。 例如,IconButton
是一个图标按钮,但它是一个无状态的Widget
,因为我们认为父Widget
需要知道该按钮是否被点击来采取相应的处理。
如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父 Widget 管理
首先我们先来创建一个无状态组件 StatelessElementB
:
- 继承
StatelessWidget
类,因为所有状态都由其父组件处理。 - 当检测到点击时,它会通知父组件。
class StatelessElementB extends StatelessWidget {
const StatelessElementB({Key? key, required this.active, required this.onChanged}) : super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
onChanged(!active);
},
child: Container(
width: 250,
height: 250,
decoration: BoxDecoration(
color: active?Colors.lightBlue:Colors.green[200],
),
child: Center(
child: Text(
active?'Active':'Inactive',
style: const TextStyle(fontSize: 30.0,color: Colors.red),
),
),
),
);
}
}
_StatefulElementState
类:
- 为
StatelessElementB
管理_active
状态。 - 实现
_handleChanged()
,当盒子被点击时调用的方法。 - 当状态改变时,调用
setState()
更新UI。
class StatefulElement extends StatefulWidget{
const StatefulElement({super.key});
@override
State<StatefulWidget> createState()=>_StatefulElementState();
}
class _StatefulElementState extends State<StatefulElement>{
bool _active = false;
void _handleChanged(bool newValue){
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
padding: const EdgeInsets.all(20),
child: StatelessElementB(
active: _active,
onChanged: _handleChanged,
),
),
),
);
}
}
混合状态管理
对于一些组件来说,混合管理的方式会非常有用。在这种情况下,组件自身管理一些内部状态,而父组件管理一些其他外部状态。(父子都是有状态的)
如果某一个状态是不同 Widget 共享的则最好由它们共同的父 Widget 管理。
_StatefulElementState
类:
- 管理
_active
状态。 - 实现
_handleTapChanged()
,当盒子被点击时调用。 - 当点击盒子并且
_active
状态改变时调用setState()
更新UI。
_StatefulElementCState
对象:
- 管理
_highlight
状态。 GestureDetector
监听所有tap
事件。当用户点下时,它添加高亮(红色边框);当用户释放时,会移除高亮。- 当按下、抬起、或者取消点击时更新
_highlight
状态,调用setState()
更新UI。 - 当点击时,将状态的改变传递给父组件。
class StatefulElementC extends StatefulWidget {
const StatefulElementC({Key? key, required this.active, required this.onChanged}) : super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
@override
State<StatefulElementC> createState() => _StatefulElementCState();
}
class _StatefulElementCState extends State<StatefulElementC> {
bool _highlight = false;
void _handleTapDown(TapDownDetails details){
setState(() {
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details){
setState(() {
_highlight = false;
});
}
void _handleTapCancle(){
setState(() {
_highlight = false;
});
}
void _handleTap(){
widget.onChanged(!widget.active);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _handleTapDown,//按下事件
onTapUp: _handleTapUp,//抬起事件
onTap: _handleTap,
onTapCancel: _handleTapCancle,
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: widget.active? Colors.orange[200]:Colors.green[200],
border: _highlight ?Border.all(color: Colors.red,width: 10.0):null,
),
child: Center(
child: Text(
widget.active?'active':'Inactive',
style: const TextStyle(fontSize: 30,color: Colors.green),
),
),
),
);
}
}