flutter插件event_bus(事件总线)封装

2023-12-20  本文已影响0人  回眸一笑百味生

App中,我们经常会需要实现广播机制,用以跨页面事件通知。事件总线通常实现了订阅模式,订阅者模式包含了发布者和订阅者两种角色,可以通过事件总线来触发事件和监听事件, 下面我们实现一个简单的全局事件总线,使用单例模式。
核心原理就:单例 + Map<事件Key,订阅者列表> + 列表遍历,一个简单的实现代码示例如下:

//订阅者回调签名
typedef void EventCallback(arg);

class EventBus {
  //私有构造函数
  EventBus._internal();

  //保存单例
  static EventBus _singleton = EventBus._internal();

  //工厂构造函数
  factory EventBus()=> _singleton;

  //保存事件订阅者队列,key:事件名(id),value: 对应事件的订阅者队列
  final _emap = Map<Object, List<EventCallback>?>();

  //添加订阅者
  void on(eventName, EventCallback f) {
    _emap[eventName] ??=  <EventCallback>[];
    _emap[eventName]!.add(f);
  }

  //移除订阅者
  void off(eventName, [EventCallback? f]) {
    var list = _emap[eventName];
    if (eventName == null || list == null) return;
    if (f == null) {
      _emap[eventName] = null;
    } else {
      list.remove(f);
    }
  }

  //触发事件,事件触发后该事件所有订阅者会被调用
  void 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(全局)变量,页面引入该文件后可以直接使用bus
var bus = EventBus();
//页面A中,监听登录事件
bus.on("login", (arg) {
  // do something
});

//登录页B中,登录成功后触发登录事件,页面A中订阅者会被调用
bus.emit("login", userInfo);

也可以用 dart:async 里的 Stream(流) 来实现:

import 'dart:async';

class EventBus {
  // 使用多订阅流的形式,这种流可以有多个监听器监听(
  final _streamController = StreamController.broadcast();

  // 定义一个单例
  static final EventBus _instance = EventBus._internal();

  factory EventBus() {
    return _instance;
  }

  EventBus._internal();

  // 发布事件
  void fire(event) {
    _streamController.add(event);
  }

  // 订阅事件
  StreamSubscription on<T>(void Function(T) onData) {
    return _streamController.stream.where((event) => event is T).listen(onData);
  }
}

// 调用处:
var eventBus = EventBus();
// 发布一个事件
eventBus.fire(UserLoggedInEvent('Alice'));
// 在其他地方订阅这个事件:
StreamSubscription subscription = eventBus.on<UserLoggedInEvent>((event) {
  print('User logged in: ${event.username}');
});
//在合适的地方取消订阅
subscription.cancel();

总体来说,封装方式1是一种基于回调函数的自定义事件机制,而封装方式2是基于 Dart 内置的 Stream 和 StreamController 的事件机制。方式1相对简单,适用于基本的事件通知需求,而方式2更灵活、功能更强大,适用于复杂的场景,尤其是需要异步事件处理的情况。

选择建议:
如果项目简单,不需要支持异步事件,且对事件系统的需求较为基础,封装方式1是一个简单有效的选择。

如果项目需要支持异步事件、有复杂的事件过滤和管理需求,或者需要广播模式支持多个监听器,封装方式2是更为合适的选择。

上一篇 下一篇

猜你喜欢

热点阅读