Flutter调用asBroadcastStream时Firestore变更触发流数据重复问题
问题根因
核心问题出在firestore_service.dart的流实现逻辑,其次存在Provider流重复创建的潜在问题:
- 你将
List<MediaItem> mediaList = []定义在了await for循环的外层,整个流的生命周期里这个列表只会初始化一次。每次Firestore快照更新触发迭代时,新的文档数据会直接追加到已有列表中,不会清空旧数据,导致每次流推送的列表长度都比上一次多一倍,最终出现重复条目。 - 其次你的
mediaProvider.getMedia()方法每次被调用都会生成全新的Firestore监听流,即使转成了广播流,多个页面多次调用时依然会创建多个独立的Firestore监听器,额外消耗资源。
修复方案
1. 修复流数据累加问题
将mediaList的初始化逻辑移到await for循环内部,每次快照更新时都创建全新的空列表填充数据:
Stream<List<MediaItem>> getMedia({ required String albumId, required MediaFilter mediaFilter, required String? uid, }) async* { // no filter Stream mediaStream = _mediaCollectionReference .where('albumId', isEqualTo: albumId) .orderBy('createdAt', descending: true) .snapshots(); await for (QuerySnapshot mediaSnapshot in mediaStream) { // 每次快照更新都新建空列表,避免累加旧数据 List<MediaItem> mediaList = []; for (var mediaDoc in mediaSnapshot.docs) { MediaItem mediaItem = MediaItem.fromData( mediaDoc.id, mediaDoc.data() as Map<String, dynamic>); mediaList.add(mediaItem); } yield mediaList; } }
2. 优化Provider流缓存逻辑
在Provider中缓存已创建的流,避免重复生成新的Firestore监听:
// 在Provider中新增成员变量缓存流 Stream<List<MediaItem>>? _mediaStream; Stream<List<MediaItem>> getMedia() { // 已创建过流直接返回,避免重复监听 if (_mediaStream != null) return _mediaStream!; _mediaStream = _firestoreService .getMedia( albumId: _albumId, mediaFilter: _mediaFilter, uid: _authService.currentUser!.getId, ) .asBroadcastStream(); setCurrentState(_mediaStream!); return _mediaStream!; } // 如果有切换筛选条件、专辑ID的需求,新增重置流的方法 void resetMediaStream() { _mediaStream = null; }
可选加固方案
你也可以在遍历文档时根据MediaItem的ID做去重处理,进一步避免意外的重复数据:
await for (QuerySnapshot mediaSnapshot in mediaStream) { Map<String, MediaItem> mediaMap = {}; for (var mediaDoc in mediaSnapshot.docs) { MediaItem mediaItem = MediaItem.fromData( mediaDoc.id, mediaDoc.data() as Map<String, dynamic>); mediaMap[mediaDoc.id] = mediaItem; } yield mediaMap.values.toList(); }
内容的提问来源于stack exchange,提问作者Rens
相关产品推荐
相关产品推荐

