首页 > 解决方案 > Flutter - 从 Cloud Firestore 读取嵌套地图

问题描述

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

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

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.0并且firebase_core: ^1.4.0

Ps.:如有英文错误,请见谅。我不是母语人士(“作家”)。

标签: flutterdictionarydartgoogle-cloud-firestore

解决方案


经过更多的尝试和失败后,我能够通过添加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<String, Quantity>实例并使用它。

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


推荐阅读