菜单腾讯云备案控制台
云+社区
专栏问答沙龙快讯团队主页开发者手册云学院TVP
找文章 / 找答案 / 找技术大牛
找文章 / 找答案 / 找技术大牛
写文章提问登录注册
flutter中使用BloC模式
写文章
flutter中使用BloC模式
brzhang
腾讯 · 高级客户端开发工程师 (已认证)
原创分享资深作者
发表于玩转全栈
3.9K
业务逻辑组件
什么是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
/// 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
@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
/**
* 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
return Scaffold(
appBar: AppBar(title: Text('Stream version of the Counter App')),
body: Center(
///注意这里,通过stream构建ui
child: StreamBuilder
stream: bloc.outCounter,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot
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
StreamSink
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
我的回答是,必须有一个地方是的,就像弹吉他一样,根弦需要,其他的不需要而且不能需要,因为如果次级页面也通过这种方式获取的话,那他销毁时,dispose被回调,这个bloc也就销毁了,一级页面的bloc也就不能用了,这其实也是一个坑,很容就掉进去了,所以,区分什么时候通过final IncrementBloc bloc = BlocProvider.of
原创声明,本文系作者授权云+社区发表,未经许可,不得转载。
如有侵权,请联系 yunjia_community@tencent.com 删除。
编辑于 2018-12-02
其他
举报
玩转全栈
20 篇文章32 人订阅
flutter实现一个sideBar
flutter中event_bus实现原理
flutter全局数据共享通知方案
Android自动化测试+性能监控预警系统搭建
Android如何实现超级棒的沉浸式体验
我来说两句
0 条评论
登录 后参与评论
上一篇:Java的算数运算符、关系运算符、逻辑运算符、位运算符
下一篇:图解 | 保护现代数据中心的安全新方法?看这一篇就够了!
相关文章
来自专栏开源优测
大数据测试之ETL测试入门
概述 在我们学习ETL测试之前,先了解下business intelligence(即BI)和数据仓库。 什么是BI? BI(Business Intell...
6208
来自专栏开源优测
[大数据测试]ETL测试或数据仓库测试入门
概述 在我们学习ETL测试之前,先了解下business intelligence(即BI)和数据仓库。 什么是BI? BI(Business Intell...
3316
来自专栏Python中文社区
用Python玩转微信的正确姿势!
0. itchat 最近研究了一些微信的玩法,我们可以通过网页版的微信微信网页版,扫码登录后去抓包爬取信息,还可以post去发送信息。 然后发现了itchat这...
5098
来自专栏老秦求学
深入理解计算机系统读书笔记之第一章:漫游
我是从豆瓣上看到好多人都在推荐这本书,于是就去借来读一读,昨天晚上用了好长时间来读这本书的第一章节,感觉这本书比较符合我(有些基础还不太明白,这本书详细的进行了...
2857
来自专栏Golang语言社区
【Go 语言社区】在 Go 语言中,如何正确的使用并发
Glyph Lefkowitz最近写了一篇启蒙文章,其中他详细的说明了一些关于开发高并发软件的挑战,如果你开发软件但是没有阅读这篇问题,那么我建议你阅读一篇。这...
3469
来自专栏禅林阆苑
USTC高级软件工程课程学习心得 【原创】
USTC高级软件工程课程学习心得 Write By CS逍遥剑仙 我的主页: www.csxiaoyao.com GitHub: github...
3346
来自专栏黑白安全
Po主是谁?通过新浪微博图片反查上传者信息
链接为 https://wxt.sinaimg.cn/thumb300/9d0d09ably1fsn7m0jyzzj20m80cidgm.jpg 的图
882
来自专栏全栈数据化营销
用python采集猫眼电影排行榜信息
随着大数据和人工智能多次被大佬提及之后,并且被定义为未来的大趋势后,天然适合于大数据和人工智能的编程语言python也异常火热,市面上出现了不少的高价格、大规模...
2777
来自专栏开源优测
[大数据测试]ETL测试或数据仓库测试入门
概述 在我们学习ETL测试之前,先了解下business intelligence(即BI)和数据仓库。 什么是BI? BI(Business Intell...
3506
来自专栏Golang语言社区
在 Go 语言中,如何正确的使用并发
Glyph Lefkowitz最近写了一篇启蒙文章,其中他详细的说明了一些关于开发高并发软件的挑战,如果你开发软件但是没有阅读这篇问题,那么我建议你阅读一篇。这...
1850
扫描二维码
扫码关注云+社区0daybank
文章评论