BLoC
BLoC表示为业务逻辑组件 (Business Logic Component),由谷歌在2018的 DartConf 首次提出------其诞生可以说是为了解耦。
在了解该框架之前需要了解;
- Stream
这个名词跟其他语言里某个XX有个共同的名字------管道。
运作时,考虑为一个带有两端的管道(Stream),当你将某物插入(通常StreamController控制插入)管道时(入口为sink),它会在管道内流动并从另一端流出(出口为stream)。
//建一个类ApplicationBloc -------- 记住这个类名字
BehaviorSubject<int> _appEvent = BehaviorSubject<int>(); //该BehaviorSubject也是广播 StreamController,它返回一个Observable,而不是Stream。
Sink<int> get _appEventSink => _appEvent.sink; //Widgets 通过 Sinks 向 BLoC 发送事件(event)
Stream<int> get appEventStream => _appEvent.stream; //BLoC 通过流(stream)通知小部件(widgets)
(BehaviorSubject,不知道啥的向下看)
- Stream中的关键词
从值,事件,对象,集合,映射,错误甚至另一个流,又或者换句话说,可以由stream传递任何类型的数据。那么,怎么知道传递的类别呢?
StreamController control= StreamController();
//监听StreamController 的stream属性,就可以时刻关注到传递的内容
StreamSubscription subscription = control.stream.listen((data) => print('$data'));
control.sink.add(6666);
control.close();
上述是Stream单订阅的一种表现方式。那么再看看另外一种形态 ----- 广播流。
StreamController<int> ctrl = StreamController<int>.broadcast();
StreamSubscription subscription = ctrl.stream
.where((value) => (value % 2 == 0))
.listen((value) => print('$value'));
for(int i=1; i<11; i++){
ctrl.sink.add(i);
}
//一般在dispose方法中close掉
ctrl.close();
另外一个关键词,StreamTransformer ---- 控制Stream内部数据的处理。
StreamTransformer可以用进行任何类型的处理,例如:
过滤(filtering):根据任何类型的条件过滤数据,
重新组合(regrouping):重新组合数据,
修改(modification):对数据应用任何类型的修改,
将数据注入其他流,
缓冲,
处理(processing):根据数据进行任何类型的操作/操作,
…
即把流转换为流。
-
RxDart
简单来说,RxDart 扩展了原始的Dart Streams API并提供了StreamController的 3个主要变体:PublishSubject、BehaviorSubject、ReplaySubject涵盖着事件订阅的概念,三者返回的都是Observable -
反应式编程
反应式编程是使用异步数据流进行编程,换句话说一个事件由数据流触发时,其连带的所有内容都会被传送,即让整个行为变得异步。
看到这里,结合1中ApplicationBloc那里注册的BehaviorSubject广播,我们可以在需要发送广播的地方:
//在ApplicationBloc 中添加该方法
void sendAppEvent(int type) {
_appEventSink.add(type);
}
//在build中初始化
final ApplicationBloc bloc = BlocProvider.of<ApplicationBloc>(context);
//需要调用的地方进行调用
bloc.sendAppEvent(Constant.TYPE_SYS_UPDATE);
//在接收的地方
BlocProvider<ApplicationBloc>(
bloc: ApplicationBloc(),
child: BlocProvider(child: XX(), bloc: null),
)
其中的BlocProvider,现在通用写法是
import 'dart:async';
import 'package:flutter/material.dart';
abstract class BlocBase {
Future getData({String labelId, int page});
Future onRefresh({String labelId});
Future onLoadMore({String labelId});
void dispose();
}
Type _typeOf<T>() => T;
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider({
Key key,
@required this.child,
@required this.bloc,
}): super(key: key);
final Widget child;
final T bloc;
@override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context){
final type = _typeOf<_BlocProviderInherited<T>>();
_BlocProviderInherited<T> provider =
context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;
return provider?.bloc;
}
}
class _BlocProviderState<T extends BlocBase> extends State<BlocProvider<T>>{
@override
void dispose(){
widget.bloc?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context){
return new _BlocProviderInherited<T>(
bloc: widget.bloc,
child: widget.child,
);
}
}
//由于使用了InheritedWidget,它现在可以调用context.ancestorInheritedElementForWidgetOfExactType()函数,它是一个O(1),这意味着祖先的检索是立即的
class _BlocProviderInherited<T> extends InheritedWidget {
_BlocProviderInherited({
Key key,
@required Widget child,
@required this.bloc,
}) : super(key: key, child: child);
final T bloc;
@override
bool updateShouldNotify(_BlocProviderInherited oldWidget) => false;
}
在这里只是简单了解下,有需要更清晰讲解的可以去看这是一个链接
除了自己学习怎么运用BLoc之外,还可以去https://pub.dartlang.org/packages/去搜索BLoc添加某人上传的依赖(还需要添加flutter_bloc
辅助运用)。后者简单了许多。举个简单的例子
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter_acesmart/entity/banner_entity.dart';
import 'package:flutter_acesmart/entity/base_entity.dart';
import 'package:flutter_acesmart/http/index.dart';
import 'package:flutter_acesmart/http/robort_index.dart';
import 'home_event.dart';
//HomeEvent和HomeState是我定义在home_event的类,HomeEvent为事件触发,HomeState为事件处理后或之前的状态。
class HomeBloc extends Bloc<HomeEvent, HomeState> {
@override
HomeState get initialState => HomeBannerLoading();
//这个是必须实现的方法
@override
Stream<HomeState> mapEventToState(HomeEvent event) async* {
if (event is HomeBannerLoad) {
yield* mapLoadBannerToState();
}
}
Stream<HomeState> mapLoadBannerToState() async* {
try {
List<BannerEntity> bannerEntitys = await _getBanners();
yield HomeBannerLoaded(bannerEntitys);
//fixme:获取机器人保留的数据或者initstate中disparch
} catch (e) {
yield LoadError(e);//在HomeState中定义的错误状态方法
}
}
//不用加try-catch,调用层已捕获了
//BannerEntity是定义的mode类
Future<List<BannerEntity>> _getBanners() async {
Response response = await HomeApi.getBanners();
BaseEntity<List> baseEntity = BaseEntity.fromJson(response.data);
List<BannerEntity> bannerEntitys = baseEntity.data.map((e) {
return BannerEntity.fromJson(e);
}).toList();
return bannerEntitys;
}
}
下main看看HomeEvent和HomeState(其实可以把他们放在一个类里面,我是觉得放在一起也挺好)
import 'package:equatable/equatable.dart';
import 'package:flutter_acesmart/entity/banner_entity.dart';
import 'package:meta/meta.dart';
abstract class HomeEvent extends Equatable {
HomeEvent([List props = const []]) : super(props);
}
abstract class HomeState extends Equatable {
HomeState([List props = const []]) : super(props);
}
///加载主页数据
// event
class HomeBannerLoad extends HomeEvent {
@override
String toString() {
return 'HomeBannerLoad';
}
}
//event需要传递参数时
class lampChange extends HomeEvent{
final int light;
lampChange({@required this.light})
: assert(light != null),
super([light]);
@override
String toString() {
return 'lampChange';
}
}
///State
class HomeBannerLoading extends HomeState {
@override
String toString() {
return 'HomeBannerLoading';
}
}
//banner下载完毕状态切换,同时把数据绑定传送
class HomeBannerLoaded extends HomeState {
List<BannerEntity> banners;
HomeBannerLoaded(this.banners) : super([banners]);
@override
String toString() {
return 'ProjectBannerLoaded{banners: ${banners?.length}}';
}
}
class LoadError extends HomeState {
Exception exception;
LoadError(this.exception) : super([exception]);
@override
String toString() {
return 'HomeLoadError';
}
}
在需要调用的地方
class HomePage extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return new HomePageState();
}
}
class HomePageState extends State<HomePage>with TickerProviderStateMixin {
HomeBloc homeBloc ;
List<BannerEntity> banners;
@override
void initState() {
super.initState();
homeBloc = HomeBloc();
banners ??= [];
//调用banner下载event
homeBloc.dispatch(HomeBannerLoad());
}
@override
Widget build(BuildContext context) {
LogUtil.e("HomePage build......");
return Center(
child: BlocListener<HomeBloc, HomeState>(
bloc: homeBloc,
listener: (context, state) {
if (state is HomeBannerLoaded) {
banners = state.banners; //绑定数据
}
else if(state is LoadError) {
DisplayUtil.showMsg(context, exception: state.exception);
}
},
child: BlocBuilder<HomeBloc, HomeState>(
bloc: homeBloc,
builder: (context, state) {
return Stack(
children: <Widget>[
Scaffold(
.......
body: Builder(builder: (context) {
return Stack(
)
],
),
......
大致的使用流程就是这样啦。