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

Flutter解析API数据时触发Null check operator空值错误求助

问题排查与修复方案

错误原因分析

你遇到的“Null check operator used on a null value”错误,根源在模型类的JSON解析逻辑中:

  • Standing类的description字段声明为可空类型Description?,但解析时用了强制非空运算符!:descriptionValues.map[json["description"]]!,如果API返回的description为null,直接触发错误。
  • 其他枚举字段(如group、status)的解析也使用了!,若API返回的字段值不在枚举映射表中,同样会因null触发错误。

修复后的模型类代码

针对上述问题,修改模型的解析逻辑,处理可空情况并避免强制非空:

import 'dart:convert';

ModelSnfLig1 modelSnfLig1FromJson(String str) => ModelSnfLig1.fromJson(json.decode(str));

String modelSnfLig1ToJson(ModelSnfLig1 data) => json.encode(data.toJson());

class ModelSnfLig1 {
  String modelSnfLig1Get;
  Parameters parameters;
  List<dynamic> errors;
  int results;
  Paging paging;
  List<Response> response;

  ModelSnfLig1({
    required this.modelSnfLig1Get,
    required this.parameters,
    required this.errors,
    required this.results,
    required this.paging,
    required this.response,
  });

  factory ModelSnfLig1.fromJson(Map<String, dynamic> json) => ModelSnfLig1(
    modelSnfLig1Get: json["get"] ?? "",
    parameters: Parameters.fromJson(json["parameters"] ?? {}),
    errors: json["errors"] != null ? List<dynamic>.from(json["errors"].map((x) => x)) : [],
    results: json["results"] ?? 0,
    paging: Paging.fromJson(json["paging"] ?? {}),
    response: json["response"] != null ? List<Response>.from(json["response"].map((x) => Response.fromJson(x))) : [],
  );

  Map<String, dynamic> toJson() => {
    "get": modelSnfLig1Get,
    "parameters": parameters.toJson(),
    "errors": List<dynamic>.from(errors.map((x) => x)),
    "results": results,
    "paging": paging.toJson(),
    "response": List<dynamic>.from(response.map((x) => x.toJson())),
  };
}

class Paging {
  int current;
  int total;

  Paging({
    required this.current,
    required this.total,
  });

  factory Paging.fromJson(Map<String, dynamic> json) => Paging(
    current: json["current"] ?? 0,
    total: json["total"] ?? 0,
  );

  Map<String, dynamic> toJson() => {
    "current": current,
    "total": total,
  };
}

class Parameters {
  String league;
  String season;

  Parameters({
    required this.league,
    required this.season,
  });

  factory Parameters.fromJson(Map<String, dynamic> json) => Parameters(
    league: json["league"] ?? "",
    season: json["season"] ?? "",
  );

  Map<String, dynamic> toJson() => {
    "league": league,
    "season": season,
  };
}

class Response {
  League league;

  Response({
    required this.league,
  });

  factory Response.fromJson(Map<String, dynamic> json) => Response(
    league: League.fromJson(json["league"] ?? {}),
  );

  Map<String, dynamic> toJson() => {
    "league": league.toJson(),
  };
}

class League {
  int id;
  String name;
  String country;
  String logo;
  String flag;
  int season;
  List<List<Standing>> standings;

  League({
    required this.id,
    required this.name,
    required this.country,
    required this.logo,
    required this.flag,
    required this.season,
    required this.standings,
  });

  factory League.fromJson(Map<String, dynamic> json) => League(
    id: json["id"] ?? 0,
    name: json["name"] ?? "",
    country: json["country"] ?? "",
    logo: json["logo"] ?? "",
    flag: json["flag"] ?? "",
    season: json["season"] ?? 0,
    standings: json["standings"] != null 
        ? List<List<Standing>>.from(json["standings"].map((x) => 
            List<Standing>.from(x.map((x) => Standing.fromJson(x)))))
        : [],
  );

  Map<String, dynamic> toJson() => {
    "id": id,
    "name": name,
    "country": country,
    "logo": logo,
    "flag": flag,
    "season": season,
    "standings": List<dynamic>.from(standings.map((x) => List<dynamic>.from(x.map((x) => x.toJson())))),
  };
}

class Standing {
  int rank;
  Team team;
  int points;
  int goalsDiff;
  Group? group;
  String form;
  Status? status;
  Description? description;
  All all;
  All home;
  All away;
  DateTime update;

  Standing({
    required this.rank,
    required this.team,
    required this.points,
    required this.goalsDiff,
    this.group,
    required this.form,
    this.status,
    this.description,
    required this.all,
    required this.home,
    required this.away,
    required this.update,
  });

  factory Standing.fromJson(Map<String, dynamic> json) => Standing(
    rank: json["rank"] ?? 0,
    team: Team.fromJson(json["team"] ?? {}),
    points: json["points"] ?? 0,
    goalsDiff: json["goalsDiff"] ?? 0,
    group: json["group"] != null ? groupValues.map[json["group"]] : null,
    form: json["form"] ?? "",
    status: json["status"] != null ? statusValues.map[json["status"]] : null,
    description: json["description"] != null ? descriptionValues.map[json["description"]] : null,
    all: All.fromJson(json["all"] ?? {}),
    home: All.fromJson(json["home"] ?? {}),
    away: All.fromJson(json["away"] ?? {}),
    update: json["update"] != null ? DateTime.parse(json["update"]) : DateTime.now(),
  );

  Map<String, dynamic> toJson() => {
    "rank": rank,
    "team": team.toJson(),
    "points": points,
    "goalsDiff": goalsDiff,
    "group": group != null ? groupValues.reverse[group] : null,
    "form": form,
    "status": status != null ? statusValues.reverse[status] : null,
    "description": description != null ? descriptionValues.reverse[description] : null,
    "all": all.toJson(),
    "home": home.toJson(),
    "away": away.toJson(),
    "update": update.toIso8601String(),
  };
}

class All {
  int played;
  int win;
  int draw;
  int lose;
  Goals goals;

  All({
    required this.played,
    required this.win,
    required this.draw,
    required this.lose,
    required this.goals,
  });

  factory All.fromJson(Map<String, dynamic> json) => All(
    played: json["played"] ?? 0,
    win: json["win"] ?? 0,
    draw: json["draw"] ?? 0,
    lose: json["lose"] ?? 0,
    goals: Goals.fromJson(json["goals"] ?? {}),
  );

  Map<String, dynamic> toJson() => {
    "played": played,
    "win": win,
    "draw": draw,
    "lose": lose,
    "goals": goals.toJson(),
  };
}

class Goals {
  int goalsFor;
  int against;

  Goals({
    required this.goalsFor,
    required this.against,
  });

  factory Goals.fromJson(Map<String, dynamic> json) => Goals(
    goalsFor: json["for"] ?? 0,
    against: json["against"] ?? 0,
  );

  Map<String, dynamic> toJson() => {
    "for": goalsFor,
    "against": against,
  };
}

enum Description {
  PROMOTION,
  PROMOTION_PLAY_OFF,
  RELEGATION
}

final descriptionValues = EnumValues({
  "Promotion": Description.PROMOTION,
  "Promotion Play-off": Description.PROMOTION_PLAY_OFF,
  "Relegation": Description.RELEGATION
});

enum Group {
  THE_1_LIG_REGULAR_SEASON
}

final groupValues = EnumValues({
  "1. Lig: Regular Season": Group.THE_1_LIG_REGULAR_SEASON
});

enum Status {
  DOWN,
  SAME,
  UP
}

final statusValues = EnumValues({
  "down": Status.DOWN,
  "same": Status.SAME,
  "up": Status.UP
});

class Team {
  int id;
  String name;
  String logo;

  Team({
    required this.id,
    required this.name,
    required this.logo,
  });

  factory Team.fromJson(Map<String, dynamic> json) => Team(
    id: json["id"] ?? 0,
    name: json["name"] ?? "",
    logo: json["logo"] ?? "",
  );

  Map<String, dynamic> toJson() => {
    "id": id,
    "name": name,
    "logo": logo,
  };
}

class EnumValues<T> {
  Map<String, T> map;
  late Map<T, String> reverseMap;

  EnumValues(this.map);

  Map<T, String> get reverse {
    reverseMap = map.map((k, v) => MapEntry(v, k));
    return reverseMap;
  }
}

优化后的API调用代码

同时优化调用逻辑,增加响应体打印(方便排查字段问题),并处理callC可能返回null的情况:

Future<ModelSnfLig1?> callC() async {
  try {
    final response = await http.get(
      Uri.parse("https://v3.football.api-sports.io/standings?league=204&season=2023"),
      headers: headers,
    );
    
    if (response.statusCode == 200) {
      // 临时打印响应体,确认字段是否符合预期
      print("API Response: ${response.body}");
      
      final result = modelSnfLig1FromJson(response.body);
      if (mounted) {
        setState(() {
          Deneme = result;
        });
      }
      return result;
    } else {
      print("HTTP Error Code: ${response.statusCode}");
      print("Error Response: ${response.body}");
    }
  } catch (e) {
    print("Exception: ${e.toString()}");
  }
  return null;
}

Future<void> fetchData() async {
  await callC();
  url = Uri.parse(widget.value);
}

@override
void initState() {
  super.initState();
  fetchData();
}

关键修复点说明

  1. 枚举字段处理:将group、status改为可空类型,解析时不再用!,而是判断字段存在后再映射。
  2. 可空字段兼容:所有JSON解析节点都增加??默认值,避免字段为null时触发错误。
  3. 错误排查辅助:增加响应体打印,方便确认API返回的字段是否和模型预期一致。

内容的提问来源于stack exchange,提问作者Çikapu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 01:22:33