flutter_bloc的思考
UI更新对比
StatefulWidget中setState(...)方法是处理Widget状态的最简单方法,但您确实应该避免使用这种方法 因为它将UI逻辑与业务逻辑混合在一起。这样对于代码的维护性和可行测试性都不太友好,同时对于大团队协作方面也不太友好。
为什么是bloc
术语“BLoC”是Business Logic Components的首字母缩写词,它依赖于异步流。Bloc 可以轻松地表示与业务逻辑分离,从而使您的代码快速、易于测试和可重用。
Bloc 的设计秉承了三个核心价值观:
- 简单:易于理解,不同技能水平的开发人员都可以使用。
- 强大:通过组合较小的组件,帮助创建令人惊叹的复杂应用程序。
- 可测试:轻松测试应用程序的各个方面,以便我们满怀信心地进行迭代。
总体而言,Bloc尝试通过规范状态更改发生的时间并在整个应用程序中强制执行单一的状态更改方式,使状态更改变得可预测。
bloc 架构
使用bloc库可以将应用程序分为三个层:
- 表示层
- 业务逻辑层
- 数据层
-
- 存储库层
-
- 数据提供程序
UI对其背后的业务逻辑一无所知,因为它完全被交给了Bloc;这是业务逻辑与设计逻辑的完全分离。太棒了! 官方flutter_bloc文档包含大量带有图像的分步示例和许多旁注解释,以确保您不会迷失。
flutter_bloc使用
BlocObserver介绍
BlocObserver观察者是一个观察bloc中发生的事情的对象,它让你可以记录各种事情。可以记录event(onEvent),可以记录event的变化(onTransition),可以记录错误(onError)。当你调试应用时候,这是非常有用的工具。Bloc中的observer是一个静态属性,直接对它进行赋值就可以了。如下图所示:
Bloc.observer = const CounterObserver();
让我们看看如何使用 BlocProvider为CounterPage提供CounterCubit,并使用BlocBuilder对状态变化做出反应。
- 首先创建Cubit的子类。如counter_cubit.dart,如下图所示:
import 'package:bloc/bloc.dart';
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
- lib_bloc_main.dart如下图所示,BlocProvider是一个StatelessWidget的子类,create创建第一步声明的CounterCubit。
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_cubit.dart';
import 'counter_page.dart';
void main() => runApp(CounterApp());
class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (_) => CounterCubit(),
child: CounterPage(),
),
);
}
}
- counter_page.dart代码如下图所示, BlocBuilder是一个Flutter Widget,它需要一个Bloc和一个构建器函数。BlocBuilder负责根据新状态构建Widget。
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_cubit.dart';
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: BlocBuilder<CounterCubit, int>(
builder: (context, count) => Center(child: Text('$count')),
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => context.read<CounterCubit>().increment(),
),
const SizedBox(height: 4),
FloatingActionButton(
child: const Icon(Icons.remove),
onPressed: () => context.read<CounterCubit>().decrement(),
),
],
),
);
}
}
此时,我们已成功将表示层与业务逻辑层分离。请注意,CounterPage中的Widget对用户点击按钮时发生的情况一无所知。该小部件只是通知CounterCubit用户已按下增加或减少按钮。
MultiBlocProvider是一个 Flutter Widget,它将多个 BlocProvider Widget 合并为一个。MultiBlocProvider提高了可读性,并且无需嵌套多个BlocProvider。这样有利于数据范围尽量小,同时就能保证Widget更新的范围尽量的小。
BlocConsumer公开了构建器和监听器,以便对新状态做出反应(如更新UI)。BlocConsumer类似于嵌套的 BlocListener和BlocBuilder,但减少了所需的样板代码量。
扩展方法
最好使用context.bloc(),它比使用非扩展版本 BlocProvider.of(context)更简洁。
只将实际需要重建以响应状态更改的小部件放入BlocBuilder() 中。不要将不需要监听更改的小部件放入其中,即使它们是 const,因为它们会“污染” bloc 的范围。
如果您需要同时监听和构建 bloc,请使用 BlocConsumer<B,S>而不是嵌套小部件;它可以减少样板代码的数量。
考虑使用ReplayBloc或ReplayBlocMixin为您的bloc添加撤销/重做操作。
context.read()不监听T,如果提供的 T 类型对象发生变化,context.read 将不会触发小部件重建。
context.select() 允许您监听状态较小部分的变化(T中数据的一部分)。
总结
Bloc旨在使用Widget更加简单,更加快捷,方便不同开发者不同团队分工都能使用,可以记录组件的各种状态,方便测试,让许多开发者遵循相同的模式和规则在一个代码库中无缝工作。此时,我们已成功将表示层与业务逻辑层分离。希望文章对您有帮助,如果文中有问题,希望您不吝指教。
参考资料
转载自:https://juejin.cn/post/7391699424844447795