将流合并在一起以创建分页的 ListView

Merging streams together to create paginated ListView

我正在构建一个 ListView,其中包含存储在 Firestore 中的 Hour 项目列表。我能够使用 StreamBuilder 创建它,但是考虑到 Hours 集合包含数千个文档的事实,有必要引入分页以减少不必要的读取。

我的想法是创建一个包含过去 24 小时查询项目的流 (mainStream)。如果用户向下滚动 20 个项目,我将通过将 mainStream 与新流合并来扩展 mainStream 和 20 个附加元素。但是,它似乎不起作用 - 流中只有前 24 个项目,合并似乎没有任何影响。

为什么我想要分页流,而不是列表?小时项目可以在其他设备上更改,我需要流来通知更改。

我坚持了一天多,其他问题也没有帮助 - 非常感谢任何帮助。

class TodayPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Today'),
      ),
      body: _buildContents(context),
    );
  }

  Widget _buildContents(BuildContext context) {
    final database = Provider.of<Database>(context, listen: true);

    DateTime startingHour =
    dateHourFromDate(DateTime.now()).subtract(Duration(hours: 24));
    DateTime beforeHour;

    // Stream with items for last 24 hours
    // hoursStream returns Stream<List<Hour>> from startingHour till beforeHour
    Stream mainStream = database.hoursStream(startingHour: startingHour.toString());

    return StreamBuilder<List<Hour>>(
      stream: mainStream,
      builder: (context, snapshot) {
        if (snapshot.data != null) {

          final List<Hour> hours = snapshot.data;
          final allHours = hours.map((hour) => hour.id).toList();

          return ListView.builder(

              // chacheExtent to make sure that itemBuilder will not build items for which we have not yet fetched data in the new stream
              cacheExtent: 5,
              itemBuilder: (context, index) {

                // if itemBuilder is reaching till the end of items in mainStream, extend mainStream by items for the following 24 hours
                if (index % 20 == 0) {
                  beforeHour = startingHour;
                  startingHour = startingHour.subtract(Duration(hours: 20));

                  // doesn't seem to work - snapshot contains elements only from the original mainStream
                  mainStream.mergeWith([
                    database.hoursStream(
                        beforeHour: beforeHour.toString(),
                        startingHour: startingHour.toString())
                  ]);
                }

                return Container(); // placeholder for a widget with content based on allHours

              });
        } else {
          return Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}

hoursStream 的实现:

  Stream<List<Hour>> hoursStream({
    String startingHour,
    String beforeHour,
  }) =>
      _service.collectionStream(
          path: APIPath.hours(uid),
          builder: (data, documentId) => Hour.fromMap(data, documentId),
          queryBuilder: (query) => query
              .where('id', isGreaterThanOrEqualTo: startingHour)
              .where('id', isLessThan: beforeHour));

最后是 collectionStream

  Stream<List<T>> collectionStream<T>({
    @required String path,
    @required T Function(Map<String, dynamic> data, String documentId) builder,
    Query Function(Query query) queryBuilder,
    int Function(T lhs, T rhs) sort,
  }) {
    Query query = FirebaseFirestore.instance.collection(path);
    if (queryBuilder != null) {
      query = queryBuilder(query);
    }
    final snapshots = query.snapshots();
    return snapshots.map((snapshot) {
      final result = snapshot.docs
          .map((snapshot) => builder(snapshot.data(), snapshot.id))
          .where((value) => value != null)
          .toList();
      if (sort != null) {
        result.sort(sort);
      }
      return result;
    });
  }

据我所知,Stream 没有 mergeWith 方法,所以我不知道它是您创建的扩展还是来自库,但您忘记的一件事是setState.

if (index % 20 == 0) {
  beforeHour = startingHour;
  startingHour = startingHour.subtract(Duration(hours: 20));

  setState(() {
    mainStream.mergeWith([
      database.hoursStream(
          beforeHour: beforeHour.toString(),
          startingHour: startingHour.toString())
    ]);
  });
}

顺便说一下,我喜欢您使用 collectionStream 方法所做的事情。非常漂亮的编码。

如果你想合并你需要使用的流 rxdart package

导入包:

import 'package:rxdart/rxdart.dart';

然后使用以下代码合并流

CombineLatestStream.list([stream1, stream2,.....]);

想要监听变化使用下面的代码

CombineLatestStream.list([stream1, stream2,.....]).listen((data) {
  data.elementAt(0); // stream1
  data.elementAt(1); // stream2
});

如果 listen 方法没有按照您的预期工作,还有其他方法可用

查看更多信息 link:https://pub.dev/packages/rxdart