将流合并在一起以创建分页的 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
我正在构建一个 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