携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
本文主要介绍Cubit类以及它的使用
Cubit 类继承自 BlocBase 的类,并且可以扩展到管理任何类型的状态。
是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);
}
在 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观察者
首先调用内部的 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);
}
}
与 onChange 一样,内部 onError 重写在全局 BlocObserver 重写之前被调用。