[Flutter]BLoC模式
BLoC简介
BLoC是Business Logic Component的英文缩写,中文译为业务逻辑组件,是一种使用响应式编程来构建应用的方式。BLoC最早由谷歌的Paolo Soares和Cong Hui设计并开发,设计的初衷是为了实现页面视图与业务逻辑的分离。如下图所示,是采用BLoC模式的应用程序的架构示意图。
使用BLoC方式进行状态管理时,应用里的所有组件被看成是一个事件流,一部分组件可以订阅事件,另一部分组件则消费事件,BLoC的工程流程下图所示。
如上图所示,组件通过Sink向Bloc发送事件,BLoC接收到事件后执行内部逻辑处理,并把处理的结果通过流的方式通知给订阅事件流的组件。在BLoC的工作流程中,Sink接受输入,BLoC则对接受的内容进行处理,最后再以流的方式输出,可以发现,BLoC又是一个典型的观察者模式。理解Bloc的运作原理,需要重点关注几个对象,分别是事件、状态、转换和流。
- 事件:在Bloc中,事件会通过Sink输入到Bloc中,通常是为了响应用户交互或者是生命周期事件而进行的操作。
- 状态:用于表示Bloc输出的东西,是应用状态的一部分。它可以通知UI组件,并根据当前状态重建其自身的某些部分。
- 转换:从一种状态到另一种状态的变动称之为转换,转换通常由当前状态、事件和下一个状态组成。
- 流:表示一系列非同步的数据,Bloc建立在流的基础之。并且,Bloc需要依赖RxDart,它封装了Dart在流方面的底层细节实现。
BLoC Widget
Bloc既是软件开发中的一种架构模式,也是一种软件编程思想。在Flutter应用开发中,使用Bloc模式需要引入flutter_bloc库,借助flutter_bloc提供的基础组件,开发者可以快速高效地实现响应式编程。flutter_bloc提供的常见组件有BlocBuilder、BlocProvider、BlocListener和BlocConsumer等。
BlocBuilder
BlocBuilder是flutter_bloc提供的一个基础组件,用于构建组件并响应组件新的状态,它通常需要Bloc和builder两个参数。BlocBuilder与StreamBuilder的作用一样,但是它简化了StreamBuilder的实现细节,减少一部分必须的模版代码。而builder()方法会返回一个组件视图,该方法会被潜在的触发多次以响应组件状态的变化,BlocBuilder的构造函数如下所示。
const BlocBuilder({
Key key,
@required this.builder,
B bloc,
BlocBuilderCondition condition,
})
可以发现,BlocBuilder的构造函数里面一共有三个参数,并且builder是一个必传参数。除了builder和bloc参数外,还有一个condition参数,该参数用于向BlocBuilder提供可选的条件,对builder函数进行缜密的控制。
BlocBuilder(
condition: (previousState, state) {
//根据返回的状态决定是否重构组件
},
builder: (context, state) {
//根据BlocA的状态构建组件
}
)
如上所示,条件获取先前的Bloc的状态和当前的bloc的状态并返回一个布尔类型的值。如果condition属性返回true,那么将调用state执行视图的重新构建。如果condition返回false,则不会执行视图的重建操作。
BlocProvider
BlocProvider是一个Flutter组件,可以通过BlocProvider.of
大多数情况下,我们可以使用BlocProvider来创建一个新的blocs,并将其提供给其它子组件,由于blocs是BlocProvider负责创建的,那么关闭blocs也需要BlocProvider进行处理。除此之外,BlocProvider还可用于向子组件提供已有bloc,由于bloc并不是BlocProvider创建的,所以不能通过BlocProvider来关闭该bloc,如下所示。
BlocProvider.value(
value: BlocProvider.of(context),
child: ScreenA(),
);
MultiBlocProvider
MultiBlocProvider是一个用于将多个BlocProvider合并为一个BlocProvider的组件,MultiBlocProvider通常用于替换需要嵌套多个BlocProviders的场景,从而降低代码的复杂度、提高代码的可读性。例如,下面是一个多BlocProvider嵌套的场景。
BlocProvider(
create: (BuildContext context) => BlocA(),
child: BlocProvider(
create: (BuildContext context) => BlocB(),
child: BlocProvider(
create: (BuildContext context) => BlocC(),
child: ChildA(),
)
)
)
可以发现,示例中BlocA嵌套BlocB, BlocB又嵌套BlocC,代码逻辑非常复杂且可读性很差。那如果使用MultiBlocProvider组件就可以避免上面的问题,改造后的代码如下所示。
MultiBlocProvider(
providers: [
BlocProvider(
create: (BuildContext context) => BlocA(),
),
BlocProvider(
create: (BuildContext context) => BlocB(),
),
BlocProvider(
create: (BuildContext context) => BlocC(),
),
],
child: ChildA(),
)
BlocListener
BlocListener是一个接收BlocWidgetListener和可选Bloc的组件,适用于每次状态更改都需要发生一次的场景。BlocListener组件的listener参数可以用来响应状态的变化,可以用它来处理更新UI视图之外的其他事情。与BlocBuilder中的builder操作不同,BlocBuilder组件的状态更改仅会调用一次监听,并且是一个空函数。BlocListener组件通常用在导航、SnackBar和显示Dialog的场景。
BlocListener(
bloc: blocA,
listener: (context, state) {
//基于BlocA的状态执行某些操作
}
child: Container(),
)
除此之外,还可以使用条件属性来对监听器函数进行更加缜密的控制。条件属性会通过比较先前的bloc的状态和当前的bloc的状态返回一个布尔值,如果条件返回true,那么监听汗水将会被调用,如果条件返回false,监听函数则不会被调用,如下所示。
BlocListener(
condition: (previousState, state) {
//返回true或false决定是否需要调用监听
},
listener: (context, state) {
}
)
如果需要同时监听多个bloc的状态,那么可以使用MultiBlocListener组件,如下所示。
BlocListener(
MultiBlocListener(
listeners: [
BlocListener(
listener: (context, state) {},
),
BlocListener(
listener: (context, state) {},
),
…
],
child: ChildA(),
)
除此之外,flutter_bloc提供的组件还有BlocConsumer、RepositoryProvider和MultiRepositoryProvider等。
当状态发生变化时,除了需要更新UI视图之外还需要处理一些其他的事情,那么可以使用BlocListener,BlocListener包含了一个listener用以做除UI更新之外的事情,该逻辑不能放到BlocBuilder里的builder中,因为这个方法会被Flutter框架调用多次,builder方法应该只是一个返回Widget的函数。
From:
作者:iOS_天气
链接:https://www.jianshu.com/p/f8ea3548a45d