【问题标题】:How to efficiently access a firestore reference field's data in flutter?如何在flutter中有效地访问firestore引用字段的数据?
【发布时间】:2019-03-24 00:49:01
【问题描述】:

使用与flutter's firestore example类似的代码,假设快照文档中存储了一个引用字段,称为:document['userRef']

首先,如何访问userRef的数据?使用document['userRef'].get().datadocument['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 dart flutter google-cloud-firestore


    【解决方案1】:

    坚持 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'),
                      );
                    },
                  );
                },
              );
            });
      }
    }
    

    【讨论】:

    • 让代码运行起来很头疼,但确实做到了——谢谢。用首字母大写命名变量是使代码不可读的最佳方法。对于那些错过它的人(我做过),有两个不同的变量 UserSnapshot 和 UsersSnapshot,所以要小心。最后你必须使用 document['userRef'].documentID 而不仅仅是参考。
    猜你喜欢
    • 2021-11-22
    • 2019-04-03
    • 1970-01-01
    • 2021-06-13
    • 2019-08-06
    • 1970-01-01
    • 2020-08-09
    • 2021-05-06
    相关资源
    最近更新 更多