Flutter Firestore StreamBuilder无限分页:初始项重复显示问题
问题:Firestore分页初始加载数据重复异常
我参考Stack Overflow的方案实现了基于Firestore StreamBuilder的分页功能,修改数据项时表现正常,但添加DocumentChangeType.added的处理逻辑后,出现初始加载时所有项重复显示的异常。以下是调整后的代码:
final StreamController<List<DocumentSnapshot>> _streamController = StreamController<List<DocumentSnapshot>>(); final List<DocumentSnapshot> _notifications = []; bool _isRequesting = false; bool _isFinish = false; final CollectionReference<Map<String, dynamic>> _notificationCollectionRef = FirebaseFirestore.instance.collection(BackendKeys.user.allUsers).doc(AuthService.currentUser!.uid).collection(BackendKeys.notifications.notifications); @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { if (!AuthService.currentUser!.isAnonymous) { unawaited(PushNotificationService.initIfPermissionIsGranted( requestPermission: true, )); } }); _notificationCollectionRef.snapshots().listen((data) { print('change in listner'); onChangeData(data.docChanges); }); requestNextPage(); } @override void dispose() { _streamController.close(); super.dispose(); } @override Widget build(BuildContext context) { return Flexible( child: Padding( padding: const EdgeInsets.symmetric( horizontal: sidePadding, ), child: SafeArea( child: Column( children: [ const SizedBox( height: sidePadding, ), // Wrapping in SizedBox with height of buttons so // the header is lined up with the other views (friends & settings) SizedBox( height: touchTargetForButtons, child: Row( children: [ Text( 'Inbox'.tr, style: AppTextStyles.avenirNextH2Bold.copyWith( color: white, ), ), const SizedBox( width: 20, ), ], ), ), const SizedBox( height: sidePadding, ), Flexible( child: NotificationListener<ScrollNotification>( onNotification: (scrollInfo) { if (scrollInfo.metrics.maxScrollExtent == scrollInfo.metrics.pixels) { requestNextPage(); } return true; }, child: StreamBuilder( stream: _streamController.stream, builder: (context, snapshot) { return AnimatedSwitcher( duration: kThemeAnimationDuration, child: snapshot.connectionState == ConnectionState.waiting ? _buildLoadingView() : snapshot.data == null ? const ErrorView() : _buildNotifications( List.generate( snapshot.data!.length, (index) => PushNotification.fromJson( snapshot.data![index].data() as Map<String, dynamic>, ), ), ), ); }, ), ), ), ], ), ), ), ); } void onChangeData(List<DocumentChange> documentChanges) { for (var notificationChange in documentChanges) { switch (notificationChange.type) { case DocumentChangeType.added: _notifications.add(notificationChange.doc); break; case DocumentChangeType.modified: int indexWhere = _notifications.indexWhere((notification) { return notificationChange.doc.id == notification.id; }); if (indexWhere >= 0) { _notifications[indexWhere] = notificationChange.doc; } break; case DocumentChangeType.removed: _notifications.removeWhere((notification) { return notificationChange.doc.id == notification.id; }); break; } } _streamController.add(_notifications); } void requestNextPage() async { if (!_isRequesting && !_isFinish) { QuerySnapshot querySnapshot; _isRequesting = true; if (_notifications.isEmpty) { querySnapshot = await _notificationCollectionRef.orderBy(BackendKeys.notifications.sentAtDateTimeSinceMillisecondsSinceEpoch).limit(10).get(); } else { querySnapshot = await _notificationCollectionRef.orderBy(BackendKeys.notifications.sentAtDateTimeSinceMillisecondsSinceEpoch).startAfterDocument(_notifications[_notifications.length - 1]).limit(10).get(); } int oldSize = _notifications.length; print('oldSize: ' + oldSize.toString()); _notifications.addAll(querySnapshot.docs); int newSize = _notifications.length; if (oldSize != newSize) { _streamController.add(_notifications); } else { _isFinish = true; } _isRequesting = false; print(newSize); } } _buildNotifications(List<PushNotification> notifications) { if (notifications.isEmpty) { return _buildEmptyNotificationsView(); } notifications.sort( (a, b) => b.sentAtDateTimeSinceMillisecondsSinceEpoch.compareTo(a.sentAtDateTimeSinceMillisecondsSinceEpoch), ); return ListView.builder( key: const Key('non_empty_notifications'), itemCount: notifications.length + 1, itemBuilder: (context, index) { if (index == notifications.length) { return SizedBox( height: safeAreaBottom + bottomNavBarHeight + sidePadding, ); } return Padding( padding: const EdgeInsets.only( bottom: 20, ), child: NotificationTile( key: UniqueKey(), notification: notifications[index], ), ); }, ); }
我尝试了多种方法仍无法定位问题原因,恳请各位提供帮助,如需更多信息请告知!
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

