# 状态管理概览

无状态组件:是不可变的,这意味着它们的属性不能改变,所有的值都是最终的。

有状态组件:持有状态可能在Widget生命周期中发生变化。实现一个StatefulWidget至少需要两个类:一个StatefulWidget类,一个State类

Flutter中三种推荐的状态管理方案

  • Redux: redux,flutter_redux
  • ScopedModel:scoped_model
  • Bloc:bloc,flutter_bloc

前面的是方案,后面的是对应的pub.dev中的第三方包名。

# 前置知识

状态管理主要的目的是解决:组件间状态传递。在学习之前,扩展一些基础的知识。

# 安装第三方包

两种安装方式:

  • flutter pub add [package-name]:直接安装最新的包
  • pubspec.yamldependencies中添加对应的包与版本,再使用flutter pub get安装;

说明:为了学习体验一致,建议使用第二种,以免由于版本带来的兼容性问题。

# 类及构造函数

定义类:

class Demo {
  final int count;
  // 定义构造函数
  Demo(this.count)
}

可以在书写了final int count之后,使用VSCode的快捷菜单创建:

image-20210921185652112

即是如下的菜单:

image-20210921185716680

其中的Demo(this.count)是Dart中的语法糖,也可以如下进行定义:

class Demo {
  int count = 0;

  Demo(int count) {
    this.count = count;
  }
}

// 其中this.count中的this可以省略
class Demo {
  int count = 0;

  Demo(int count) {
    count = count; // 这一行是不是看起来非常的多余,括号中的count是一个形参,是外部传递进来的,等号左边的count是Demo类中的公有成员。
  }
}

上面的写法太多余了,所以推荐使用语法糖的写法:Demo(this.count)

# 命名式构造函数

class Demo {
  final int count = 0;

  Demo(this.count)
  // 命名式构造函数
  Demo.iniState() : count = 0;
}

# 可选参数与必传参数

class Demo {
  final int count;

  Demo(this.count);
}

这个地方的count就是一个必传参数:

image-20210921194016562

可以省略new关键字:

image-20210921194104681

设置成可选参数:

image-20210921194128527

注意:

  • 设置成可选参数之后,需要通过属性名才能设置参数的值;

    class Demo {
      final int? count;
    
      Demo({this.count});
    }
    
    var demo = Demo(count: 0);
    
  • 可以在申明的类型之后使用?来告诉dart的编译器这里是一个可选的属性,从而跳过null值检查;

    class Demo {
      final int? count;
    
      Demo({this.count});
    }
    
  • 也可以添加一个required带表是必传参数:

    class Demo {
      final int count;
    
      Demo({required this.count});
    }
    

# 应用场景与优缺点

# 1. Redux

优点

Redux允许集中管理一个状态,因为只有Reducers可以完成从一个状态到另一个状态的转换。 在流程中插入中间件的便利性也是一种优势。例如,如果需要不断地验证与服务器的连接性或跟踪活动。

缺点

  • 一个单一的Store和一个巨大的State(如果你想坚持Redux的良好做法)。

  • 在Reducer和MiddleWare的层面上,有太多的 "如果......那么 "的比较。

  • 重建次数太多(每次State发生变化时)

应用场景:

  • 熟悉React,Redux,有之前的Redux的概念的学习;
  • state功能模块不多,并且页面中不需要高性能刷新:例如,用户认证、购物车、等......

不推荐使用的场景:

当你需要处理某个东西的多个实例,并且每个实例都有自己的状态时,我不推荐使用Redux。

# 2. ScopedModel

优点

  • ScopedModel可以很容易地将Model和它的逻辑重组在一个位置。
  • ScopedModel不需要任何Streams概念的知识,这使得初学者更容易实现。
  • ScopedModel既可以用于全局逻辑,也可以用于局部逻辑。
  • ScopedModel不限于State管理。

缺点

  • ScopedModel并没有提供任何方法让代码知道Model的哪个部分发生了变化,并导致ScopedModelDescendant被调用。

  • 太多的(重新)构建。

    每当一个Model通知它的监听器时,所有与该Model相关的东西都会重新构建(AnimatedBuilder, InheritedWidget...)。

  • 需要使用外部软件包,但有可能随着软件包的发展而发生中断变化。

应用场景

  • 当开发者对Streams不是很熟悉时
  • 当模型不是太复杂时

不推荐使用的场景:

  • 当应用程序出于性能考虑,需要减少构建次数时。
  • 当一个应用程序需要精确地知道模型的哪一部分已经改变了。

# 3. BLoC

优点

  • BLoC可以轻松地将业务逻辑重新组合到一个位置。
  • BLoC可以很容易地精确确定任何变化的性质(通过其输出接口,基于流)。
  • 由于使用了 StreamBuilder 小工具,BLoC 可以很容易地将(重新)构建的数量限制在最低限度。
  • 流的使用是非常强大的,并打开了许多行动的大门(转换,独特,debounce...)。
  • BLoCs可用于全局和局部逻辑。
  • 基本BLoC不限于状态管理
  • 不需要使用任何外部软件包。

缺点:

对于初学者来说,开始使用BLoC是比较困难的,因为它需要额外了解Flutter的实际工作方式。

应用场景:

几乎所有的场景都可以使用,分cubit与bloc

不推荐使用的场景:

  • 单一的state,没有太多性能的需求,直接使用scoped_model即可
  • 功能比较单一,父子页面传参,没有跨层级;