Stream: https://www.yuque.com/zhuchaoyang/wcyoce/orhnkz#T6PJ8
全局事件总线
在APP中,我们经常会需要一个广播机制,用以跨页面事件通知,比如一个需要登录的APP中,页面会关注用户登录或注销事件,来进行一些状态更新。这时候,一个事件总线便会非常有用,事件总线通常实现了订阅者模式,订阅者模式包含发布者和订阅者两种角色,可以通过事件总线来触发事件和监听事件,本节我们实现一个简单的全局事件总线,我们使用单例模式,代码如下:
lib/services/bus_login.dart
// 订阅者回调签名// typedef 给某一种特定的函数类型起了一个名字,可以认为是一个类型的别名,// 可以类比class和对象这样理解:自己定义了一种数据类型,// 不过这种数据类型是函数类型,一个一个的具体实现的函数就相当于按照这种类型实例化的对象会有类型检查typedef EventCallback(arg);class EventBusLogin {// 命名构造函数EventBusLogin._internal() {}// 保存单例static EventBusLogin _instance = EventBusLogin._internal();// 工厂构造函数factory EventBusLogin() => _instance;// 保存事件订阅者队列,key: 事件名(id), value: 对应事件的订阅者队列var _emap = Map<Object, List<EventCallback>>();// 添加订阅者on(eventName, EventCallback fn) {if (eventName == null || fn == null) return;_emap[eventName] ??= List<EventCallback>();_emap[eventName].add(fn);}// 移除订阅者off(eventName, [EventCallback fn]) {var list = _emap[eventName];if (eventName == null || list == null) return;if (fn == null) {_emap[eventName] = null;} else {list.remove(fn);}}// 触发事件,事件触发后该事件所有订阅者会被调用emit(eventName, [arg]) {var list = _emap[eventName];if (list == null) return;// 反向遍历,防止订阅者在回调中移除自身带来的下标错位int len = list.length - 1;for (var i = len; i > -1; --i) {list[i](arg);}}}// 定义一个 top-level(全局)变量,页面引入该文件后可以直接使用 busLoginvar busLogin = EventBusLogin();
A 页面
import 'package:app1/services/bus_login.dart';@overridevoid initState() {super.initState();//监听登录事件busLogin.on('login', (arg) {print('test-login => $arg'); //test-login => {id: 1, username: 朱朝阳}});}
login页面
//登录成功后,触发登录事件,页面A中订阅者会被调用busLogin.emit('login', {'id': 1,'username': '朱朝阳',});
注意:Dart中实现单例模式的标准做法就是使用static变量+工厂构造函数的方式,这样就可以保证
new EventBus()始终返回都是同一个实例,读者应该理解并掌握这种方法。
事件总线通常用于组件之间状态共享,但关于组件之间状态共享也有一些专门的包如redux、以及前面介绍过的Provider。对于一些简单的应用,事件总线是足以满足业务需求的,如果你决定使用状态管理包的话,一定要想清楚您的APP是否真的有必要使用它,防止“化简为繁”、过度设计。
库 event_bus
https://pub.dev/packages/event_bus
安装依赖
dependencies:event_bus: ^1.1.1
导入
import 'package:event_bus/event_bus.dart';
使用
定义event_bus服务
import 'package:event_bus/event_bus.dart';// 初始化 busEventBus eventBus = EventBus();// 登录成功事件(自定义广播数据)class LoginSucEvent {Map<String, dynamic> userInfo;LoginSucEvent(this.userInfo);}
触发事件
eventBus.fire(LoginSucEvent({'id': 1,'userInfo': '朱朝阳',}));
监听事件
@overridevoid initState() {super.initState();// 监听指定事件eventBus.on<LoginSucEvent>().listen((event) {print('LoginSucEvent => ${event.userInfo}'); //LoginSucEvent => {id: 1, userInfo: 朱朝阳}// 监听到登录成功广播,接着做登录成功后的操作// ...});// 监听所有事件eventBus.on().listen((event) {print(event.runtimeType); //LoginSucEventprint('LoginSucEvent => ${event.userInfo}'); //LoginSucEvent => {id: 1, userInfo: 朱朝阳}});}
销毁事件
eventBus.destroy();
源码
import 'dart:async';class EventBus {//StreamController 是 Stream 的一个帮助类,可用于整个 Stream 过程的控制。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();}}
