专栏首页玩转全栈flutter中使用BloC模式
原创

flutter中使用BloC模式

2860元腾讯云代金券免费领取,付款直接抵现金,立即领取>>>

腾讯云海外服务器1折限时抢购,2核4G云主机768元/1年,立即抢购>>>

腾讯云服务器1折限时抢购,2核4G云主机899元/3年,立即抢购>>>

业务逻辑组件

什么是BloC模式?

BloC【Business Logic Component】模式是paolo soares 和 cong hui 在2018年Google dartconf上提出的,具体的视频你可以参考YouTube.

从视频中可以看到paolo soares用一个及其简单的例子阐述了传统写法的问题:

1、业务逻辑和UI组件糅合在一起。

2、不方便测试,不利于单独的测试业务逻辑部分。

3、不能更好的重用业务逻辑代码,体现在,如果网络请求的逻辑有所变动的话,加入这个业务功能被两个端(web、flutter)使用的话,是需要改动两个地方的。

基于上面出现的一些问题,paolo soares顺利的将我们重传统的开放方式,引入到了Bloc模式。

传统的开发方式

传统的开发方式,可以很明显的看出来,其中网络请求的代码和ui界面写了一起,日积月累,这里面的代码复杂度会随之增加,下面是改造之后的编写方式,将业务逻辑抽出来,放到了一个businessLogic中了。

改造之后的方式

可以看到改造之后,变得清晰多了,这个文件几乎就全部是UI构建的代码,所有的逻辑都抽到了businessLogic中了。做过android开发的小伙伴看到这个模式就一定会联想到MVP设计模式了吧,其中Presenter似乎就是干businessLogic的事情了。更具我自己的一点理解来看,实际上BloC设计模式,似乎和MVP没有什么本质区别,两种设计模式的最终目的就是为了把和UI糅合在一起的业务逻辑代码剥离开来,单独的抽取到一层中。

如何用BloC模式

应用

上图是描述的是,组件的一些基本行为,【展示数据】,【发送事件】。在flutter中,实现BloC模式的精髓就是,

展示的数据从BloC中来,具体到了stream上,有了stream的到来,就可以使用StreamBuilder来构建ui了。

// TODO(ianh): remove unreachable code above once https://github.com/dart-lang/linter/issues/1141 is fixed
class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
  /// Creates a new [StreamBuilder] that builds itself based on the latest
  /// snapshot of interaction with the specified [stream] and whose build
  /// strategy is given by [builder]. The [initialData] is used to create the
  /// initial snapshot. It is null by default.
  const StreamBuilder({
    Key key,
    this.initialData,
    Stream<T> stream,
    @required this.builder
  })
      : assert(builder != null),
        super(key: key, stream: stream);

发送事件丢给BloC处理,具体到了sink上。

/**
 * A generic destination for data.
 *
 * Multiple data values can be put into a sink, and when no more data is
 * available, the sink should be closed.
 *
 * This is a generic interface that other data receivers can implement.
 */
abstract class Sink<T> {
  /**
   * Adds [data] to the sink.
   *
   * Must not be called after a call to [close].
   */
  void add(T data);

  /**
   * Closes the sink.
   *
   * The [add] method must not be called after this method.
   *
   * Calling this method more than once is allowed, but does nothing.
   */
  void close();
}

恩,事件发送过去之后,onListen中就可以收到,并且识别出事件,进行相应的处理,之后,stream中产生了新的数据,于是,StreamBuilder又触发了UI的更新,整个流程就跑通了。

来一个简单的例子

UI部分

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
 ///...
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final IncrementBloc bloc = BlocProvider.of<IncrementBloc>(context);

    return Scaffold(
      appBar: AppBar(title: Text('Stream version of the Counter App')),
      body: Center(
      ///注意这里,通过stream构建ui
        child: StreamBuilder<int>(
          stream: bloc.outCounter,
          initialData: 0,
          builder: (BuildContext context, AsyncSnapshot<int> snapshot){
            return Text('You hit me: ${snapshot.data} times');
          }
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: (){
        ///注意这里,事件发送
          bloc.incrementCounter.add(null);
        },
      ),
    );
  }
}

Bloc部分

class IncrementBloc{
  int _counter;

  //
  // Stream to handle the counter,第一组stream
  //
  StreamController<int> _counterController = StreamController<int>();
  StreamSink<int> get _inAdd => _counterController.sink;//这个sink用与给outCounter添加数据
  Stream<int> get outCounter => _counterController.stream;//这个就是ui需要使用的stream

  //
  // Stream to handle the action on the counter,第二组stream
  //
  StreamController _actionController = StreamController();
  StreamSink get incrementCounter => _actionController.sink;//这个暴露给外部,用户接受ui事件

  IncrementBloc(){
    _counter = 0;
    _actionController.stream.listen(_handleLogic);
  }

  void _handleLogic(data){
    _counter = _counter + 1;
    _inAdd.add(_counter);
  }
}

初次接触这种模式,你可能会稍感不适,没有任何关系,在心中把这个回路多跑即便,就清楚了,注意这里的BloC的设计上用到了两组stream,对,你没看错,是两组,两组形成了一个【闭环】,才能搞出这种【打法】。

因为第一组stream用户产生ui用的数据,第二组stream用户接受处理UI事件。

总结及个人建议

使用Bloc模式开发app的好处显而易见,大约有:

1、严重遵守了单一职责原则,代码解耦更好。

2、模块更加易于测试。

3、便面了setState的方式来触发build,可能性能更好,注意,只是可能,因为这也是大佬们说的,我并不太认可,实际上我认为,即便是使用streamBuilder,当stream有新的data时,也是触发了其包裹的组件走build的。

那么,你真的需要BloC模式吗?

1、个人觉得,非常简单的页面,使用BloC就有点过了,实际上像上面那个例子,点击次数计数,用StateFulWidget明显就是更优选择,使用BloC就有点为了模式而模式了。

2、用于不用BloC,要基于业务场景来考虑,个人觉得,对于多个UI共享一份数据的例子,就非常使用BloC模式,比如订单相关的页,购物车等等,因为订单状态的扭转,购物车物品同步,用户发送的事件相当多,这种如果使用BloC模式,显然会是目前来看的最优模式。

Redux相比大家也听过了,flutter中当然也是有的,那么,和Bloc有什么区别么?

1、个人觉得,并没有什么区别,都可以实现数据共享,大家也都能实现总线的功能,redux理解难度上,似乎还要比Bloc更加复杂点,因为他概念会多一些。

2、如果让我选择,我更加倾向于直接使用Bloc,最少的代码完成需求,比起引入一个库,话费的代价要少。

初学者的疑问

1、想bloc发送事件一定需要通过另外一个streamController么?答案是不一定,写成一个公开发送,直接操作那个数据相关的StreamController发送数据也可以,个人觉得这么写可能还更加简单呢?只是看自己以的业务逻辑吧。

2、必须要通过,final IncrementBloc bloc = BlocProvider.of<IncrementBloc>(context);获取bloc么?

我的回答是,必须有一个地方是的,就像弹吉他一样,根弦需要,其他的不需要而且不能需要,因为如果次级页面也通过这种方式获取的话,那他销毁时,dispose被回调,这个bloc也就销毁了,一级页面的bloc也就不能用了,这其实也是一个坑,很容就掉进去了,所以,区分什么时候通过final IncrementBloc bloc = BlocProvider.of<IncrementBloc>(context);获取,什么时候通过参数传递的方式传递过去,还是很重要的。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Flutter调试工具devTools是如何工作的

    然后,这不,你就会安装一下这些依赖库,如是,就可以对这个devtools的原理进行一个初步的分析。

    brzhang
  • 结合MTA来走出打造app性能数据监控平台的最后一公里

    App前期的工作主要以业务开发为主,在开发阶段,我们比较关注的是如何能个快速迭代开发,当这个紧锣密鼓的阶段结束之后,大多数App会走向稳定运营的阶段了,那么在业...

    brzhang
  • 如何利用node把别人的html变成你想要的json

    相信作为一个移动端的老狗,当你遇到一个有内涵的网站的时候,而且当你发现里面有太多的噪音尤其是铺天盖地的 广告 的时候,你是不是有种想把它净化一下的感觉呢?比如来...

    brzhang
  • 线程池-Threadlocal

    ThreadLoclc初衷是线程并发时,解决变量共享问题,但是由于过度设计,比如弱引用的和哈希碰撞,导致理解难度大、使用成本高,反而成为故障高发点,容易出现内存...

    DougWang
  • 面试题:Redis 的持久化有哪几种方式?

    redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?

    用户1263954
  • NVIDIA Jetson TX2核心板这些细节不注意,别怪坏了不修哟!

    集中反馈几个客户NVIDIA Jetson TX2核心板出问题,但由于这些细节问题,导致被认定为人损,从而无法获得NVIDIA正常一年内保修服务。

    GPUS Lady
  • Ruby 应用容器封装踩坑记录(Lobsters)

    本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

    soulteary
  • termux

    分享一个超好用的手机命令行高级终端软件termux,强大的安卓手机渗透工具,没有之一。

    AngelNH
  • 蚂蚁金服首席架构师:开源SQLFlow牛刀初试,实时大数据系统才是未来基石

    这就是蚂蚁金服近日开源首个将SQL应用于AI引擎项目SQLFlow后,业界给出的反应。

    养码场
  • ThreadLocal 源码解析

    多线程访问共享可变数据时,涉及到线程间数据同步的问题。并不是所有时候,都要用到 共享数据,所以线程封闭概念就提出来了。

    JavaEdge

扫码关注云+社区

领取腾讯云代金券

http://www.vxiaotou.com