如何有效地扑捉Firestore参考字段的数据?
问题内容:
使用与flutter的firestore示例类似的代码,假设在快照文档中存储了一个名为的参考字段document['userRef']
。
首先,如何访问userRef的数据?使用document['userRef'].get().data
或document['userRef'].get().username
我无法访问数据。(NoSuchMethodError: Class 'Future<DocumentSnapshot>' has no instance getter 'data'
)
我也尝试使用document['userRef'].get().then(...)
但收到错误:type 'Future<dynamic>' is not a subtype of type 'String'
即使.then
可以使用,难道不是每个消息都会再次查找相同的参考吗?在这里,数据库是实时更新的,但是不必在ListView中对多个消息进行相同的查找。
class MessageList extends StatelessWidget {
MessageList({this.firestore});
final Firestore firestore;
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection('messages').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
final int messageCount = snapshot.data.documents.length;
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document = snapshot.data.documents[index];
// document['userRef'] exists here
return ListTile(
title: Text(document['message'] ?? '<No message retrieved>'),
subtitle: Text('Message ${index + 1} of $messageCount'),
);
},
);
},
);
}
}
编辑:我可以使用FutureBuilder来获取嵌套数据,尽管不确定它的效率如何。(这是否可能会将大量冗余请求发送到Firebase?)
为嵌套数据创建一个小部件,其中存在document [‘userRef’]:
FutureBuilder(
future: userData(document['userRef']),
builder: (BuildContext context,
AsyncSnapshot<dynamic> uData) {
return Text(uData.data['username']);
},
);
而userData函数如下所示:
Future<dynamic> userData(DocumentReference user) async {
DocumentSnapshot userRef = await user.get();
return userRef.data;
}
问题答案:
坚持使用Firebase和Flutter的方式,可以在Streambuilder内部使用Streambuilder。也就是说,不要将FutureBuilder用于嵌套数据,而是让您等待每个.get请求。
(该代码未经测试,但原理已通过测试。)
class MessageList extends StatelessWidget {
MessageList({this.firestore});
final Firestore firestore;
@override
Widget build(BuildContext context) {
Map UserSnapshot = Map(); // create a variable for accessing users by id
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection('users').snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> UsersSnapshot) {
// process usersnapshot from list to map
UsersSnapshot.data.documents.forEach((userRecord) {
//print(optionRecord.documentID); // debug
UserSnapshot[userRecord.documentID] = userRecord;
});
// user data can be accessed as soon as there is a reference field or documentID:
// UserSnapshot[document['userRef']]['userName'}
return StreamBuilder<QuerySnapshot>(
stream: firestore.collection('messages').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> MessagesSnapshot) {
if (!MessagesSnapshot.hasData) return const Text('Loading...');
final int messageCount = MessagesSnapshot.data.documents.length;
return ListView.builder(
itemCount: messageCount,
itemBuilder: (_, int index) {
final DocumentSnapshot document =
MessagesSnapshot.data.documents[index];
// document['userRef'] exists here
// UserSnapshot[document['userRef']]['userName'} is accessible here
return ListTile(
title:
Text(document['message'] ?? '<No message retrieved>'),
subtitle: Text('Message ${index + 1} of $messageCount'),
);
},
);
},
);
});
}
}