从Flutter写入和读取嵌套映射到Firebase文档

时间:2020-05-31 22:27:26

标签: firebase flutter

我正在努力通过flutter应用程序在Firebase中编写(也尚未弄清楚如何读取)嵌套地图。我正在编写一个费用跟踪程序,其中类别列表存储在每个日志中。我可以在Firebase上进行映射,保存和检索原始字段,而迷路的地方是尝试将类别和子类别的映射写入Firebase,然后如何读取它们。

解决类别和子类别本质上是相同的,因此我只介绍其中之一。另外,我目前将类别ID用作类别类本身的键和一部分,稍后我将从类本身中删除该ID,因为我认为这是一种不好的做法,目前仅在其他地方为我提供帮助。 / p>

我也一直在遵循BLOC方法,所以我也有一些模型可以转换为用于处理Firebase的实体。

这是一个类别(我已经删除了不相关的信息,例如道具和字符串):

<pre>
    class MyCategory extends Equatable {
  final String id;
  final String name;
  final IconData iconData;

  MyCategory({@required this.id, @required this.name, this.iconData});
  MyCategoryEntity toEntity() {
    return MyCategoryEntity(
      id: id,
      name: name,
      iconCodePoint: iconData.codePoint.toString(),
      iconFontFamily: iconData.fontFamily,
    );
  }

  static MyCategory fromEntity(MyCategoryEntity entity) {
    return MyCategory(
      id: entity.id,
      name: entity.name,
      iconData: IconData(int.parse(entity.iconCodePoint),
          fontFamily: entity.iconFontFamily),
    );
  }
}
</pre>

该类别的模型实体具有JsonSerialization,并且还使用了Icon codepoint / fontFamily,因为从Firebase来回传递似乎比直接使用IconData更友好。

<pre>
    @JsonSerializable()
class MyCategoryEntity extends Equatable {
  final String id;
  final String name;
  final String iconCodePoint;
  final String iconFontFamily;

  const MyCategoryEntity(
      {this.id, this.name, this.iconCodePoint, this.iconFontFamily});

  factory MyCategoryEntity.fromJson(Map<String, dynamic> json) =>
      _$MyCategoryEntityFromJson(json);

  Map<String, dynamic> toJson() => _$MyCategoryEntityToJson(this);
</pre>

我认为您不需要看到Log模型,更多的是直接与Firebase对话的LogEntity。这是我完全陷入困境的地方。我已经看到了一些如何传递对象列表的示例,但似乎无法弄清楚如何传递对象图。我也不确定如何将其从Firestore读回LogEntity(如下面的两个TODO所示)。

<pre>
    class LogEntity extends Equatable {
  final String uid;
  final String id;
  final String logName;
  final String currency;
  final bool active;
  //String is the category id
  final Map<String, MyCategory> categories;
  final Map<String, MySubcategory> subcategories;


  const LogEntity({this.uid, this.id, this.logName, this.currency, this.categories, this.subcategories, this.active});


  static LogEntity fromSnapshot(DocumentSnapshot snap) {
    return LogEntity(
      uid: snap.data[UID],
      id: snap.documentID,
      logName: snap.data[LOG_NAME],
      currency: snap.data[CURRENCY_NAME],
      //TODO de-serialize categories and subcategories
      active: snap.data[ACTIVE],

    );
  }

  Map<String, Object> toDocument() {
    return {
      UID: uid,
      LOG_NAME: logName,
      CURRENCY_NAME: currency,
    //TODO serialize categories and subcategories, this does doesn't work
      CATEGORIES: categories.map((key, value) => categories[key].toEntity().toJson()).toString(),
      ACTIVE: active,
    };
  }
}
</pre>

我真的希望对此有所帮助,因为我能够理解的唯一示例是列表,而不是地图。我也想在这个应用程序的几个地方使用类似的方法,所以弄清楚这一点至关重要。

Git:https://github.com/cver22/expenses2

1 个答案:

答案 0 :(得分:3)

导出数据

Firebase需要一个Map<String, dynamic>,其中dynamic是其支持的类型之一(例如字符串,数字,映射,数组等)。因此,如果您有一个自定义对象,例如您的示例中的MyCategory,则需要将其.map()设置为一种受支持的类型(在这种情况下,还需要另一个Map<String, dynamic>)。因此,您可以执行<map that you need to convert>.map((key, value) => MapEntry(key, <method to convert each value to a Map<String, dynamic>>))。您的示例几乎是正确的,但是它有一个toString(),它将使Firebase将其存储为字符串,而将 not 存储为映射(这不是所需的行为)。如评论中所述,删除toString()应该可以解决该问题。

检索数据

与导出数据类似,Firebase将为您提供Map<String, dynamic>,其中dynamic是Firebase支持的类型之一,类别和子类别又为Map<String, dynamic>。因此,您需要将动态的snap.data[KEY]转换为代码中相应类型的Map<String, YourObject>。解析它的步骤如下:

  1. 以动态方式从快照获取数据
  2. 将此动态数据投射到地图上
  3. .map()将此强制转换的数据转换为您自己的类型

因此,应执行以下操作:

    return LogEntity(
      uid: snap.data[UID],
      id: snap.documentID,
      logName: snap.data[LOG_NAME],
      currency: snap.data[CURRENCY_NAME],
      categories: (snap.data[CATEGORIES] as Map<String, dynamic>).map((key, value) => MapEntry(key, MyCategory.fromEntity(MyCategoryEntity.fromJson(value)))),
      // todo subcategories should follow the same form as categories; I will leave that one up to you
      active: snap.data[ACTIVE],

    );

检索嵌套基元类型

如果有人发现此问题,以寻找如何解析使用Firebase中原始类型的嵌套地图/列表(例如Map<String, String>Map<String, int> ,或List<String>仅举几例)。在这种情况下,请检查Map<String, PrimitiveType>.from()List<PrimitiveType>.from()构造函数;假设PrimitiveType是Firebase支持的类型之一,它们可能会完全按照您想要的方式工作。如果没有,请参见上面的答案。


还有一件小事:我没有仔细检查您的所有代码,但我不禁注意到的是您的static LogEntity fromSnapshot()。在Dart中,通常会根据用例使用factorynamed构造函数,但我绝不是专家。