Flutter/Dart类型错误:Name?/Type?无法赋值给Name/Type参数
解决FFXIVCollect法术API数据模型的类型匹配错误
调用FFXIVCollect的法术API时,用QuickType生成了Dart数据模型,但碰到两个类型不匹配错误:
参数类型'Name?'无法赋值给参数类型'Name'。
参数类型'Type?'无法赋值给参数类型'Type'。
错误出现在Aspect类fromMap方法的name: nameValues.map[json["name"]],以及Source类fromMap方法的type: typeValues.map[json["type"]]位置。
生成的模型代码
import 'dart:convert'; List<FinalApi> finalApiFromMap(String str) => List<FinalApi>.from(json.decode(str).map((x) => FinalApi.fromMap(x))); String finalApiToMap(List<FinalApi> data) => json.encode(List<dynamic>.from(data.map((x) => x.toMap()))); class FinalApi { FinalApi({ required this.id, required this.name, required this.description, required this.tooltip, required this.order, required this.rank, required this.patch, required this.owned, required this.icon, required this.type, required this.aspect, required this.sources, }); int id; String name; String description; String tooltip; int order; int rank; String patch; String owned; String icon; Aspect type; Aspect aspect; List<Source> sources; factory FinalApi.fromMap(Map<String, dynamic> json) => FinalApi( id: json["id"], name: json["name"], description: json["description"], tooltip: json["tooltip"], order: json["order"], rank: json["rank"], patch: json["patch"], owned: json["owned"], icon: json["icon"], type: Aspect.fromMap(json["type"]), aspect: Aspect.fromMap(json["aspect"]), sources: List<Source>.from(json["sources"].map((x) => Source.fromMap(x))), ); Map<String, dynamic> toMap() => { "id": id, "name": name, "description": description, "tooltip": tooltip, "order": order, "rank": rank, "patch": patch, "owned": owned, "icon": icon, "type": type.toMap(), "aspect": aspect.toMap(), "sources": List<dynamic>.from(sources.map((x) => x.toMap())), }; } class Aspect { Aspect({ required this.id, required this.name, }); int id; Name name; factory Aspect.fromMap(Map<String, dynamic> json) => Aspect( id: json["id"], name: nameValues.map[json["name"]], ); Map<String, dynamic> toMap() => { "id": id, "name": nameValues.reverse[name], }; } enum Name { WATER, FIRE, BLUNT, PIERCING, LIGHTNING, NONE, EARTH, SLASHING, ICE, WIND, PIERCING_FIRE, BLUNT_EARTH, MAGIC, PHYSICAL } final nameValues = EnumValues({ "Blunt": Name.BLUNT, "Blunt/Earth": Name.BLUNT_EARTH, "Earth": Name.EARTH, "Fire": Name.FIRE, "Ice": Name.ICE, "Lightning": Name.LIGHTNING, "Magic": Name.MAGIC, "None": Name.NONE, "Physical": Name.PHYSICAL, "Piercing": Name.PIERCING, "Piercing/Fire": Name.PIERCING_FIRE, "Slashing": Name.SLASHING, "Water": Name.WATER, "Wind": Name.WIND }); class Source { Source({ required this.type, required this.text, required this.relatedType, required this.relatedId, }); Type type; String text; dynamic relatedType; dynamic relatedId; factory Source.fromMap(Map<String, dynamic> json) => Source( type: typeValues.map[json["type"]], text: json["text"], relatedType: json["related_type"], relatedId: json["related_id"], ); Map<String, dynamic> toMap() => { "type": typeValues.reverse[type], "text": text, "related_type": relatedType, "related_id": relatedId, }; } enum Type { OTHER, DUNGEON } final typeValues = EnumValues({"Dungeon": Type.DUNGEON, "Other": Type.OTHER}); class EnumValues<T> { late Map<String, T> map; late Map<T, String> reverseMap; EnumValues(this.map); Map<T, String> get reverse { if (reverseMap == null) { reverseMap = map.map((k, v) => new MapEntry(v, k)); } return reverseMap; } }
问题原因
从Map中通过nameValues.map[json["name"]]和typeValues.map[json["type"]]取值时,若API返回的字段不在枚举映射内或字段缺失,会返回null。但Aspect和Source的构造函数要求的是不可为空的Name/Type类型,因此触发类型不匹配错误。
解决方法
以下三种方案可根据实际场景选择:
1. 强制非空断言(确保API返回值全覆盖)
如果能保证API返回的所有name和type字段都存在于枚举映射中,可使用!断言告诉编译器该值不会为null:
// 修改Aspect.fromMap factory Aspect.fromMap(Map<String, dynamic> json) => Aspect( id: json["id"], name: nameValues.map[json["name"]]!, ); // 修改Source.fromMap factory Source.fromMap(Map<String, dynamic> json) => Source( type: typeValues.map[json["type"]]!, text: json["text"], relatedType: json["related_type"], relatedId: json["related_id"], );
注意:若API返回未覆盖的值,会抛出空指针异常。
2. 设置默认值(安全兜底)
当取值为null时,提供一个默认枚举值,避免空指针:
// 修改Aspect.fromMap factory Aspect.fromMap(Map<String, dynamic> json) => Aspect( id: json["id"], name: nameValues.map[json["name"]] ?? Name.NONE, ); // 修改Source.fromMap factory Source.fromMap(Map<String, dynamic> json) => Source( type: typeValues.map[json["type"]] ?? Type.OTHER, text: json["text"], relatedType: json["related_type"], relatedId: json["related_id"], );
3. 允许字段为空(适配缺失场景)
若API确实可能返回null,可将Aspect和Source的对应字段改为可空类型,并在toMap方法中处理null情况:
// 修改Aspect类 class Aspect { Aspect({ required this.id, this.name, }); int id; Name? name; factory Aspect.fromMap(Map<String, dynamic> json) => Aspect( id: json["id"], name: nameValues.map[json["name"]], ); Map<String, dynamic> toMap() => { "id": id, "name": name != null ? nameValues.reverse[name!] : null, }; } // 修改Source类 class Source { Source({ this.type, required this.text, required this.relatedType, required this.relatedId, }); Type? type; String text; dynamic relatedType; dynamic relatedId; factory Source.fromMap(Map<String, dynamic> json) => Source( type: typeValues.map[json["type"]], text: json["text"], relatedType: json["related_type"], relatedId: json["related_id"], ); Map<String, dynamic> toMap() => { "type": type != null ? typeValues.reverse[type!] : null, "text": text, "related_type": relatedType, "related_id": relatedId, }; }
后续使用这些字段时需处理null情况。
内容的提问来源于stack exchange,提问作者yuuu
相关产品推荐
相关产品推荐

