Flutter 通知中心

作者: HuyaRC | 来源:发表于2021-04-01 15:39 被阅读0次

    Flutter关于Notification,没什么可说的,可以实现将数据从子组件向父组件传递。

    但是如果要是实现多页面、跨页面、广播进行一对多的通知,类似iOS中的NSNotificationCenter,在Fluter中暂时没有还发现。
    类似这样的功能:

    3个页面,其中page3发出通知,page1page2接收通知,做出相应操作。
    有很多方法都可以实现这样的功能,这里探讨下用通知怎么实现。

    因为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的通知都会移除。无法实现对应单个特定页面的通知进行移除。

    完善:

    为解决以上问题,每个通知需要对应三个元素,分别是:namekey(具体是谁加入的,类似于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还在学习中!

    当然,有什么更好的方案,欢迎留言!

    相关文章

      网友评论

        本文标题:Flutter 通知中心

        本文链接:https://www.haomeiwen.com/subject/pretkltx.html