【Flutter 专题】99 初识 EventBus
和尚在 Android 开发过程中经常会用到 EventBus 事件分发机制,EventBus 遵从 publish/subscribe 模式,即发布/订阅模式;简化了模块之间通信,对于项目的解耦很实用;而 Flutter 也提供了相应的 event_bus 插件,今天和尚学习尝试一下;
EventBus
源码分析
class EventBus {
StreamController _streamController;
StreamController get streamController => _streamController;
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
EventBus.customController(StreamController controller)
: _streamController = controller;
Stream<T> on<T>() {
if (T == dynamic) {
return streamController.stream;
} else {
return streamController.stream.where((event) => event is T).cast<T>();
}
}
void fire(event) {
streamController.add(event);
}
void destroy() {
_streamController.close();
}
}
简单分析源码可得,EventBus 核心主要是通过 Stream 来进行事件分发的,其中初始化时会创建一个 StreamController.broadcast(sync: sync) 广播流;fire() 广播发送方法主要是向 StreamController 中添加事件,on() 为广播监听,都是对 Stream 流操作;
案例尝试
和尚尝试做一个主题切换的小尝试,同时尝试了 EventBus 和 Provider 两种方式;
1. EventBus 初始化
和尚首先创建一个全局的 EventBus,通常每个应用只有一个事件总线,但如果需要多个事件总线的话可以在初始化时设置 sync = false;
EventBus eventBus = EventBus();
2. EventBus 定义事件
和尚创建两个自定义事件,分别为语言切换和主题色切换;使用方法和 Android 一致;
class LanguageEvent {
String languageType;
LanguageEvent(this.languageType);
}
class ThemeColorEvent {
Color color;
ThemeColorEvent(this.color);
}
3. EventBus 发布事件
EventBus 通过 fire(event) 进行事件分发;
return GestureDetector(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 14),
child: Row(children: <Widget>[
_itemColorWid(_colorList[index]),
Expanded(child: dataIndex == 1 ? _itemColorWid(_colorList[index]) : Text(_languageList[index])),
SizedBox(width: 20),
Icon(Icons.done)
])),
onTap: () {
eventBus.fire(dataIndex == 1 ? ThemeColorEvent(_colorList[index]) : LanguageEvent(_languageList[index]));
Navigator.pop(context);
});
4. EventBus 接收事件
对于 EventBus 的接收可以通过 on(event).listen() 来监听;其中若 on() 可以监听所有事件也可以监听固定的事件,区别是是否限制当前广播;
// 监听所有广播
eventBus.on().listen((event) {
if (event is LanguageEvent) {
print('EventBus --> LanguageEvent --> ${event.languageType}');
} else if (event is ThemeColorEvent) {
themeColor = event.color;
print('EventBus --> ThemeColorEvent --> ${event.color}');
}
print('EventBus --> Type --> ${event.runtimeType}');
});
// 监听固定类型广播
eventBus.on<LanguageEvent>().listen((event) {
print('EventBus.on<LanguageEvent>() --> ${event.languageType}');
});
5. EventBus 销毁
为了防止内存泄漏,一般在应用销毁时都需要对 EventBus 进行销毁;
eventBus.cancel();
eventBus.destroy();
和尚预想的是在 main.dart 的 runApp(MyApp()) 中直接更改 ThemeData,但是 MyApp() 是 StatelessWidget 无状态类型的,虽然可以通过 EventBus 监听切换主题,但是直接更新 UI 相对复杂一些;此时和尚尝试用 Provider 来进行主题切换,Provider 核心是 InheritedWidget 可以直接更新主题色;
1. Provider 定义事件
class ThemeColorNotifier with ChangeNotifier {
Color themeColor;
Color get getThemeColor => themeColor;
setThemeColor(getThemeColor) {
themeColor = getThemeColor;
notifyListeners();
}
}
2. Provider 发送通知
_itemClick(dataIndex, index) {
return Consumer<ThemeColorNotifier>(builder: (context, themeColor, _) {
return GestureDetector(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 14),
child: Row(children: <Widget>[
_itemColorWid(_colorList[index]),
Expanded(child: dataIndex == 1 ? _itemColorWid(_colorList[index]) : Text(_languageList[index])),
SizedBox(width: 20),
Icon(Icons.done)
])),
onTap: () {
themeColor.setThemeColor(_colorList[index]);
Navigator.pop(context);
});
});
}
3. Provider 接收通知
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ThemeColorNotifier())
],
child: Consumer<ThemeColorNotifier>(builder: (context, themeColor, _) {
return _mainProviderWid(themeColor);
}));
_mainProviderWid(themeColor) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: themeColor != null ? themeColor.getThemeColor : Colors.blue),
home: MyHomePage(title: 'Flutter Demo Home Page'),
routes: _commonRoute());
}
和尚尝试了 EventBus 和 Provider 两种方式进行主题色切换,对于不同的场景可以自由选择;给和尚最直接的感觉是 EventBus 主要是事件分发,只发送/接收数据,更偏向于数据层,而 Provider 实际是对 InheritedWidget 的优化和封装,可以在发送/接收数据同时更新 UI 层;
小扩展
和尚在测试过程中在设置 ListView 对话框时出现如下错误:
I/flutter (28408): The following assertion was thrown during performLayout():
I/flutter (28408): RenderShrinkWrappingViewport does not support returning intrinsic dimensions.
I/flutter (28408): Calculating the intrinsic dimensions would require instantiating every child of the viewport, which
I/flutter (28408): defeats the point of viewports being lazy.
I/flutter (28408): If you are merely trying to shrink-wrap the viewport in the main axis direction, you should be able
I/flutter (28408): to achieve that effect by just giving the viewport loose constraints, without needing to measure its
I/flutter (28408): intrinsic dimensions.
和尚测试可以设置 ListView 中 Container 宽或高即可,也可以将 ListView 包裹在容器中并为其设置宽度为 double.maxFinite;
_itemDialog(context, dataIndex) {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Row(children: <Widget>[
Icon(Icons.settings), SizedBox(width: 8),
Text(dataIndex == 1 ? 'Theme Color' : 'Language')
]),
content: Container(
width: double.maxFinite,
child: ListView.builder(
itemCount: dataIndex == 1 ? _colorList.length : _languageList.length,
physics: ScrollPhysics(),
primary: false, shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return _itemClick(dataIndex, index);
})));
});
}
和尚仅是在应用中尝试了 EventBus 并未对源码进行系统的研究,涉及还很浅显;如有错误请多多指导!
来源:阿策小和尚
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- leetcode1552题解【二分+贪心】
- 这三年被分布式坑惨了,曝光十大坑
- 为什么删数据也提示空间不足呢?
- 前端踩坑系列《六》——让人又爱又恨的npm包
- docker运行Tomcat后访问首页报404(详细永久解决步骤)
- vue.js如何快速入门第1篇
- 3.列表-HTML基础
- 启动时间的一些分析
- 1.HTML基础知识-HTML进阶
- 浅析一个postgresql的死锁问题
- 你的第一个React App (二 ) - 应用组件开发
- 在Angular应用里使用Redux
- Elasticsearch 日志配置详解
- Cypress系列(52)- fixture() 命令详解
- Docker学习笔记---Dockerfile