内容简介:widget只做UI展示,bloc实现控制逻辑,model做数据封装。 我在运营线的新需求开发中实际使用了bloc做了一个新的页面,就是商机败北页面。 这个页面有三级的败北原因选择,败北原因是级联的,还有每次选择设备后要重新请求库存,提交败北等操作。在同一个页面中跨widget数据获取数据和操作比较多,例如提交败北的时候,需要获取多个子widget中的数值,如果没有使用bloc的话,就需要在子widget的构造方法中传递model或回调方法。如果页面层级较多,这种model和回调方法就需要逐级传递。 例如在
widget只做UI展示,bloc实现控制逻辑,model做数据封装。 我在运营线的新需求开发中实际使用了bloc做了一个新的页面,就是商机败北页面。 这个页面有三级的败北原因选择,败北原因是级联的,还有每次选择设备后要重新请求库存,提交败北等操作。
2).实现跨层级model访问和方法调用
在同一个页面中跨widget数据获取数据和操作比较多,例如提交败北的时候,需要获取多个子widget中的数值,如果没有使用bloc的话,就需要在子widget的构造方法中传递model或回调方法。
如果页面层级较多,这种model和回调方法就需要逐级传递。 例如在下图左边,需要把submiModel或回调方法从最上层一直传递到最底部,才能做到收集所有的子widget中的信息,最终集合所有的widget中的参数到submitModel中,提交败北请求。
下面右边的代码就是这样逐层传递参数的方法,如果层级很多的话会非常头疼,不仅要重复的声明变量,而且如果修改的话要每个地方都改。
而使用了bloc的话,构造函数就特别干净,不需要逐层传递model或者回调方法。
下面的两块代码都是页面widget结构中最下层的两个widget,不需要在构造方法中传递任何参数或回调方法,但是同样能调用拿到model和调用回调方法。怎么实现的继续往下看。:)
3).不直接使用setState方法(StreamController)
之前在项目中直接使用setState会产生一些问题,例如当前State是unmounted状态,这时直接调用setState会抛异常。
而使用bloc的话刷新页面是使用如下方式。关键的代码是_requestController.add
4). 更加简洁的局部刷新(StreamBuilder)
我们现有项目中有一些非常复杂的页面,但是只使用了一个文件,所有的业务逻辑和widget组件都在这个页面里面。
下面是一段这种混杂网络请求、widget显示的代码示例。
有多个网络请求和对应的数据显示widget,不论哪个请求返回或者用户点击事件,只要调用一个setState就能刷新页面更新显示。
这样相比于多个子widget的方式就不用逐层传递数据和回调方法。但是破坏了面向对象开发的基本原则:信息隐藏,比如修改某个widget的一个小功能,因为在一个很大的文件中修改,其中业务逻辑错综复杂,改一个小功能可能就会影响其他逻辑,产生意料不到的后果,这一块的代码就变得非常难以维护。
同时直接调用setState刷新整个页面的性能也有问题,也会产生抛异常的问题。
而使用bloc的方式会更加自由,可以像第二点中的图中一样使用多个子widget,在子widget中进行刷新。
也可以直接改造上图的代码,在一个widget中实现局部刷新。
第四个优点其实和第三个是一并实现的,都是通过streamBuidler和streamController,这两者是配套使用的。 其实bloc就是streamBuilder+streamController+ancestorWidgetOfExactType,和web端的ajax比较类似,几项现有技术整合出了新的东西 。
StreamBuilder要传入一个stream对象才能实现刷新,而这个stream要从streamController中获取的。
当在上面的代码中调用_requestController.add(deviceModel)后,下面的StreamBuilder就会自动调用builder方法,实现局部刷新StreamBuilder,而不是刷新外层的InkWell。
StreamBuilder是一个Widget,可以在任意地方插入,实现局部刷新非常简单。
2.简单示例代码
要实现一个BlocProvider,现有项目中已经集成,整个项目只需要一个BlocProvider,可以放在Utils目录中。
实现Bloc其实就只用这一个类和flutter自带的方法就行了,代码非常简单,并不需要在yaml文件中引入第三方库。
所有的bloc都需要继承BlocBase,通常一个bloc对应一个会刷新页面的业务逻辑(如网络请求、切换tab)。
bloc初始化可以在 任意父页面、爷爷页面中,获取只需要使用BlocProvider.of(context)。
使用bloc代码如下:
有三点需要注意的。 1.bloc要保存在state中,不然会因为widget重新构建而丢失。 2.bloc需要调用dispose,在bloc中的dispose中会调用streamController的close方法,最好把bloc的dispose和create在同一个state中调用。 3.BlocProvider要在更外面一层创建。
下面是bloc创建和blocProvider的代码。
3.bloc+rxdart实现原理
1).bloc实现原理
bloc触发刷新的方式就是使用StreamBuilder+StreamController,但是直接使用StreamController有一个很大的缺点就是只能进行一对一的listen,且模式非常单一。所以需要使用到rxdart,下面会讲到。
bloc另一重要特性就是跨层级获取bloc,在bloc可以获取到model或者调用方法。
其中使用了ancestorWidgetOfExactType,因为flutter widget是树状结构的,结构层次保存在BuildContext中,可以从子节点依次往父节点找符合类型的widget,所以所有使用了BlocProvider包裹的widget都可以通过of方法找到,再返回widget中的bloc。(bloc最终是保存在外一层的state中)。
final type = _typeOf<BlocProvider<T>>(); BlocProvider<T> provider = context.ancestorWidgetOfExactType(type); 复制代码
在下图中,最下层的widget调用of方法后,可以直接获取到顶层页面的submitBloc,因为flutter widget是树状结构的,结构层次保存在BuildContext中,可以从子节点依次往父节点找符合类型的widget,所以所有使用了BlocProvider包裹的widget都可以通过of方法找到,再返回widget中的bloc。(bloc最终是保存在外一层的state中)。
2).rxdart实现原理
StreamController实现了观察者模式,监听者不是直接被调用,而是处于观察状态,当event加入streamController后,监听者获得异步回调。
rxdart是对StreamController的扩展,提供了更多的模式。分为两个部分Subject和Observable。
其中Observable对stream封装后提供多个处理方法,例如map、expand、merge、every、contact。
var obs = Observable(Stream.fromIterable([1,2,3,4,5])) .map((item)=>++item); obs.listen(print); 输出:2 3 4 5 6 复制代码
Subject是对StreamController的扩展,常用的有以下几种。
PublishSubject:StreamController广播版,streamController只能有一个listener,PublishSubject可以多次listen。下面的其他几种Subject也都是广播版。
BehaviorSubject: 缓存最近一次的事件。如果先发生event,后listen,也能收到缓存的event。
ReplaySubject: 缓存所有的事件,之前加入的所有event,listen后,都会顺序发送过来。
4.经典使用场景
1).多个网络图片组件共用一个http下载。
开发zn_web_image这个网络图片下载缓存组件的时候,为了测试数据加载缓存的请求,把图片链接放在一个数组中并多次重复。发现同样的图片链接在上面已经下载完成后,下面还会重复下载。
发现这是由于每次都创建新的bloc导致的,通过使用一个bloc list解决了这个问题。
static ZNImageBLocList _getInstance() { if (_instance == null) { _instance = new ZNImageBLocList._internal(); } return _instance; } List<ZNImageBloc> blocList; ZNImageBloc getBloc(String url,ZNImageConfig config){ for(ZNImageBloc bloc in blocList){ if(bloc.imageUrl==url){ return bloc; } } ZNImageBloc bloc = new ZNImageBloc(url, config); blocList.add(bloc); return bloc; } 复制代码
class ZnWebImageState extends State<ZnWebImage> { ZNImageBloc bloc; @override void dispose() { // TODO: implement dispose bloc.dispose(); super.dispose(); } @override void initState() { // TODO: implement initState super.initState(); bloc = ZNImageBLocList.instance.getBloc(widget.url, widget.config); } @override Widget build(BuildContext context) { // TODO: implement build return BlocProvider<ZNImageBloc>( bloc: bloc, child: ZNImageContainer(), ); } } 复制代码
这样多个zn_web_image的widget就能共用同一个bloc,在下载过程中同步显示进度条,下载完成后同时收到通知显示下载好的图片。
2).tabBarView中多个页面共用一个是否通过认证属性。
bloc在征信项目中也解决了一个很头疼的问题,就是否通过认证的状态,这个状态全局共用,且有多个变更入口,认证状态变更后要求所有页面更新显示。
用到个人认证的地方有多个入口:
1.第一个tab中的首页顶部。
2.确认订单页面,可以从第一个tab首页进入,也可以从第二个tab的订单列表进入。
3.第四个tab的个人中心页面。
最开始没有使用bloc的时候,写了多个网络请求获取认证状态,isAuth属性也在每个页面分别保存。
1.首页通过requestAuth从服务器端获取isAuth属性。
2.确认订单页面也需要从requestAuth获取isAuth属性。虽然首页通过请求获取到了isAuth属性,从首页进入确认订单页面可以传递isAuth属性。但是从订单列表进入时没有这个属性,订单列表是和首页同时初始化的,还是需要网络请求。
3.个人中心页面也需要从requestAuth获取isAuth属性。虽然首页通过请求获取到了isAuth属性,但是个人中心页面是和首页同时在tabbarView中初始化的,不能通过构造方法传递isAuth属性。
第二个问题就是从其中一个页面进入认证页面完成认证后,其他所有页面都需要刷新认证状态,没有使用bloc时,在这些页面的生命周期方法中写了重新发起网络请求来刷新isAuth状态。
这样非常的麻烦,而且工作量大很多。
使用bloc之后,isAuth这样全局共用的属性,只需要在MaterialApp外面加一层BlocProvider就行了,任何一个子页面都能直接获取isAuth,并且能同步isAuth的状态更新,刷新UI显示。
//个人认证bloc CreditAuthBloc authBloc; @override void dispose() { authBloc.dispose(); super.dispose(); } @override void initState() { super.initState(); authBloc = CreditAuthBloc(); } @override Widget build(BuildContext context) { return BlocProvider<CreditAuthBloc>(bloc: authBloc,child: MaterialApp( title: '', initialRoute:'/', home: Scaffold( body: Column( children: <Widget>[ Expanded( child: TabBarView(physics: NeverScrollableScrollPhysics(),controller: tabController, children: [ ZNMarketPage(),//首页 ZNOrderList(),//订单列表 ZNBill(), ZNMyCenter(),//个人中心 ]), ), ], )), ),); } 复制代码
子页面获取bloc
//使用bloc中的auth // int userAuthStatus = 0; //个人认证 0: 未认证, 1: 已认证 CreditAuthBloc authBloc; @override void didChangeDependencies() { // TODO: implement didChangeDependencies super.didChangeDependencies(); authBloc = BlocProvider.of<CreditAuthBloc>(context); } 复制代码
通过streamBuilder使用bloc
以上所述就是小编给大家介绍的《bloc+rxdart 项目实战,flutter的mvc方案》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 日志服务与SIEM(如Splunk)集成方案实战
- Kubernates网络解决方案技术原理深入剖析-Kubernates商业环境实战
- 跨域认证解决方案-JSON WEB TOKEN讲解与实战
- HTML5 容器入门解析:支付宝 Hybrid 方案原理与实战
- Android组件化方案及组件消息总线modular-event实战
- HBase阻塞急救与朱丽叶暂停线上环境解决方案-OLAP商业环境实战
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
七周七语言(卷2)
【美】Bruce A. Tate(泰特)、Fred Daoud(达乌德)、Ian Dees(迪斯) / 7ML翻译组 / 人民邮电出版社 / 2016-12 / 59
深入研习对未来编程具有重要意义的7种语言 Lua、Factor、Elixir、Elm、Julia、Idris和MiniKanren 本书带领读者认识和学习7种编程语言,旨在帮助读者探索更为强大的编程工具。 本书延续了同系列的畅销书《七周七语言》《七周七数据库》和《七周七Web开发框架》的体例和风格。 全书共8章,前7章介绍了Lua、Factor、Elm、Elixir、Jul......一起来看看 《七周七语言(卷2)》 这本书的介绍吧!