Flutter快学快用24讲--08 状态管理:Flutter 状态管理及对比选型

本文探讨了Flutter应用中状态管理的重要性,对比了InheritedWidget、Redux和Provider三种常见状态管理技术,并通过示例详细解释了它们的实现方式和优缺点。最终推荐使用Provider来优化组件设计,实现多页面状态同步。
摘要由CSDN通过智能技术生成

上一课时我详细介绍了有/无状态组件的应用设计,但是在设计过程中,还缺乏一个对状态管理的考虑。本课时介绍状态管理设计的必要性,以及一些常见的状态管理技术对比,最后再着重通过 Provider 来优化前一课时中的例子。

状态管理场景

上一课时的例子中,只涉及一个有状态的组件 article_like_bar ,接下来我们需要实现另外一个详情页面,并且在详情页面中也需要一个点赞功能,具体的界面效果可以参考动图 1 (为了界面更好,我在上一课时的基础上增加了一些样式)。

20200620_110314.gif

图 1 增加二级点赞详情页面效果

在上面的动图例子中,你是否发现了一个问题?第一个页面的点赞数与第二个页面的点赞数并不同步。在实际项目开发过中,需求方希望二级详情页面的点赞数能与第一个页面的点赞数同步。

如果不引入新的技术方案,能想到的办法就是将该状态进行提升,放到其共同的父节点上,然后将父节点设计为有状态组件,并提供修改状态的方法给到子组件。可以用图 2 来表示。

Drawing 1.png

图 2 状态提升共享方式

上面的方式是可以做到这点,但是你有没有发现,只因为一个点赞行为,就需要将两个页面的所有组件(静态组件和动图组件)进行重新 build ,成本实在太高,这也违背了我们上一课时的组件设计原则(尽可能减少动态组件下的静态组件)。为了更好地解决这个问题,我们就需要引入一些状态管理的方法,下面就介绍一些常见的技术方案,同时做一个对比。

状态选型对比

状态管理技术不少于 10 种,但是为了高效,我只介绍其中比较核心的三个,第一个是原生所使用的 InheritedWidget ;第二个是相对前端同学比较熟悉的 Redux 技术;最后一个则是我们推荐使用的技术 Provider 。

InheritedWidget

InheritedWidget 核心原理和状态提升原理一致,将 likeNum 提升到根节点,但不需要一层层地将变量传递下去,只需要在根节点声明即可。

现在我们有一个页面,页面下有两个组件,两个组件都需要用同一个名字,并且第二个组件的名字可以点击切换随机名字,而切换以后需要及时更新第一个组件中的名字。页面效果如图 3 所示。

Drawing 3.png

图 3 多组件状态共享效果

按照上面介绍的例子以及上一课时的知识点,画一个简单的组件树,并且附带上需要的状态属性,如图 4 所示。

Drawing 4.png

图 4 InheritedWidget 组件设计

  • 首先创建一个根结点为一个有状态组件 name_game;

  • name_game 为一个有状态类,状态属性为 name,并带有 changName 的状态修改方法;

    • 创建一个状态管理类组件 NameInheritedWidget ;
  • 创建 NameInheritedWidget 的三个子组件,分别为 welcome(显示欢迎 name )、random_name(显示 name ,并且有点击切换随机 name 操作)和 other_widgets 。

对于上面的结构,肯定有很多同学比较疑惑,other_widgets 并没有使用这个 name 状态,为什么要在 NameInheritedWidget 下呢?

带着这样的疑惑,我们先来看下 name_game 核心代码(为了在专栏中更简洁,我省去了部分代码,完整代码大家可以参考文章下的 github 代码地址)。

/// 随机名字游戏组件状态管理类
class NameGameState extends State<NameGame> {
  /// name 状态
  String name;
  /// 构造函数参数,避免父组件状态变化,而引起的子组件的重 build 操作
  Widget child;
  /// 修改当前名字
  void changeName() {
    List<String> nameList = ['flutter one', 'flutter two', 'flutter three'];
    int pos = Random().nextInt(3);
    setState(() {
      name = nameList[pos];
    });
  }
  @override
  void initState() {
    setState(() {
      name = 'test flutter';
    });
    super.initState();
  }
  /// 构造函数
  NameGameState()
  {
    child = Column (
        children: <Widget>[
          Welcome(),
          RandomName(),
          TestOther(),
        ]
    );
  }
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        NameInheritedWidget(
            child: child,
            onNameChange: changeName,
            name: name
        ),
      ],
    );
  }
}

上面代码中,定义状态属性 name ,并创建了可以修改 state 的 changeName 方法。接下来在 build 中使用 NameInheritedWidget 这个组件(该组件可以理解为前端所说的高阶组件,也就是通过将组件作为参数传递进该组件,并返回一个新的组件的功能组件),这个组件包裹了两个需要状态 name 的组件( Welcome 和 RandomName )以及一个不需要状态的 TestOther。

上面代码中还有一个比较特殊的地方,就是将 child 作为了 state ,在构造函数中进行了定义,并将该组件的所有子组件都包含在了 child 中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值