flutter3.0学习笔记

状态(State)管理

Preview
  • 状态管理的常见方法
  • Widget 管理自己的状态
  • 父Widget管理子Widget的状态
  • 混合状态管理

状态管理的常见方法

管理状态的最常见的方法有: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),
          ),
        ),
      ),
    );
  }
}