我正在努力通过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>
我真的希望对此有所帮助,因为我能够理解的唯一示例是列表,而不是地图。我也想在这个应用程序的几个地方使用类似的方法,所以弄清楚这一点至关重要。
答案 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>
。解析它的步骤如下:
.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中,通常会根据用例使用factory或named构造函数,但我绝不是专家。