Flutter Firebase 创建一个动态的流列表
Flutter Firebase create a dynamic list of streams
如何创建流的动态列表?
我需要一个构建流列表和 return 流的函数。
我的功能(尝试 return 这样的值 List<Stream<'dynamic'>>
:
List<Stream<dynamic>> _buildStreamList() async* {
var chat_overview_object = await query_chat_overview_id();
List<Stream<dynamic>> stream_list = [];
for (var i = 0; i < chat_overview_object.length; i++) {
stream_list.add(
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1234')
.snapshots(),
);
}
yield stream_list;
}
为什么我需要这个?
我尝试在文档上构建具有多个流的流生成器。
流可以不同(例如,一个流查看集合 a 文档,另一个流查看集合 b 文档 b)。
该示例有效,但仅适用于修复流。现在我想实现一个 ** 函数,其中 return 一个动态的流列表。
这是我的代码:
var _data = [];
typedef MultiStreamWidgetBuilder<T> = Widget Function(BuildContext context);
// A widget that basically re-calls its builder whenever any of the streams
// has an event.
class MultiStreamBuilder extends StatefulWidget {
const MultiStreamBuilder({
required this.streams,
required this.builder,
Key? key,
}) : super(key: key);
final List<Stream<dynamic>> streams;
final MultiStreamWidgetBuilder builder;
Widget build(BuildContext context) => builder(context);
@override
State<MultiStreamBuilder> createState() => _MultiStreamBuilderState();
}
class _MultiStreamBuilderState extends State<MultiStreamBuilder> {
final List<StreamSubscription<dynamic>> _subscriptions = [];
@override
void initState() {
super.initState();
_subscribe();
}
@override
void didUpdateWidget(MultiStreamBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.streams != widget.streams) {
// Unsubscribe from all the removed streams and subscribe to all the added ones.
// Just unsubscribe all and then resubscribe. In theory we could only
// unsubscribe from the removed streams and subscribe from the added streams
// but then we'd have to keep the set of streams we're subscribed to too.
// This should happen infrequently enough that I don't think it matters.
_unsubscribe();
_subscribe();
}
}
@override
Widget build(BuildContext context) => widget.build(context);
@override
void dispose() {
_unsubscribe();
super.dispose();
}
void _subscribe() {
for (final s in widget.streams) {
final subscription = s.listen(
(dynamic data) {
setState(() {
_data.add(data);
print('data: ' + _data.toString());
});
},
onError: (Object error, StackTrace stackTrace) {
setState(() {});
},
onDone: () {
setState(() {});
},
);
_subscriptions.add(subscription);
}
}
void _unsubscribe() {
for (final s in _subscriptions) {
s.cancel();
}
_subscriptions.clear();
}
}
class AppWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiStreamBuilder(
streams: _buildStreamList(),
[
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1233')
.snapshots(),
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1234')
.snapshots(),
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1232')
.snapshots()
],
builder: _buildMain,
);
}
}
Widget _buildMain(BuildContext context) {
return MaterialApp(
home: Row(
children: [
Text(
'_data: ' + _data.toString(),
style: TextStyle(fontSize: 15),
)
],
));
}
编辑!
过了一会儿我得到了解决方案:
//multi stream widget builder
typedef MultiStreamWidgetBuilder<T> = Widget Function(BuildContext context);
// widget that basically re-calls its builder whenever any of the streams has an event.
class MultiStreamBuilder extends StatefulWidget {
const MultiStreamBuilder({
required this.streams,
required this.builder,
Key? key,
}) : super(key: key);
final List<Stream<dynamic>> streams;
final MultiStreamWidgetBuilder builder;
Widget build(BuildContext context) => builder(context);
@override
State<MultiStreamBuilder> createState() => _MultiStreamBuilderState();
}
//multi streambuilder
class _MultiStreamBuilderState extends State<MultiStreamBuilder> {
final List<StreamSubscription<dynamic>> _subscriptions = [];
@override
void initState() {
super.initState();
_subscribe();
}
@override
void didUpdateWidget(MultiStreamBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.streams != widget.streams) {
// Unsubscribe from all the removed streams and subscribe to all the added ones.
// Just unsubscribe all and then resubscribe. In theory we could only
// unsubscribe from the removed streams and subscribe from the added streams
// but then we'd have to keep the set of streams we're subscribed to too.
// This should happen infrequently enough that I don't think it matters.
_unsubscribe();
_subscribe();
}
}
@override
Widget build(BuildContext context) => widget.build(context);
@override
void dispose() {
_unsubscribe();
super.dispose();
}
void _subscribe() {
for (final s in widget.streams) {
final subscription = s.listen(
(dynamic data) {
setState(() {
//check object contain id
var object = chat_overview_object.singleWhere(
(element) => element.chat_overview_id[0].trim() == data.id);
//add to object data
object.last_message_send = data['last_message_send'];
object.last_update = data['last_update'];
object.members_id = data['members_id'];
object.new_message = data['new_message'];
object.user_display_name = data['user_display_name'];
});
//set index
index++;
//check all data load
if (index == chat_overview_object.length) {
//set data has load
data_load.value = false;
}
},
onError: (Object error, StackTrace stackTrace) {
setState(() {});
},
onDone: () {
setState(() {});
},
);
_subscriptions.add(subscription);
}
}
void _unsubscribe() {
for (final s in _subscriptions) {
s.cancel();
}
_subscriptions.clear();
}
}
您使用了错误的方法:您不应为每个 document
构建一个单独的 stream
,然后再决定您使用的是哪个 stream
。相反,您应该只构建 1 stream
,returns 您可能需要的所有 documents
。
然后,在您的 StreamBuilder
中,您可以应用您的 if
条件来决定您需要使用哪个 document
。这样,文档将始终是最新的。
确保应用正确的 where
子句从数据库中调用适当的 documents
。
编辑 1:
所以你需要的是一个集合组:
db.collectionGroup('data').where('yourField', isEqualTo: 'whatever').snapshots()
如何创建流的动态列表?
我需要一个构建流列表和 return 流的函数。
我的功能(尝试 return 这样的值 List<Stream<'dynamic'>>
:
List<Stream<dynamic>> _buildStreamList() async* {
var chat_overview_object = await query_chat_overview_id();
List<Stream<dynamic>> stream_list = [];
for (var i = 0; i < chat_overview_object.length; i++) {
stream_list.add(
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1234')
.snapshots(),
);
}
yield stream_list;
}
为什么我需要这个?
我尝试在文档上构建具有多个流的流生成器。
流可以不同(例如,一个流查看集合 a 文档,另一个流查看集合 b 文档 b)。
该示例有效,但仅适用于修复流。现在我想实现一个 ** 函数,其中 return 一个动态的流列表。
这是我的代码:
var _data = [];
typedef MultiStreamWidgetBuilder<T> = Widget Function(BuildContext context);
// A widget that basically re-calls its builder whenever any of the streams
// has an event.
class MultiStreamBuilder extends StatefulWidget {
const MultiStreamBuilder({
required this.streams,
required this.builder,
Key? key,
}) : super(key: key);
final List<Stream<dynamic>> streams;
final MultiStreamWidgetBuilder builder;
Widget build(BuildContext context) => builder(context);
@override
State<MultiStreamBuilder> createState() => _MultiStreamBuilderState();
}
class _MultiStreamBuilderState extends State<MultiStreamBuilder> {
final List<StreamSubscription<dynamic>> _subscriptions = [];
@override
void initState() {
super.initState();
_subscribe();
}
@override
void didUpdateWidget(MultiStreamBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.streams != widget.streams) {
// Unsubscribe from all the removed streams and subscribe to all the added ones.
// Just unsubscribe all and then resubscribe. In theory we could only
// unsubscribe from the removed streams and subscribe from the added streams
// but then we'd have to keep the set of streams we're subscribed to too.
// This should happen infrequently enough that I don't think it matters.
_unsubscribe();
_subscribe();
}
}
@override
Widget build(BuildContext context) => widget.build(context);
@override
void dispose() {
_unsubscribe();
super.dispose();
}
void _subscribe() {
for (final s in widget.streams) {
final subscription = s.listen(
(dynamic data) {
setState(() {
_data.add(data);
print('data: ' + _data.toString());
});
},
onError: (Object error, StackTrace stackTrace) {
setState(() {});
},
onDone: () {
setState(() {});
},
);
_subscriptions.add(subscription);
}
}
void _unsubscribe() {
for (final s in _subscriptions) {
s.cancel();
}
_subscriptions.clear();
}
}
class AppWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiStreamBuilder(
streams: _buildStreamList(),
[
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1233')
.snapshots(),
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1234')
.snapshots(),
FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc('chat_overview_id_1232')
.snapshots()
],
builder: _buildMain,
);
}
}
Widget _buildMain(BuildContext context) {
return MaterialApp(
home: Row(
children: [
Text(
'_data: ' + _data.toString(),
style: TextStyle(fontSize: 15),
)
],
));
}
编辑!
过了一会儿我得到了解决方案:
//multi stream widget builder
typedef MultiStreamWidgetBuilder<T> = Widget Function(BuildContext context);
// widget that basically re-calls its builder whenever any of the streams has an event.
class MultiStreamBuilder extends StatefulWidget {
const MultiStreamBuilder({
required this.streams,
required this.builder,
Key? key,
}) : super(key: key);
final List<Stream<dynamic>> streams;
final MultiStreamWidgetBuilder builder;
Widget build(BuildContext context) => builder(context);
@override
State<MultiStreamBuilder> createState() => _MultiStreamBuilderState();
}
//multi streambuilder
class _MultiStreamBuilderState extends State<MultiStreamBuilder> {
final List<StreamSubscription<dynamic>> _subscriptions = [];
@override
void initState() {
super.initState();
_subscribe();
}
@override
void didUpdateWidget(MultiStreamBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.streams != widget.streams) {
// Unsubscribe from all the removed streams and subscribe to all the added ones.
// Just unsubscribe all and then resubscribe. In theory we could only
// unsubscribe from the removed streams and subscribe from the added streams
// but then we'd have to keep the set of streams we're subscribed to too.
// This should happen infrequently enough that I don't think it matters.
_unsubscribe();
_subscribe();
}
}
@override
Widget build(BuildContext context) => widget.build(context);
@override
void dispose() {
_unsubscribe();
super.dispose();
}
void _subscribe() {
for (final s in widget.streams) {
final subscription = s.listen(
(dynamic data) {
setState(() {
//check object contain id
var object = chat_overview_object.singleWhere(
(element) => element.chat_overview_id[0].trim() == data.id);
//add to object data
object.last_message_send = data['last_message_send'];
object.last_update = data['last_update'];
object.members_id = data['members_id'];
object.new_message = data['new_message'];
object.user_display_name = data['user_display_name'];
});
//set index
index++;
//check all data load
if (index == chat_overview_object.length) {
//set data has load
data_load.value = false;
}
},
onError: (Object error, StackTrace stackTrace) {
setState(() {});
},
onDone: () {
setState(() {});
},
);
_subscriptions.add(subscription);
}
}
void _unsubscribe() {
for (final s in _subscriptions) {
s.cancel();
}
_subscriptions.clear();
}
}
您使用了错误的方法:您不应为每个 document
构建一个单独的 stream
,然后再决定您使用的是哪个 stream
。相反,您应该只构建 1 stream
,returns 您可能需要的所有 documents
。
然后,在您的 StreamBuilder
中,您可以应用您的 if
条件来决定您需要使用哪个 document
。这样,文档将始终是最新的。
确保应用正确的 where
子句从数据库中调用适当的 documents
。
编辑 1:
所以你需要的是一个集合组:
db.collectionGroup('data').where('yourField', isEqualTo: 'whatever').snapshots()