Flutter 通知中心
Flutter关于Notification,没什么可说的,可以实现将数据从子组件向父组件传递。
但是如果要是实现多页面、跨页面、广播进行一对多的通知,类似iOS中的NSNotificationCenter
,在Fluter中暂时没有还发现。
类似这样的功能:
3个页面,其中page3
发出通知,page1
和page2
接收通知,做出相应操作。
有很多方法都可以实现这样的功能,这里探讨下用通知怎么实现。
因为Flutter中并没有提供类似iOS中的NSNotificationCenter
功能,那我们可不可以自己撸一个呢?
思路:
首先想到的是将通知中心设计成单例类(Flutter单例),以方便使用;其次需要提供加入通知、发出通知、移除通知等方法。
- 加入通知 :记录每一次加入的通知,并以
name
标记不同的通知; - 发出通知:根据
name
在通知中心中拿到对应的通知,并执行通知方法; - 移除通知:根据
name
在通知中心中拿到对应的通知,并移除对应通知;
以上只是初步的设想,肯定有不完善的地方,我们后面会一一完善。
思路有了,下面就开始撸代码实现了:
class RCNotificationCenter {
/// 单例
factory RCNotificationCenter() => _getInstance();
static RCNotificationCenter get instance => _getInstance();
static RCNotificationCenter _instance;
RCNotificationCenter._internal();
static RCNotificationCenter _getInstance() {
if (_instance == null) {
_instance = RCNotificationCenter._internal();
}
return _instance;
}
/// 创建通知中心,postName与通知--对应
Map<String, Function(dynamic value)> _poolMap = Map<String, Function(dynamic value)>();
/// 添加监听者方法(加入通知)
void addObserver(String postName,notification(dynamic value)) {
_poolMap[postName] = notification;
}
/// 发送通知
void postNotification(String postName, dynamic value) {
/// 判断Map是否含有postName
if (_poolMap.containsKey(postName)) {
/// 拿到对应的通知,并执行
_poolMap[postName](value);
}
}
/// 根据postName移除对应通知
void removeNotification(String postName) {
if (_poolMap.containsKey(postName)) {
_poolMap.remove(postName);
}
}
/// 清空通知中心
void removeAll(){
_poolMap.clear();
}
}
使用时:
/// 加入通知
RCNotificationCenter().addObserver("postName",(value){
print("接收到通知:$object");
});
/// 发送通知
RCNotificationCenter().postNotification("postName", value);
问题:
主要有两个:
-
虽然可以实现通知功能,但是由于使用了
Map
,每次加入后,对与同一个name
直接重新赋值覆盖了,并不能现实通知的一对多;相同的name
的通知,只会执行最后一条。 -
移除通知存在问题,在多个页面加入同一个通知后,会出现本来只想移除某一个页面的通知,结果所有的对应相同
name
的通知都会移除。无法实现对应单个特定页面的通知进行移除。
完善:
为解决以上问题,每个通知需要对应三个元素,分别是:name
、key
(具体是谁加入的,类似于iOS的self,后期可以根据此元素来移除特定页面的通知,而不会影响其他页面相同的通知,做到谁加入,谁移除)、notification
通知执行方法。
为此,笔者创建一个通知的模型用来对应每一个通知。
完善后的代码:
class RCNotificationCenter {
// 单例
factory RCNotificationCenter() => _getInstance();
static RCNotificationCenter get instance => _getInstance();
static RCNotificationCenter _instance;
RCNotificationCenter._internal();
static RCNotificationCenter _getInstance() {
if (_instance == null) {
_instance = RCNotificationCenter._internal();
}
return _instance;
}
//创建通知中心
List<RCNotificationModel> pool = [];
//添加监听者方法(加入通知中心)
void addObserver(String postName, dynamic key,void notification(dynamic value)) {
RCNotificationModel model = RCNotificationModel.fromList([postName,key,notification]);
pool.add(model);
}
//发送通知
void postNotification(String postName, dynamic value) {
/// 遍历拿到对应的通知,并执行
pool.forEach((element) {
if(element.postName == postName){
element.notification(value);
}
});
}
/// 根据postName移除通知
void removeOfName(String postName) {
/// 线程安全,不可使用forEach执行添加、移除等操作
pool.removeWhere((element) => element.postName == postName);
}
/// 根据key移除通知
void removeOfKey(dynamic key){
pool.removeWhere((element) => element.key == key);
}
/// 清空通知中心
void removeAll(){
pool.clear();
}
}
/// 通知模型
class RCNotificationModel{
String postName;
dynamic key; /// 根据key标记是谁加入的通知,一般直接传widget就好
Function(dynamic value) notification;
/// 简单写一个构造方法
RCNotificationModel.fromList(List list){
this.postName = list.first;
this.key = list[1];
this.notification = list.last;
}
}
使用时:
/// 加入通知中心
RCNotificationCenter().addObserver("postName",widget,(value){
print("接收到通知:$value");
});
/// 发出通知
RCNotificationCenter().postNotification("postName", value);
/// 移除名字为postName的所有通知
RCNotificationCenter().removeOfName("postName");
/// 移除标记为key的所有通知,一般在dispose()调用
RCNotificationCenter().removeOfKey(widget);
以上就是实现了类似iOS中的NSNotificationCenter通知中心,基本上可以实现多页面、跨页面、广播进行一对多的通知。
可优化部分:监听加入通知的key,key资源释放了,自动调用移除通知方法。iOS可以绑定对象以此监听对象什么时候被释放。目前Flutter还在学习中!
当然,有什么更好的方案,欢迎留言!