如何使用单个StreamController实现多页面的数据流共享监听?
问题解答
基础结论
可以仅通过原生StreamController实现跨页面共享Stream的需求,无需引入RxDart。
问题根源
你当前使用的普通广播流(BroadcastStream)默认不缓存历史事件,仅会将事件推送给订阅时已经处于监听状态的订阅者。你在仪表盘页面先完成初始化、拿到首帧数据推送到流之后,再进入其他页面订阅,自然拿不到之前已经发送过的最新数据。
解决方案
新增缓存变量存储最新的流数据,新订阅者加入时先返回缓存的最新值,再同步后续的流更新即可,修改后的单例代码如下:
class ChatFieldService { static final ChatFieldService _singleton = ChatFieldService._internal(); factory ChatFieldService() => _singleton; ChatFieldService._internal(); final FirebaseService db = FirebaseService(); String fromUserId; // 新增缓存变量存储最新数据 List<ChatFieldModel> _latestChatFields = []; final StreamController<List<ChatFieldModel>> _chatFields$ = StreamController<List<ChatFieldModel>>.broadcast(); // 重写流获取逻辑:新订阅者先拿到缓存的最新数据,再接收后续更新 Stream<List<ChatFieldModel>> get getChatFields async* { yield _latestChatFields; yield* _chatFields$.stream; } StreamSubscription streamSubscription; void initialize(String id) { // 防止同用户重复初始化 if (fromUserId == id) return; fromUserId = id; _getAllChatFields(); } // 注意:该方法不要在单个页面销毁时调用,仅在用户登出、APP全局销毁时调用 void dispose() { streamSubscription?.cancel(); _chatFields$?.close(); _latestChatFields = []; fromUserId = null; } void _getAllChatFields() { // 先取消旧的订阅避免重复监听 streamSubscription?.cancel(); streamSubscription = db.getAllChatField(fromUserId).listen((data) { // 先更新缓存再推送事件 _latestChatFields = data; if (!_chatFields$.isClosed) _chatFields$.sink.add(data); }); } }
额外注意事项
- 每个页面自己创建的
StreamSubscription,需要在当前页面的dispose生命周期中取消,避免内存泄漏:
@override void dispose() { oneSubscription?.cancel(); super.dispose(); }
内容的提问来源于stack exchange,提问作者BIS Tech
相关产品推荐
相关产品推荐

