Flutter - 从 Cloud Firestore 读取嵌套地图

Posted

技术标签:

【中文标题】Flutter - 从 Cloud Firestore 读取嵌套地图【英文标题】:Flutter - Read Nested Maps from Cloud Firestore 【发布时间】:2021-09-30 22:43:20 【问题描述】:

我在尝试从 Cloud Firestore 读取另一个地图中的地图时遇到问题。我在互联网上找到了几篇关于该主题的帖子,但作为 Flutter 和 Firebase 的初学者,我无法根据我的情况调整这些案例。

在 Firebase Firestore 中,我有以下数据示例,属于“媒体”集合:

我能够读取几乎所有数据,包括数组(使用强制转换),但“成分”确实让我很痛苦。它是Map<String, Quantity>,数量是我的一类:

class Quantity 
  final double amount;
  final String unit;

  const Quantity(
    required this.amount,
    required this.unit,
  );
  String toString() => '$amount $unit';

现在它很简单,但会随着更多使用双精度值的方法而增长。这就是我不想摆脱它并简化数据库条目的原因。

我需要使用 Stream Builder 读取所有数据并将其传递给 ListView.buider,如下所示:

Widget build(BuildContext context) 
    return StreamBuilder(
      stream: FirebaseFirestore.instance
          .collection('media')
          .orderBy('initials')
          .snapshots(),
      builder: (BuildContext ctx,
          AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> media) 
        if (media.connectionState == ConnectionState.waiting) 
          return Center(
            child: CircularProgressIndicator(),
          );
        

        var docs = media.data!.docs;
        var box = Hive.box<Favorite>('favorites');

        return ListView.builder(
          itemCount: media.data!.docs.length,
          itemBuilder: (ctx, i) 
            Medium md = Medium(
              initials: docs[i].get('initials'),
              longName: docs[i].get('longName'),
              ingredients: <String, Quantity>, // docs[i].get('ingredients'),
              steps: docs[i].get('steps').cast<String>(),
              mediumState: PhysicalState.values.elementAt(
                docs[i].get('mediumState'),
              ),
              reference: docs[i].data().containsKey('reference')
                  ? docs[i].get('reference')
                  : '',
              isComplement: docs[i].data().containsKey('isComplement')
                  ? docs[i].get('isComplement')
                  : false,
              ps: docs[i].data().containsKey('ps') ? docs[i].get('ps') : '',
            );

            if (box.get(docs[i].get('initials')) == null) 
              box.put(
                docs[i].get('initials'),
                Favorite(
                  initials: docs[i].get('initials'),
                  isFavorite: false,
                ),
              );
            

            return MediumCard(
              medium: md,
            );
          ,
        );
      ,
    );
  

被调用的 Medium 构造函数是这个:

class Medium 
  final String initials;
  final String longName;
  final Map<String, Quantity> ingredients;
  final List<String> steps;
  final PhysicalState mediumState;
  String reference;
  bool isComplement;
  String ps;

  Medium(
    required this.initials,
    required this.longName,
    required this.ingredients,
    required this.steps,
    required this.mediumState,
    this.reference = '',
    this.isComplement = false,
    this.ps = '',
  );

我可以发布很多尝试和错误消息,但帖子会不必要地长,所以只有一次尝试,尝试使用与其他条目 docs[i].get('ingredients') 相同的逻辑,这给了我这个:

type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Map<String, Quantity>'

请务必注意,“成分”中的 os 数量可能会有所不同,并且并不总是与上述示例中的两个相同(水和琼脂)。

最后,这个问题。如何调整 ListView.builder 代码中“成分”的导入以正确读取 Firebase 中的那部分数据?

我在 Flutter 2.2.3cloud_firestore: ^2.4.0firebase_core: ^1.4.0

Ps.:很抱歉有任何英文错误。我不是母语人士(“作家”)。

【问题讨论】:

嗨@Rodrigo,我可以检查您的“步数”地图是否是动态的(意味着步数可以改变)?如果是这样,你如何在颤动中映射它?我在 ***.com/questions/69052105/… 面临类似的问题。很想听听您对此的看法。谢谢! 嗨@scottlee。我的步骤确实是动态大小的。我用来从 Firestore 解析这些信息的代码是:steps: doc.data().containsKey('steps') ? doc.get('steps').cast&lt;String&gt;() : &lt;String&gt;[],。重要的是要注意,我的“步骤”数据实际上是 firestore 中的一个数组,它对应于 Dart/Flutter 中的 List 而不是地图。我会看看你链接的问题。如果对如何提供帮助有任何想法,我会在那里发帖。 【参考方案1】:

经过更多的尝试和失败,我能够通过添加到 ListView.builder 代码来解决我的问题:

    var docs = media.data!.docs;
    var box = Hive.box<Favorite>('favorites');

    Map<String, Quantity> myIngredients = ;

    return ListView.builder(
      itemCount: media.data!.docs.length,
      itemBuilder: (ctx, i) 
        // Convert data from firebase into <String, Quantity>
        Map.from(docs[i].get('ingredients')).entries.forEach((e) 
          myIngredients[e.key] = Quantity(
            amount: e.value['amount'].toDouble(),
            unit: e.value['unit'],
          );
        );

        Medium md = Medium(
          initials: docs[i].get('initials'),
          longName: docs[i].get('longName'),
          ingredients: myIngredients,
          steps: docs[i].get('steps').cast<String>(),
          mediumState: PhysicalState.values.elementAt(
            docs[i].get('mediumState'),
          ),
          reference: docs[i].data().containsKey('reference')
              ? docs[i].get('reference')
              : '',
          isComplement: docs[i].data().containsKey('isComplement')
              ? docs[i].get('isComplement')
              : false,
          ps: docs[i].data().containsKey('ps') ? docs[i].get('ps') : '',
        );

所以使用Map.from 方法我能够设置Map&lt;String, Quantity&gt; 实例并使用它。

如果有人找到更好、更清洁的解决方案,我很想看看。 非常感谢。

【讨论】:

以上是关于Flutter - 从 Cloud Firestore 读取嵌套地图的主要内容,如果未能解决你的问题,请参考以下文章

Flutter - 从 Cloud Firestore 读取嵌套地图

如何使用 graphql 从 Firebase 使用 Flutter 从 Cloud Firestore 获取数据?

在进行身份验证之前,如何使用 Flutter 从 Cloud Firestore 搜索和访问数据?

Flutter Firebase Cloud 功能无法调用

Flutter Cloud Messaging:如何从应用程序发送通知(而不是从 Firebase 控制台)

如何从 Cloud Firestore 中获取数据,其中 user.uid 等于 Flutter 中的文档 ID?