flutter 解耦框架BLoC在网络请求中的应用

BLoC
BLoC表示为业务逻辑组件 (Business Logic Component),由谷歌在2018的 DartConf 首次提出------其诞生可以说是为了解耦。
在了解该框架之前需要了解;

  1. 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):根据数据进行任何类型的操作/操作,

即把流转换为流。

  1. RxDart
    简单来说,RxDart 扩展了原始的Dart Streams API并提供了StreamController的 3个主要变体:PublishSubjectBehaviorSubjectReplaySubject涵盖着事件订阅的概念,三者返回的都是Observable

  2. 反应式编程
    反应式编程是使用异步数据流进行编程,换句话说一个事件由数据流触发时,其连带的所有内容都会被传送,即让整个行为变得异步。

看到这里,结合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(
          
                         )
                       ],
                    ),
   ......


大致的使用流程就是这样啦。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值