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(); }
关键修复点说明
- 枚举字段处理:将
group、status改为可空类型,解析时不再用!,而是判断字段存在后再映射。 - 可空字段兼容:所有JSON解析节点都增加
??默认值,避免字段为null时触发错误。 - 错误排查辅助:增加响应体打印,方便确认API返回的字段是否和模型预期一致。
内容的提问来源于stack exchange,提问作者Çikapu
相关产品推荐
相关产品推荐

