Flutter | 状态管理

2022-02-11 14:29:22 浏览数 (1)

本文示例代码

概述

响应式的编程框架中都有一个永恒的主题 "状态管理",无论是在 React/Vue 还是在Flutter中,他们的问题和解决的思想都是一致的

额.....,响应式编程是啥意思????????

响应式编程,以下答案参考自百度百科: 响应式编程是一种面向数据流和变化传播的范式 在命令式编程中, a b = c ,表示 将表达式的结果赋值给 c,而之后改变 b 或者 c 不会影响到 A 在响应式编程中,c 的值会随着 a 或者 b 的值更新而更新

看到这里终于明白响应是编程是个啥玩意了

其实上面的例子中,a 和 b 指的就是状态,而 c 则代表的就是用户可以看到的,如界面等。

也就是说,当状态发生变化的时候,页面也会随之刷新,

个人理解:响应式编程解决的就是数据一致性的问题。保证在状态发生改变之后,可以立即同步到页面中;

Flutter 中的状态管理

在 Flutter 中,StatefulWidget 的状态应该被谁管理?

Widget 本身?父 Widget 还是别的对象来管理? 答案是取决于实际情况

以下是管理状态最常见的方法:

  • Widget 管理自己的状态 如果状态时有关界面外观效果的,例如颜色,动画,那么状态最好由 Widget本身来管理
  • Widget 管理子 Widget 的状态 如果状态是用户数据,如选中的状态,滑块的位置,则该状态最好由父 Widget 管理
  • 混合管理(父 Widget 和 子 Widget 都管理状态) 如果某一个状态是不同 Widget 共享的,则最后由他们共同的 父 Widget 管理

其实在 Widget 内部管理状态封装性会好一些,而在父 Widget 中管理会比较灵活。有些时候,如果不确定到底应该怎么管理,那么首选的应该是在父 Widget 中管理。

实践

  • Widget 管理自身的状态
代码语言:javascript复制
class _TapBoxAState extends State<TapBoxA> {
  bool _active = false;

  void _handleTap() {
    setState(() {
      _active = !_active;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        width: 200,
        height: 200,
        child: Center(
          child: Text(_active ? "Active" : "Inactive",
              style: TextStyle(fontSize: 32, color: Colors.white)),
        ),
        decoration:
            BoxDecoration(color: _active ? Colors.lightBlue : Colors.green),
      ),
    );
  }
}
  • 父 Widget 管理子 Widget 的状态 对于 父 Widget 来说,管理状态并告诉子Widget合适更新通常是比较好的方式。例如 IconButton 是一个图标按钮,他是他是一个无状态的 Widget,应为我们父 Widget 需要知道该按钮是否被点击来采取相应的处理 示例
代码语言:javascript复制
//------------------------ ParentWidget -----------------------
class ParentWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapBoxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: TapBoxB(
        active: _active,
        onChange: _handleTapBoxChanged,
      ),
    );
  }
}
//------------------------ TapBoxB -----------------------
class TapBoxB extends StatelessWidget {
  final bool active;
  final ValueChanged<bool> onChange;

  TapBoxB({Key key, this.active, this.onChange});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Container(
        width: 200,
        height: 200,
        child: Center(
          child: Text(active ? "Active" : "Inactive",
              style: TextStyle(fontSize: 32, color: Colors.white)),
        ),
        decoration:
            BoxDecoration(color: active ? Colors.lightBlue : Colors.green),
      ),
      onTap: () => onChange(!active),
    );
  }
}

上面栗子中,TapBoxB 通过回调将自己的状态传递到父组件,状态由父组件管理,因此它的父组件为 StatefullWidget ,但是由于 TapBoxB 本身不管理任何状态,所以是 StatelessWidget 每次 setState 的时候都会重新执行 build 方法,将状态传递到子组件,因此TabBoxB 不需要对状态进行管理,直接使用即可 执行效果和上图一样

  • 混合状态管理 对于一些组件来说,混合管理的方式会非常有用,组件自身管理一些内部的状态,而父组件管理一些其他的外部状态 示例:
代码语言:javascript复制
//------------------------ ParentWidget -----------------------
class ParentWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapBoxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: TapBoxC(
        active: _active,
        onChange: _handleTapBoxChanged,
      ),
    );
  }
}

//------------------------ TapBoxC -----------------------
class TapBoxC extends StatefulWidget {
  final bool active;
  final ValueChanged<bool> onChange;

  TapBoxC({Key key, this.active, this.onChange});

  @override
  State<StatefulWidget> createState() => _TapBoxCState();
}

class _TapBoxCState extends State<TapBoxC> {
  bool _highlight = false;

  void _handleTapDown(TapDownDetails detailis) {
    setState(() {
      _highlight = true;
    });
  }

  void _handleTapUp(TapUpDetails detailis) {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTapCancel() {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTap() {
    widget.onChange(!widget.active);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      onTapDown: _handleTapDown,
      //按下事件
      onTapUp: _handleTapUp,
      //抬起事件
      onTapCancel: _handleTapCancel,
      //取消事件
      child: Container(
        width: 200,
        height: 200,
        child: Center(
          child: Text(widget.active ? "Active" : "Inactive",
              style: TextStyle(fontSize: 32, color: Colors.white)),
        ),
        decoration: BoxDecoration(
            color: widget.active ? Colors.lightBlue : Colors.green,
            border: _highlight
                ? Border.all(color: Colors.teal[700], width: 10)
                : null),
      ),
    );
  }
}

效果如下:

手指按下的时候回出现一个边框,抬起时,边框消失,点击完成之后,组件的颜色改变 对于开发人员来说,只关心组件是否处于 Active 状态,而不会在意边框的具体实现,所以,我们将边框的状态隐藏在内部,对外只暴露 active 状态

  • 全局的状态管理 当应用中需要一些跨组件,路由的状态需要同步时,上面的几种方法便很难胜任了。比如,在设置页面修改应用的语言,为了让设置实时生效,我们期望在语言状态改变时,App 中依赖语言的组件可以重新 build 一下,但是这些依赖语言的组件并不在一起,所以这种情况使用上面这几种办法很难管理 这是正确的做法是通过一个全局的状态管理器来处理这种相距较远的组件之间通信,目前有两种解决办法: 1,实现一个全局的事件总线,将语言的状态改变对应为一个事件,然后在 App 中依赖语言的组件 initState 方法中订阅语言改变事件,当用户切换语言之后,订阅此事件的组件就会收到通知,收到通知后重新 setState 即可 2,使用一些专门用于状态管理的包,如 Provider,Redux 等,具体的使用可上 pub 查看详细信息

参考

Flutter 实战

0 人点赞