• Flutter 基础组件:状态管理


    前言

    一个永恒的主题,“状态(State)管理”,无论是在React/Vue(两者都是支持响应式编程的Web开发框架)还是Flutter中,他们讨论的问题和解决的思想都是一致的。
    一个问题,StatefulWidget的状态应该被谁管理?Widget本身?父Widget?都会?还是另一个对象?答案是取决于实际情况!以下是管理状态的最常见的方法

    1. Widget管理自己的状态。
    2. Widget管理子Widget状态。
    3. 混合管理(父Widget和子Widget都管理状态)。

    如何决定使用哪种管理方法?下面是官方给出的一些原则

    1. 如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父Widget管理。
    2. 如果状态是有关界面外观效果的,例如颜色、动画,那么状态最好由Widget本身来管理。
    3. 如果某一个状态是不同Widget共享的则最好由它们共同的父Widget管理。

    三个例子

    下面通过三个例子来分别说明这三种管理方式。

    Widget管理自身状态

    _TapBoxAState 类:

    • TapBoxA。
    • 定义_active:确定盒子的当前颜色的布尔值。
    • 定义_handleTap()函数,该函数在点击该盒子时更新_active,并调用setState()更新UI。
    • 实现widget的所有交互式行为。
    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) {
        // 使用GestureDetector来识别点击事件
        return GestureDetector(
          onTap: _handleTap,
          child: Container(
            child: Center(
              child: Text(
                _active ? 'Active' : 'Inactive',
                style: TextStyle(fontSize: 32.0, color: Colors.white),
              ),
            ),
             200.0,
            height: 200.0,
            decoration: BoxDecoration(
              color: _active ? Colors.lightGreen[700] : Colors.grey[700],
            ),
          ),
        );
      }
    }
    
    

    父Widget管理子Widget的状态

    对于父Widget来说,管理状态并告诉其子Widget何时更新通常是比较好的方式。
    例如,IconButton是一个图标按钮,但它是一个无状态的Widget,因为我们认为父Widget需要知道该按钮是否被点击来采取相应的处理。

    ParentWidgetState 类:

    1. 为TapBoxB 管理_active状态。
    2. 实现_handleTaPBoxChanged(),当盒子被点击时调用的方法。
    3. 当状态改变时,调用setState()更新UI。
      TapBoxB 类:
    4. 继承StatelessWidget类,因为所有状态都由其父组件处理。
    5. 当检测到点击时,它会通知父组件。

    TaPBoxB通过回调将其状态导出到其父组件,状态由父组件管理,因此它的父组件为StatefulWidget。
    但是由于TapBoxB不管理任何状态,所以TapBoxB为StatelessWidget。

    class ParentWidgetB extends StatefulWidget {
      @override
      _ParentWidgetBState createState() => _ParentWidgetBState();
    }
    
    class _ParentWidgetBState extends State<ParentWidgetB> {
      bool _active = false;
    
      void _handleTaPBoxChanged(bool newValue) {
        setState(() {
          _active = newValue;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: TapBoxB(
            active: _active,
            onChanged: _handleTaPBoxChanged,
          ),
        );
      }
    
    }
    
    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);
      }
    
      @override
      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),
              ),
            ),
             200.0,
            height: 200.0,
            decoration: BoxDecoration(
              color: active ? Colors.lightGreen[400] : Colors.grey[400],
            ),
          ),
        );
      }
    }
    
    

    混合状态管理

    TapBoxC示例中,手指按下时,盒子的周围会出现一个深绿色的边框,抬起时,边框消失。
    点击完成后,盒子的颜色改变。TapBoxC将其_active状态导出到其父组件中,但在内部管理其_highlight状态。
    这个例子有两个状态对象_ParentWidgetState和_TaPBoxCState。

    _ParentWidgetStateC类:

    1. 管理_active 状态。
    2. 实现 _handleTaPBoxChanged() ,当盒子被点击时调用。
    3. 当点击盒子并且_active状态改变时调用setState()更新UI。

    _TapBoxCState 对象:

    1. 管理_highlight 状态。
    2. GestureDetector监听所有tap事件。当用户点下时,它添加高亮(深绿色边框);当用户释放时,会移除高亮。
    3. 当按下、抬起、或者取消点击时更新_highlight状态,调用setState()更新UI。
    4. 当点击时,将状态的改变传递给父组件。
    class ParentWidgetC extends StatefulWidget {
      @override
      _ParentWidgetCState createState() => _ParentWidgetCState();
    }
    
    class _ParentWidgetCState extends State<ParentWidgetC> {
      bool _active = false;
    
      void _handleTapBoxChanged(bool newValue) {
        setState(() {
          _active = newValue;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: TapBoxC(active: _active, onChanged: _handleTapBoxChanged,),
        );
      }
    }
    
    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);
      }
    
      @override
      Widget build(BuildContext context) {
        // 在按下时添加绿色边框,当抬起时,取消高亮
        return GestureDetector(
          onTapDown: _handleTapDown,  // 处理按下事件
          onTapUp: _handleTapUp,  // 处理抬起事件
          onTap: _handleTap,
          onTapCancel: _handleTapCancel,
          child: Container(
            child: Center(
              child: Text(
                  widget.active ? 'Active' : 'Inactive',
                  style: TextStyle(fontSize: 32.0, color: Colors.white),
              ),
            ),
             200.0,
            height: 200.0,
            decoration: BoxDecoration(
              color: widget.active ? Colors.lightGreen[200] : Colors.grey[200],
              border: _highlight ? Border.all(color: Colors.teal[700],  10.0,) : null,
            ),
          ),
        );
      }
    }
    
    

    全局状态管理

    当应用中需要一些跨组件(包括跨路由)的状态需要同步时,上面三种管理方式就很难胜任了。
    有一个设置页,里面可以设置应用的语言,为了让设置实时生效,期望在语言状态发生改变时,APP中依赖应用语言的组件能够重新build一下,但这些依赖应用语言的组件和设置页并不在一起。
    而全局状态管理则可以处理这种相距较远的组件之间的通信,目前有两种方法:

    1. 实现一个全局的事件总线,将语言状态改变为一个事件,然后在App中依赖应用语言的组件的initState方法中订阅语言改变的事件。
      当用户设置页切换语言后,发布语言改变事件,而订阅了此事件的组件就会收到通知,收到通知后调用setState()重新build一下即可。
    2. 使用一些专门用于状态管理的包,如Provider、Redux等。
  • 相关阅读:
    msyql 授权ip
    discuz和ecmail最简单解决同步登陆登出
    mysqldump大数据的备份与恢复
    PHP-OB缓存
    jquery对象和dom对象
    Javascript面向对象编程-深入浅出讲的非常棒
    修改mysql密码
    phpcms 详情页没有点击数的问题
    phpcms pc标签调用整理
    设置devenv.exe启动版本(转)
  • 原文地址:https://www.cnblogs.com/parzulpan/p/12058265.html
Copyright © 2020-2023  润新知