【问题标题】:Flutter pagination with firestore stream使用 Firestore 流进行颤振分页
【发布时间】:2021-09-17 16:45:25
【问题描述】:

如何在flutter(在本例中为flutter web)上使用firestore流正确实现分页? 我目前使用 bloc 的方法很可能是错误的,就像这样

加载下一页时在 bloc 上调用的函数,请注意,每次调用该函数时,我都会将 state 的 lastPage 变量增加 1:

Stream<JobPostingState> _loadNextPage() async* {
yield state.copyWith(isLoading: true);
try {
  service
      .getAllDataByClassPage(state.lastPage+1)
      .listen((List<Future<DataJob>> listDataJob) async {
    List<DataJob?> listData = [];
    await Future.forEach(listDataJob, (dynamic element) async {
      DataJob data= await element;
      listData.add(data);
    });
    bool isHasMoreData = state.listJobPostBlock.length!=listData.length;
    //Update data on state here
  });
} on Exception catch (e, s) {
  yield StateFailure(error: e.toString());
}}

调用函数来获取流数据

Stream<List<Future<DataJob>>> getAllDataByClassPage(
      String className, int page) {
    Stream<QuerySnapshot> stream;
    if (className.isNotEmpty)
      stream = collection
          .orderBy('timestamp', "desc")
          .where('class', "==", className).limit(page*20)
          .onSnapshot;
    else
      stream = collection.onSnapshot;

    return stream.map((QuerySnapshot query) {
      return query.docs.map((e) async {
        return DataJob.fromMap(e.data());
      }).toList();
    });
  }

使用这种方法,它可以按预期工作,当我加载下一页并仍在收听流时加载的数据增加,但我不知道这是否是正确的方法,因为它替换了流,它可能会读取数据两次并结束与不使用分页相比,我对 Firestore 的阅读次数要多得多。非常感谢任何建议,谢谢。

【问题讨论】:

    标签: flutter dart google-cloud-firestore stream


    【解决方案1】:

    您的方法确实不是最好的,而且随着您的扩展,您的成本会更高。我会在你的鞋子里做的是创建一个代表你的流的全局变量,这样你就可以操纵它。我看不到您的所有代码,因此我将尽可能通用,以便您可以将其应用于您的代码。

    首先让我们将流控制器声明为一个可以保存流值的全局变量:

    StreamController<List<DocumentSnapshot>> streamController = 
        StreamController<List<DocumentSnapshot>>();
    

    之后,我们需要将您的 getAllDataByClassPage 函数更改为以下内容:

    async getAllDataByClassPage(String className) {
        Stream stream = streamController.stream;
        //taking out of the code your className logic
        ...
        if(stream.isEmpty){
             QuerySnapshot snap = await collection.orderBy('timestamp', "desc")
                                                  .where('class', "==", className)
                                                  .limit(20) 
                                                  .onSnapshot
             streamController.add(snap.docs);
        }else{
             DocumentSnapshot lastDoc = stream.last;
             QuerySnapshot snap = await collection.orderBy('timestamp', "desc")
                                                  .where('class', "==", className)
                                                  .startAfterDocument(lastDoc)
                                                  .limit(20)
                                                  .onSnapshot;
             streamController.add(snap.docs);
        }
      }
    

    之后,您需要做的就是调用streamController.stream;

    注意:我没有测试此代码,但这是您应该尝试做的一般理想。

    【讨论】:

    • 嗨,谢谢你的回答,会试试这个,但这会让旧流也主动更新吗?因为我希望它也能在所有页面上的数据发生更改时进行监听,所以 streamController.add(snap.docs) 这个函数是否仍会连接到 Firestore?非常感谢
    • 我相信你仍然会指向内存中的同一个对象,所以我认为流应该自动更新。此外,streamController.add(snap.docs) 不会在 firestore 中进行读取,填充snap 变量的实际查询会,但话又说回来,您不会像在原始代码中那样读取重复数据,您只会获得新数据,这意味着总读取次数更少,成本更低。
    • 我已经对此进行了测试,这里的问题是快照上的集合正在返回流,所以我无法添加它,除非我将 onsnapshot 更改为 .get() 或者我将流更改为流跨度>
    猜你喜欢
    • 1970-01-01
    • 2021-04-14
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 2021-11-25
    • 2021-03-31
    • 2018-04-23
    • 1970-01-01
    相关资源
    最近更新 更多