You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 12:15:37