Flutter学习 - Bloc - 03 Cubit的使用

2,458 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情

本文主要介绍Cubit类以及它的使用

Cubit 类继承自 BlocBase 的类,并且可以扩展到管理任何类型的状态。

image.png

是Bloc中的核心,进行输入和输出,是一个可以公开触发状态变化的函数

1. 创建

创建一个简单的Cubit

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
}

我们要定义它管理的状态类型。上面的比较简单我们定义类型为int,复杂的情况要使用class

  • 初始化状体

我们之后要初始化状态,上面的例子,在内部初始化的时候调用父类super,并设置为0,也可以使用外部变量来使得初始化更灵活

class CounterCubit extends Cubit<int> {
 CounterCubit(int initialState) : super(initialState);
}

初始化不同的状态

final cubitA = CounterCubit(0); // state starts at 0
final cubitB = CounterCubit(10); // state starts at 10

通过emit输出一个新的状态:

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void add() => emit(state +1);
  void reduce() => emit(state -1);
}

我们通过方法增加,emit 函数受到保护,这意味着它只能在 Cubit 内部使用。

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('计数器'),),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          BlocBuilder<CounterCubit,int>(
            builder: (context,state){
              return Text('$state');
            },
          ),
          SizedBox(height: 50,),
          ElevatedButton(onPressed: ()=> context.read<CounterCubit>().add(), child: const Text('增加')),
          ElevatedButton(onPressed: ()=> context.read<CounterCubit>().reduce(), child: const Text('减少')),
          ElevatedButton(onPressed: ()=> context.read<CounterCubit>().close(), child: const Text('关闭'))

        ],
      ),
    ),
  );
}

我们点击增加数字会增加,点击减少会减少,函数来触发状态更改。从而更改了UI。当我们调用close后不再响应了。

Bad state: Cannot emit new states after calling close

2. Stram的使用

Cubit同时也是Stream的一种类型,因此具有Stream的特征,我们也可以通过订阅来更新状态。

Future<void> main() async {
  final cubit = CounterCubit();
  final subscription = cubit.stream.listen(print); // 1
  cubit.add();
  await Future.delayed(Duration.zero);
  await subscription.cancel();
  await cubit.close();
}

添加了 await Future.delayed(Duration.zero),以避免立即取消订阅。在 Cubit 上调用 listen 时,将仅接收后续状态更改

3. 观察Cubit

当 Cubit 发出新状态时,将有一个 改变 发生。我们可以通过重写 onChange 方法来观察给定 Cubit 的所有变化。

@override
void onChange(Change<int> change) {
  super.onChange(change);
  print(change);
}

image.png

在 Cubit 状态更新之前发生 Change 改变。一个 改变 由 currentState 和 nextState 组成。

4. Bloc观察者

使用 bloc 库的另一个好处是,我们可以在一处访问所有 变化。即使在此应用程序中只有一个 Cubit,在大型应用程序中也很常见,有许多 Cubits 管理应用程序状态的不同部分。

如果我们希望能够对所有 变化 做出响应,我们可以简单地创建自己的 BlocObserve (Bloc观察者)来观察改变。

这个类似我们为对象添加观察者,全局监听对象的属性发生变化,类似我们iOS中的kvo

class TestBlocObserver extends BlocObserver {

  @override
  void onChange(BlocBase bloc, Change change){
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');

  }

}

我们在cubit初始化之前添加Bloc观察者

image.png

首先调用内部的 onChange 替代,然后在 BlocObserver 中调用 onChange。在 BlocObserver 中,除了 变化 本身之外,我们还可以访问 Cubit 实例。

5.错误处理

每个 Cubit 都有一个 addError 方法,该方法可用于指示发生了错误。

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void add() => emit(state +1);
  void reduce() => emit(state -1);

  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onError(Object error, StackTrace stackTrace) {
    print('$error, $stackTrace');
    super.onError(error, stackTrace);
  }
}

也可以在 BlocObserver 中重写 onError 方法以全局处理所有报告的错误。

class TestBlocObserver extends BlocObserver {

  @override
  void onChange(BlocBase bloc, Change change){
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');

  }
  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}

image.png

与 onChange 一样,内部 onError 重写在全局 BlocObserver 重写之前被调用。