flutter bus 总线设计(iOS通知)
本篇是利用 stremcontroller.broadcast 实现了类似 iOS 通知功能,具备跨组件1对多消息通知.
业务场景平时也十分多,比如用户更新用户信息,消息接收修改会话,红点,消息列表更新会话等等,都是需要多处监听,比如游客模式到用户登录,十几个页面的更新
说到游客模式到登录,getx 中多语言切换使用了forceAppUpdate感兴趣的可以看看这里,不知道是不是个黑科技
或者可以根App 中加个 GetBuilder,执行 update(),毕竟游客模式到登录模式需要刷的页面太多了.
回归正题, bus项目代码也十分简单,所以直接上代码 附带加一些说明.
import 'dart:async';
import 'package:event_bus/event_bus.dart';
import 'package:flutter_psd/common/services/bus/index.dart';
// MARK: - --------------------------------------busName
class EventName {
/// 用户信息更新
static const user_info = "user_info";
}
// MARK: - --------------------------------------bus
final bus = Bus();
class Bus {
StreamController _streamController;
StreamController get streamController => _streamController;
Bus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
/// 监听事件
Stream<dynamic> on(String eventName) {
return streamController.stream
.where((event) {
try {
return (event as BusEvent).eventName == eventName;
} catch (e) {
return false;
}
})
.map((event) => (event as BusEvent).data)
.cast<dynamic>();
}
/// 发送事件
void emit(String eventName, {dynamic data}) {
var event = BusEvent(eventName: eventName, data: data);
streamController.add(event);
}
/// 销毁事件
void fire(StreamSubscription? subscription) {
subscription?.cancel();
}
void destroy() {
_streamController.close();
}
}
首先要说明的是,因为这是应用内框架,所有我设计的代码,都是以我认为比较好的实现来写的,如果大家有各自更好的思路,这里也希望抛砖引玉.
我这里就不讲什么单例写法了,直接 new 了一个全局对象做单例,比较是自己的项目,自己遵守好规范即可,bus类总共主要就三个方法.
监听事件
/// 监听事件
Stream<dynamic> on(String eventName) {
return streamController.stream
.where((event) {
try {
return (event as BusEvent).eventName == eventName;
} catch (e) {
return false;
}
})
.map((event) => (event as BusEvent).data)
.cast<dynamic>();
}
我设计的是你监听事件名,然后我传给你我能提供的数据.
使用时
userInfoSubs = bus.on(EventName.user_info).listen((event) {
update();
});
pub.dev 上有一个比较高星的三方event_bus,我其实也是按照他的源码改成我自己的框架,他在监听方面推荐我们使用强类型监听转换,这样做可以强制接收类型,但需要 new class,但我用习惯了 iOS 的通知,我个人更愿意的是对消息的事件名做声明,毕竟是做项目,接收数据 dynamic 大家都应该知道业务中自己需要的是啥,如果真不喜欢,也可以自己做一层 transform(compactMap).
//event_bus 源码 on 方法
Stream<T> on<T>() {
if (T == dynamic) {
return streamController.stream as Stream<T>;
} else {
return streamController.stream.where((event) => event is T).cast<T>();
}
}
发送数据
/// 发送事件
void emit(String eventName, {dynamic data}) {
var event = BusEvent(eventName: eventName, data: data);
streamController.add(event);
}
使用时
bus.emit(EventName.user_info);
emit 是因为 bus 这个概念是我在接触 vue 时知道的,当时他们使用的发送都是 emit,所以这里我也就没有使用 iOS 通知的 post,其实 String做参数名还不能够做到足够的类型约束,如果可以使用 swift 的 protocol 做限制,就可以不允许他们手写事件名,我对数据接收 data 是可以允许做dynamic,但是我不能接收事件名被直接写 string.
销毁事件
/// 销毁事件
void fire(StreamSubscription? subscription) {
subscription?.cancel();
}
本来 stream 可以做到自己手动销毁,但是我想强调的是规范,框架这东西,规范占了一大部分,如果不遵守规范,再牛的框架也会被后来接手者腐蚀,何况我这种功能随手写的框架.