我偶然发现了完全相同的问题,即使我使用的是 Bloc 而不是 Riverpod。
我为此写了整篇文章,以支持列表的实时更新并允许无限滚动:ARTICLE ON MEDIUM
我的方法是按名称和 ID(例如)对查询进行排序,并使用 startAfter 而不是 startAfterDocument。
例如:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:infite_firestore_list/domain/list_item_entity.dart';
import 'package:infite_firestore_list/domain/item_repository.dart';
class FirebaseItemRepository implements ItemRepository {
final _itemsCollection = FirebaseFirestore.instance.collection('items');
@override
Future<Stream<List<ListItem>>> getItems({
String startAfterName = '',
String startAfterId = '',
int paginationSize = 10,
}) async {
return _itemsCollection
.orderBy("name")
.orderBy(FieldPath.documentId)
.startAfter([startAfterName, startAfterId])
.limit(paginationSize)
.snapshots()
.map((querySnapshot) => querySnapshot.docs.map((doc) {
return ListItemDataModel.fromFirestoreDocument(doc).toDomain();
}).toList());
}
}
在您的逻辑中,您只需使用 id 和 name 或您希望使用的任何字段,例如日期。
如果您使用多个orderBy 的组合,则在您第一次运行查询时,Firebase 可能会要求您使用将出现在日志中的链接来构建索引。
这种方法的缺点是,它只有在您确定您在 orderBy 中使用的字段是唯一的情况下才有效。实际上,例如,如果您按日期排序,如果两个字段具有相同的日期并且您使用 startAfter 该日期(第一项),您可以跳过具有相同日期的第二项...
在我的示例中,startAfterId 似乎没有用,但在我的用例中,它解决了我偶然发现的一些边缘情况。
另类
我认为但我个人不喜欢的另一种选择(因此我没有在我的文章中提到它)可能是将每个页面的最后一个文档的快照的数组存储在存储库本身中。
比使用逻辑域中的 id 请求新页面并在存储库本身中建立对应关系 id <--> snapshot。
如果您希望存储库单例中的页面数量有限,因此需要一个受控数组,那么这种方法可能会很有趣,否则它会闻到内存泄漏的味道,这就是为什么我个人不喜欢这种方法尽可能保持通用性。