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

Flutter解决'type 'Null' is not a subtype of Map<String,dynamic>'错误

问题:Flutter解析含Null字段的JSON时报错 type 'Null' is not a subtype of Map<String dynamic>

问题背景

调用接口获取JSON对象列表时,部分返回的JSON中userID字段为null,使用手动编写的解析代码转换数据时,抛出type 'Null' is not a subtype of Map<String dynamic>错误。

示例JSON结构

{
  "_id": "62dd4989eef1e8863eb65ffe",
  "shoopkeper": {
    "_id": "62d78910a0d65589c3d112af",
    "userName": "lakshya",
    "location": {
      "lat": 23.2403327,
      "lng": 77.4389773
    }
  },
  "offerId": {
    "_id": "62da41208be0d1f7a092e4ea",
    "offername": "thisisoffername",
    "imageURL": ""
  },
  "userID": {
    "_id": "62d5038dd7d46f3c025a0d3f",
    "userName": "Mahak "
  },
  "state": 0,
  "createdAt": "2022-07-24T13:30:49.534Z",
  "updatedAt": "2022-07-24T13:30:49.534Z",
  "__v": 0
}

注:部分返回的JSON中userID字段为null

当前JSON解析代码

class CollectedOffer {
  CollectedOffer({
    this.id,
    this.shoopkeper,
    this.offerId,
    this.userId,
    this.state,
    this.createdAt,
    this.updatedAt,
    this.v,
    this.redeemDate,
  });

  String? id;
  Shoopkeper? shoopkeper;
  OfferId? offerId;
  UserId? userId;
  int? state;
  DateTime? createdAt;
  DateTime? updatedAt;
  int? v;
  DateTime? redeemDate;

  factory CollectedOffer.fromJson(Map<String, dynamic> json) => CollectedOffer(
        id: json["_id"],
        shoopkeper: Shoopkeper.fromJson(json["shoopkeper"]),
        offerId: OfferId.fromJson(json["offerId"]),
        userId: UserId.fromJson(json["userID"]), // 核心问题:当json["userID"]为null时,强行调用fromJson会触发类型错误
        state: json["state"],
        createdAt: DateTime.parse(json["createdAt"]),
        updatedAt: DateTime.parse(json["updatedAt"]),
        v: json["__v"],
        redeemDate: DateTime.parse(json["redeemDate"]), // 额外问题:redeemDate字段不存在或为null时,此处会崩溃
      );
}

class OfferId {
  OfferId({
    this.id,
    this.offername,
    this.imageUrl,
  });

  String? id;
  String? offername;
  String? imageUrl;

  factory OfferId.fromJson(Map<String, dynamic> json) => OfferId(
        id: json["_id"],
        offername: json["offername"],
        imageUrl: json["imageURL"],
      );
}

class Shoopkeper {
  Shoopkeper({
    this.id,
    this.userName,
    this.location,
  });

  String? id;
  String? userName;
  Location? location;

  factory Shoopkeper.fromJson(Map<String, dynamic> json) => Shoopkeper(
        id: json["_id"],
        userName: json["userName"],
        location: Location.fromJson(json["location"]),
      );
}

class Location {
  Location({
    this.lat,
    this.lng,
  });

  double? lat;
  double? lng;

  factory Location.fromJson(Map<String, dynamic> json) => Location(
        lat: json["lat"],
        lng: json["lng"],
      );
}

class UserId {
  UserId({
    this.id,
    this.userName,
  });

  String? id;
  String? userName;

  factory UserId.fromJson(Map<String, dynamic> json) => UserId(
        id: json["_id"],
        userName: json["userName"],
      );
}

错误信息

type 'Null' is not a subtype of Map<String dynamic>

FutureBuilder相关代码

if (response.statusCode == 200) {
  jsonResponse = json.decode(response.body)['objects'];
  if (jsonResponse.isNotEmpty) {
    setState(() {
      isdataNull = false;
    });
  }

  return jsonResponse.map((data) => CollectedOffer.fromJson(data)).toList();
} else {
  throw Exception('${response.statusCode}');
}

解决方案

1. 修复userID字段的null处理

在CollectedOffer.fromJson方法中,先判断json["userID"]是否为null,仅当它不为null时才调用UserId.fromJson,否则直接赋值为null:

factory CollectedOffer.fromJson(Map<String, dynamic> json) => CollectedOffer(
      id: json["_id"],
      shoopkeper: Shoopkeper.fromJson(json["shoopkeper"]),
      offerId: OfferId.fromJson(json["offerId"]),
      userId: json["userID"] != null ? UserId.fromJson(json["userID"]) : null, // 新增null判断
      state: json["state"],
      createdAt: DateTime.parse(json["createdAt"]),
      updatedAt: DateTime.parse(json["updatedAt"]),
      v: json["__v"],
      redeemDate: json["redeemDate"] != null ? DateTime.parse(json["redeemDate"]) : null, // 同时修复redeemDate的null问题
    );

2. 可选:为其他可能为null的字段添加防护

如果接口返回的shoopkeper或offerId也存在为null的情况,同样需要添加null判断:

shoopkeper: json["shoopkeper"] != null ? Shoopkeper.fromJson(json["shoopkeper"]) : null,
offerId: json["offerId"] != null ? OfferId.fromJson(json["offerId"]) : null,

3. 推荐:使用json_serializable自动生成解析代码

手动编写解析代码容易遗漏null判断,推荐使用官方推荐的json_serializable库,它会自动处理null字段和类型转换:

  • 添加依赖到pubspec.yaml:
    dependencies:
      json_annotation: ^4.8.1
    
    dev_dependencies:
      build_runner: ^2.4.4
      json_serializable: ^6.7.0
    
  • 改造实体类并添加注解:
    import 'package:json_annotation/json_annotation.dart';
    
    part 'collected_offer.g.dart';
    
    @JsonSerializable()
    class CollectedOffer {
      CollectedOffer({
        this.id,
        this.shoopkeper,
        this.offerId,
        this.userId,
        this.state,
        this.createdAt,
        this.updatedAt,
        this.v,
        this.redeemDate,
      });
    
      @JsonKey(name: '_id')
      String? id;
      Shoopkeper? shoopkeper;
      OfferId? offerId;
      @JsonKey(name: 'userID')
      UserId? userId;
      int? state;
      @JsonKey(fromJson: _dateTimeFromJson)
      DateTime? createdAt;
      @JsonKey(fromJson: _dateTimeFromJson)
      DateTime? updatedAt;
      @JsonKey(name: '__v')
      int? v;
      @JsonKey(fromJson: _dateTimeFromJson)
      DateTime? redeemDate;
    
      factory CollectedOffer.fromJson(Map<String, dynamic> json) => _$CollectedOfferFromJson(json);
      Map<String, dynamic> toJson() => _$CollectedOfferToJson(this);
    }
    
    // 日期转换工具方法
    DateTime? _dateTimeFromJson(String? dateStr) {
      if (dateStr == null) return null;
      return DateTime.parse(dateStr);
    }
    
    // 其他实体类同样添加@JsonSerializable()注解和对应的fromJson/toJson方法,示例省略
    
  • 运行命令生成解析代码:
    flutter pub run build_runner build
    

内容的提问来源于stack exchange,提问作者Arjun Malhotra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 22:15:41