这篇是使用 Bloc 来实现业务逻辑与UI分离。主要就是慕课网课程列表的网络请求并且展示。

首先定义一个基础事件的类,如下:

abstract class LessonEvent {}

然后我定义了3种事件:

  1. 发起网络请求事件:
class FetchDataEvent extends LessonEvent {}
  1. 网络请求成功事件:
class FetchDataSuccessEvent extends LessonEvent {}
  1. 网络请求失败的事件:
class FetchDataFailedEvent extends LessonEvent {}

然后定义一个枚举类型,来表示这3种事件,这样就可以在 BlockBuild 中根据枚举类型来展示不同的 UI 了。

enum LoadStatus { loading, success, failed }

课程的数据结构如下:

import 'package:json_annotation/json_annotation.dart';part 'LessonBean.g.dart';@JsonSerializable()
class LessonBean {int status;String msg;List<LessonDetail> data;LessonBean({required this.status, required this.msg, required this.data});factory LessonBean.fromJson(Map<String, dynamic> srcJson) =>_$LessonBeanFromJson(srcJson);Map<String, dynamic> toJson() => _$LessonBeanToJson(this);@overridebool operator ==(Object other) {if (identical(other, this)) return true;if (other is LessonBean) {return other.runtimeType == this.runtimeType &&other.status == this.status &&other.msg == this.msg &&other.data == this.data;} else {return false;}}@overrideint get hashCode {int result = 17;result = 37 * result + status.hashCode;result = 37 * result + msg.hashCode;result = 37 * result + data.hashCode;return result;}
}@JsonSerializable()
class LessonDetail {final int id;final String name;final String picSmall;final String picBig;final String description;LessonDetail({required this.id,required this.name,required this.picSmall,required this.picBig,required this.description});factory LessonDetail.fromJson(Map<String, dynamic> srcJson) =>_$LessonDetailFromJson(srcJson);Map<String, dynamic> toJson() => _$LessonDetailToJson(this);@overridebool operator ==(Object other) {if (identical(other, this)) {return true;} else if (other is LessonDetail) {return other.runtimeType == this.runtimeType &&other.id == this.id &&other.name == this.name &&other.picSmall == this.picSmall &&other.picBig == this.picBig &&other.description == this.description;} else {return false;}}@overrideint get hashCode {int result = 17;result = 37 * result + id.hashCode;result = 37 * result + name.hashCode;result = 37 * result + picSmall.hashCode;result = 37 * result + picBig.hashCode;result = 37 * result + description.hashCode;return result;}
}

重写了其中的 hashcode 和重载了运算符 ==

然后再定义一个课程的包装类,包含了 LessonBean 和 枚举类型 LoadStatus,如下:

class LessonWrap {LessonBean? lessonBean;LoadStatus status;LessonWrap({required this.lessonBean, required this.status});
}

这个包装类型 LessonWrap 将作为自定义 Bloc 的输出,然后 BlockBuilder中就会观察到数据的改变。

LessonBloc 的代码如下:

class LessonBloc extends Bloc<LessonEvent, LessonWrap> {LessonBean? _lessonBean;LessonBloc(LessonWrap initial) : super(initial) {on<FetchDataEvent>((event, emit) {_requestData();});on<FetchDataSuccessEvent>((event, emit) {emit(LessonWrap(lessonBean: _lessonBean, status: LoadStatus.success));});on<FetchDataFailedEvent>((event, emit) {emit(LessonWrap(lessonBean: null, status: LoadStatus.failed));});/// 初始化 bloc 的时候就去请求数据add(FetchDataEvent());}void _requestData() async {_lessonBean = await MukeService().getPersonalLesson(7);/// 接口请求太快了,看不到loading圈,所以加个延时Timer(Duration(seconds: 2), () {if (_lessonBean == null) {add(FetchDataFailedEvent());} else {add(FetchDataSuccessEvent());}});}
}

页面展示的逻辑如下:

class BlocInstanceWidget extends StatelessWidget {Widget getRow(LessonDetail detail) {return GestureDetector(child: Container(color: Colors.blueGrey,width: double.infinity,padding: EdgeInsets.only(bottom: 10, left: 10, top: 10),child: Column(mainAxisAlignment: MainAxisAlignment.start,crossAxisAlignment: CrossAxisAlignment.start,mainAxisSize: MainAxisSize.min,children: [Text("Row >>>  ${detail.name}"),SizedBox(height: 5,),Container(child: ClipRRect(// 圆角图片borderRadius: BorderRadius.circular(8),child: Image(image: NetworkImage(detail.picSmall),width: 60,height: 60,fit: BoxFit.fitHeight,),),),SizedBox(height: 6,),Text(detail.description,style: const TextStyle(color: Colors.brown,fontWeight: FontWeight.bold,fontStyle: FontStyle.italic),),],),),);}@overrideWidget build(BuildContext context) {LessonBloc lessonBloc = BlocProvider.of<LessonBloc>(context);return Scaffold(appBar: AppBar(title: Text('Bloc Instance'),),body: Container(child: Column(children: [BlocBuilder<LessonBloc, LessonWrap>(builder: (context, wrap) {if (wrap.status == LoadStatus.loading) {return Container(margin: EdgeInsets.only(top: 300),// color: Color(0xffFFFFFF),alignment: Alignment.center,child: CircularProgressIndicator(color: const Color(0xff2A9DFF).withOpacity(0.5),));} else if (wrap.status == LoadStatus.success) {/// 因为 在 Column 中嵌套 ListView ,所以需要加上 Expanded ,/// 否则报 Vertical viewport was given unbounded heightreturn Expanded(child: ListView.separated(shrinkWrap: true,itemCount: wrap.lessonBean!.data.length,itemBuilder: (BuildContext context, int position) {return getRow(wrap.lessonBean!.data[position]);},separatorBuilder: (context, index) {return Divider(height: 1.0, indent: 10, color: Colors.black);},));} else {return Text("请求数据出错!!!!");}},buildWhen: (previous, next) {/// 过滤条件,只有2次数据不一致时才刷新return previous.lessonBean != next.lessonBean;},),ElevatedButton(onPressed: () {/// 由于我加了过滤条件,即上面的 buildWhen,/// 因为2次刷新的数据是一样的(我重写了 LessonBean 的 hashcode 和 '=='),所以不会刷新界面lessonBloc.add(FetchDataEvent());},child: Text('刷新数据')),],),),);}
}

入口代码如下:

void main() {runApp(MaterialApp(home: BlocProvider<LessonBloc>(create: (context) => LessonBloc(LessonWrap(lessonBean: null, status: LoadStatus.loading)),child: BlocInstanceWidget())));
}

Flutter 状态管理之Bloc下相关推荐

  1. Flutter 状态管理之Bloc上

    前言:Flutter 的状态管理插件有很多,比如 Provider,GetX 还有本篇要讲述的 Bloc .Bloc 目前最新的版本是 flutter_bloc: ^8.0.1. BLoC 依赖 St ...

  2. FlyAI小课堂:Flutter 状态管理之BLoC

    在正式介绍 BLoC之前, 为什么我们需要状态管理.如果你已经对此十分清楚,那么建议直接跳过这一节. 如果我们的应用足够简单,Flutter 作为一个声明式框架,你或许只需要将 数据 映射成 视图 就 ...

  3. Flutter 状态管理指南之 Provider

    2019 Google I/O 大会,Flutter 团队在"Pragmatic State Management in Flutter "演讲上正式介绍了 Provider.自此 ...

  4. flutter 状态管理 flutter_bloc 的使用以及总结

    Bloc介绍 flutter_bloc 8.0,直接把mapEventToState方法去掉,需要手动注册事件处理器,不用再写if else 来判断event ,也不用写yield flutter_b ...

  5. Flutter状态管理1-ChangeNotifierProvider的使用

    关于Flutter中的状态管理,可以参考官网的介绍:Simple app state management 中文网的介绍:简单的应用状态管理 Flutter 官方的两个sample: provider ...

  6. Flutter 状态管理

    目录 一.状态管理简介 1.1 为什么需要状态管理 1.1.1 Flutter与adr/ios UI框架区别 1.1.2 状态管理框架使用场景 1.2 需要解决的问题 二.状态管理框架现有方案调研 2 ...

  7. Flutter:状态管理(5) --BLoC

    应用的开发架构:BLoC 其意思是业务逻辑组件,其实就是把业务需要的一些逻辑单独拿出来放在一个类里面.这种类就叫Bloc 可以使用这种架构创建反应式的应用. 这种架构是Google发出来的,其开源AP ...

  8. 【flutter-mobx】Flutter 状态管理- 使用 MobX实现计数器

    实践flutter mobx: 提示:对下面这篇掘金文章的实践,通过flutter结合 mobx 实现计数器,过程中也添加了一些处理和注释,代码放在feat/mobx下 https://juejin. ...

  9. Flutter 超简单状态管理

    Flutter 状态管理 目前Flutter已经有许多状态管理的方案,但就我个人而言,并不能完全满足我的要求.我希望状态管理更加简单,而不是成为负担,我希望状态管理更加可靠,而不是使用过于复杂的实现. ...

最新文章

  1. 安装python程序后要进行什么设置-安装好Pycharm后如何配置Python解释器简易教程...
  2. linux 程序 加密码忘了怎么办,linux忘记了密码怎么办
  3. 募捐名单_年度捐款,以及为什么现在应该捐款,而不是以后
  4. MySQL和Oracle的添加字段的处理差别
  5. 在计算机回收站中可以恢复,电脑回收站删除了怎么恢复吗
  6. java调用其他程序吗_java本地方法如何调用其他程序函数,方法详解
  7. 区块链开发公司:区块链技术如何改变个人数据安全
  8. if 与 while
  9. android热成像模块,Thermal Camera
  10. java https pfx_使用HttpClient携带pfx证书调用HTTPS协议的WebService
  11. LeCo-82.删除排序链表中的重复元素(二)
  12. “百度杯”CTF比赛 十一月场Look
  13. Android手机上,利用bat脚本模拟用户操作
  14. ps ctrl+shift+j 切割瓶子
  15. mysql查找名字含A的的姓名_mysql查询结果排序,name字段有中文名字,有英文名字,如何按a-z的顺序把名字排序...
  16. hilbert算法的c语言实现,关于希尔伯特变换的 c语言实现
  17. java自定义函数式接口
  18. 机器学习算法——决策树4(剪枝处理)
  19. bzoj4920: 薄饼切割
  20. NFT行业名词、用语篇

热门文章

  1. 24部经典电影的24句话的24个哲理
  2. 自动驾驶(七)---------初探轨迹规划
  3. android极光推送原理,【揭秘】极光推送ios、Android消息推送达率的原理
  4. hive窗口函数必备宝典
  5. 国产数据库人大金仓踩坑记录和函数适配
  6. 数据库(事务的四大特性)
  7. 使用 VMware 安装 CentOS(一)
  8. vue项目接入视频监控系列-------播放器的选择
  9. 深入理解 Java中的 流 (Stream)
  10. Windows下JMC8.1.0以上版本启动失败