Flutter - 接收并修改来自 Stream 的数据
Flutter - Receive and then modify data from Stream
我正在尝试执行以下操作:
- 监听 Firestore 流,以便在添加新文档时,StreamBuilder 将接收它、修改它,然后呈现它。
“修改”采用 Stream 数据,其中包括 Firestore UID,从具有该 UID 的 Firestore 获取数据,然后用该数据填充 StreamBuilder。
因此流程是:添加新文档 -> Stream 获取文档 -> 函数从该文档获取 UID -> 函数使用该 UID 从 Firestore 获取更多数据 -> 函数 returns 填充 StreamBuilder那个新数据。
我当前的设置如下 -- 有效,但 FutureBuilder 显然在每次重建小部件时调用 Firestore,没有人想要那样。
Stream<QuerySnapshot> upperStream;
void initState() {
super.initState();
upperStream = aStream();
}
Stream<QuerySnapshot> aStream() {
return Firestore.instance
.collection('FirstLevel')
.document(ownUID (not related to stream))
.collection('SecondLevel')
.snapshots();
}
Future<List> processStream(List streamData) async {
List futureData = List();
for (var doc in streamData) {
Map<String, dynamic> dataToReturn = Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection('FirstLevel')
.document(OTHER USER'S UID FROM STREAM)
.get();
dataToReturn['i'] = userDoc['i'];
futureData.add(dataToReturn);
}
return futureData;
}
...
...
//The actual widget
Expanded(
child: StreamBuilder(
stream: upperStream,
builder: (context, snapshot) {
// Error/null handling
return FutureBuilder(
future: processStream(snapshot.data.documents),
builder: (context, futureSnap) {
// Error/null handling
return ListView.builder(
shrinkWrap: true,
itemCount: futureSnap.data.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
//Continuing with populating
});
});
}),
),
处理这种流程的最佳方式是什么?创建一种方法,修改 Firestore 流中的数据然后返回,根本不需要 ListView.builder?
编辑:我试过像这样创建自己的流:
Stream<Map<String, dynamic>> aStream2() async* {
QuerySnapshot snap = await Firestore.instance
.collection(FirstLevel)
.document(OWN UID)
.collection(SecondLevel)
.getDocuments();
for (var doc in snap.documents) {
Map<String, dynamic> data = Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection(FirstLevel)
.document(OTHER USERS UID RECEIVED FROM STREAM)
.get();
data['i'] = userDoc['i'];
yield data;
}
}
但是,当向 SecondLevel 集合添加新文档时,流不是 triggered/updated。
好的,我想我找到了解决方法。我从流中获取数据,对其进行修改,然后在一种方法中将其交给 StreamBuilder,不再需要 FutureBuilder。正如 Christopher Moore 在评论中提到的那样,关键在于 await for
。流方法如下所示:
Stream<List> aStream() async* {
List dataToReturn = List();
Stream<QuerySnapshot> stream = Firestore.instance
.collection(LevelOne)
.document(OWN UID)
.collection(LevelTwo)
.snapshots();
await for (QuerySnapshot q in stream){
for (var doc in q.documents) {
Map<String, dynamic> dataMap= Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection('UserData')
.document(doc['other user data var'])
.get();
dataMap['i'] = userDoc['i'];
//...//
dataToReturn.add(dataMap);
}
yield dataToReturn;
}
}
然后 StreamBuilder 会根据需要填充修改后的数据。
我发现自己使用它在我的应用程序中使用 Dash Chat 包实现了一个聊天系统。我认为在流上使用 map 函数可能更清晰一些,这里是一个示例:
Stream<List<ChatMessage>> getMessagesForConnection(
String connectionId) {
return _db
.collection('connections')
.doc(connectionId)
.collection('messages')
.snapshots()
.map<List<ChatMessage>>((event) {
List<ChatMessage> messages = [];
for (var doc in event.docs) {
try {
messages.add(ChatMessage.fromJson(doc.data()));
} catch (e, stacktrace) {
// do something with the error
}
}
return messages;
});}
我正在尝试执行以下操作:
- 监听 Firestore 流,以便在添加新文档时,StreamBuilder 将接收它、修改它,然后呈现它。
“修改”采用 Stream 数据,其中包括 Firestore UID,从具有该 UID 的 Firestore 获取数据,然后用该数据填充 StreamBuilder。
因此流程是:添加新文档 -> Stream 获取文档 -> 函数从该文档获取 UID -> 函数使用该 UID 从 Firestore 获取更多数据 -> 函数 returns 填充 StreamBuilder那个新数据。
我当前的设置如下 -- 有效,但 FutureBuilder 显然在每次重建小部件时调用 Firestore,没有人想要那样。
Stream<QuerySnapshot> upperStream;
void initState() {
super.initState();
upperStream = aStream();
}
Stream<QuerySnapshot> aStream() {
return Firestore.instance
.collection('FirstLevel')
.document(ownUID (not related to stream))
.collection('SecondLevel')
.snapshots();
}
Future<List> processStream(List streamData) async {
List futureData = List();
for (var doc in streamData) {
Map<String, dynamic> dataToReturn = Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection('FirstLevel')
.document(OTHER USER'S UID FROM STREAM)
.get();
dataToReturn['i'] = userDoc['i'];
futureData.add(dataToReturn);
}
return futureData;
}
...
...
//The actual widget
Expanded(
child: StreamBuilder(
stream: upperStream,
builder: (context, snapshot) {
// Error/null handling
return FutureBuilder(
future: processStream(snapshot.data.documents),
builder: (context, futureSnap) {
// Error/null handling
return ListView.builder(
shrinkWrap: true,
itemCount: futureSnap.data.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
//Continuing with populating
});
});
}),
),
处理这种流程的最佳方式是什么?创建一种方法,修改 Firestore 流中的数据然后返回,根本不需要 ListView.builder?
编辑:我试过像这样创建自己的流:
Stream<Map<String, dynamic>> aStream2() async* {
QuerySnapshot snap = await Firestore.instance
.collection(FirstLevel)
.document(OWN UID)
.collection(SecondLevel)
.getDocuments();
for (var doc in snap.documents) {
Map<String, dynamic> data = Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection(FirstLevel)
.document(OTHER USERS UID RECEIVED FROM STREAM)
.get();
data['i'] = userDoc['i'];
yield data;
}
}
但是,当向 SecondLevel 集合添加新文档时,流不是 triggered/updated。
好的,我想我找到了解决方法。我从流中获取数据,对其进行修改,然后在一种方法中将其交给 StreamBuilder,不再需要 FutureBuilder。正如 Christopher Moore 在评论中提到的那样,关键在于 await for
。流方法如下所示:
Stream<List> aStream() async* {
List dataToReturn = List();
Stream<QuerySnapshot> stream = Firestore.instance
.collection(LevelOne)
.document(OWN UID)
.collection(LevelTwo)
.snapshots();
await for (QuerySnapshot q in stream){
for (var doc in q.documents) {
Map<String, dynamic> dataMap= Map<String, dynamic>();
DocumentSnapshot userDoc = await Firestore.instance
.collection('UserData')
.document(doc['other user data var'])
.get();
dataMap['i'] = userDoc['i'];
//...//
dataToReturn.add(dataMap);
}
yield dataToReturn;
}
}
然后 StreamBuilder 会根据需要填充修改后的数据。
我发现自己使用它在我的应用程序中使用 Dash Chat 包实现了一个聊天系统。我认为在流上使用 map 函数可能更清晰一些,这里是一个示例:
Stream<List<ChatMessage>> getMessagesForConnection(
String connectionId) {
return _db
.collection('connections')
.doc(connectionId)
.collection('messages')
.snapshots()
.map<List<ChatMessage>>((event) {
List<ChatMessage> messages = [];
for (var doc in event.docs) {
try {
messages.add(ChatMessage.fromJson(doc.data()));
} catch (e, stacktrace) {
// do something with the error
}
}
return messages;
});}