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

Flutter类型转换错误:Null不是int的子类型,求解决方案

环境信息
  • Flutter 3.3.9 • stable渠道
  • 框架 • 修订版本b8f7f1f986(2周前)• 2022-11-23 06:43:51 +0900
  • 引擎 • 修订版本8f2221fbef
  • 工具 • Dart 2.18.5 • DevTools 2.15.0
问题描述

我花了数小时尝试解决JSON解析相关错误,但未能成功,恳请告知解决方法。当前使用Flutter空安全特性,以下是我使用的模型代码、API请求响应及生成的代码:

base_response_model.dart

import 'package:json_annotation/json_annotation.dart';

part 'base_response_model.g.dart';

@JsonSerializable()
class BaseResponseModel {
  final int page;

  @JsonKey(name: 'page_size')
  final int pageSize;

  final int total;
  final int pages;

  @JsonKey(name: 'prev_page', defaultValue: 0)
  final dynamic prevPage;

  @JsonKey(name: 'next_page', defaultValue: 0)
  final dynamic nextPage;

  BaseResponseModel({
    required this.page,
    required this.pageSize,
    required this.total,
    required this.pages,
    required this.prevPage,
    required this.nextPage,
  });

  factory BaseResponseModel.fromJson(Map<String, dynamic> json) =>
      _$BaseResponseModelFromJson(json);

  Map<String, dynamic> toJson() => _$BaseResponseModelToJson(this);
}

get_municipalities_by_province_response_model.dart

import 'package:delivery/app/data/models/andariego/andariego_models/municipality_model.dart';
import 'package:delivery/app/data/models/andariego/andariego_response_models/base_response_model.dart';
import 'package:json_annotation/json_annotation.dart';

part 'get_municipalities_by_province_response_model.g.dart';

@JsonSerializable()
class GetMunicipalitiesByProvinceResponseModel extends BaseResponseModel {
  final List<MunicipalityModel> data;

  GetMunicipalitiesByProvinceResponseModel({
    required super.page,
    required super.pageSize,
    required super.total,
    required super.pages,
    required super.prevPage,
    required super.nextPage,
    required this.data,
  });

  factory GetMunicipalitiesByProvinceResponseModel.fromJson(
          Map<String, dynamic> json) =>
      _$GetMunicipalitiesByProvinceResponseModelFromJson(json);

  @override
  Map<String, dynamic> toJson() =>
      _$GetMunicipalitiesByProvinceResponseModelToJson(this);
}

municipality_model.dart

import 'package:delivery/app/data/models/andariego/andariego_models/base_andariego_model.dart';
import 'package:json_annotation/json_annotation.dart';

part 'municipality_model.g.dart';

@JsonSerializable()
class MunicipalityModel extends BaseAndariegoModel {
  final int parent;

  MunicipalityModel({
    required super.id,
    required super.name,
    required this.parent,
  });

  factory MunicipalityModel.fromJson(Map<String, dynamic> json) =>
      _$MunicipalityModelFromJson(json);

  @override
  Map<String, dynamic> toJson() => _$MunicipalityModelToJson(this);
}

API响应示例(api_response.json)

{
  "page": 1,
  "page_size": 20,
  "total": 11,
  "pages": 1,
  "prev_page": null,
  "next_page": null,
  "data": [
    {
      "id": 1188,
      "name": "Consolación del Sur",
      "parent": 58
    },
    {
      "id": 1132,
      "name": "Guane",
      "parent": 58
    },
    {
      "id": 1125,
      "name": "La Palma",
      "parent": 58
    },
    {
      "id": 1124,
      "name": "Los Palacios",
      "parent": 58
    },
    {
      "id": 1186,
      "name": "Mantua",
      "parent": 58
    },
    {
      "id": 1182,
      "name": "Minas de Matahambre",
      "parent": 58
    },
    {
      "id": 1189,
      "name": "Pinar del Rio",
      "parent": 58
    },
    {
      "id": 1165,
      "name": "Sandino",
      "parent": 58
    },
    {
      "id": 1133,
      "name": "San Juan y Martínez",
      "parent": 58
    },
    {
      "id": 1187,
      "name": "San Luis",
      "parent": 58
    },
    {
      "id": 1169,
      "name": "Viñales",
      "parent": 58
    }
  ]
}

自动生成的代码

base_response_model.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'base_response_model.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

BaseResponseModel _$BaseResponseModelFromJson(Map<String, dynamic> json) =>
    BaseResponseModel(
      page: json['page'] as int,
      pageSize: json['page_size'] as int,
      total: json['total'] as int,
      pages: json['pages'] as int,
      prevPage: json['prev_page'] ?? 0,
      nextPage: json['next_page'] ?? 0,
    );

Map<String, dynamic> _$BaseResponseModelToJson(BaseResponseModel instance) =>
    <String, dynamic>{
      'page': instance.page,
      'page_size': instance.pageSize,
      'total': instance.total,
      'pages': instance.pages,
      'prev_page': instance.prevPage,
      'next_page': instance.nextPage,
    };

get_municipalities_by_province_response_model.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'get_municipalities_by_province_response_model.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

GetMunicipalitiesByProvinceResponseModel
    _$GetMunicipalitiesByProvinceResponseModelFromJson(
            Map<String, dynamic> json) =>
        GetMunicipalitiesByProvinceResponseModel(
          page: json['page'] as int,
          pageSize: json['page_size'] as int,
          total: json['total'] as int,
          pages: json['pages'] as int,
          prevPage: json['prev_page'] ?? 0,
          nextPage: json['next_page'] ?? 0,
          data: (json['data'] as List<dynamic>)
              .map((e) => MunicipalityModel.fromJson(e as Map<String, dynamic>))
              .toList(),
        );

Map<String, dynamic> _$GetMunicipalitiesByProvinceResponseModelToJson(
        GetMunicipalitiesByProvinceResponseModel instance) =>
    <String, dynamic>{
      'page': instance.page,
      'page_size': instance.pageSize,
      'total': instance.total,
      'pages': instance.pages,
      'prev_page': instance.prevPage,
      'next_page': instance.nextPage,
      'data': instance.data,
    };

municipality_model.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'municipality_model.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

MunicipalityModel _$MunicipalityModelFromJson(Map<String, dynamic> json) =>
    MunicipalityModel(
      id: json['id'] as int,
      name: json['name'] as String,
      parent: json['parent'] as int,
    );

Map<String, dynamic> _$MunicipalityModelToJson(MunicipalityModel instance) =>
    <String, dynamic>{
      'id': instance.id,
      'name': instance.name,
      'parent': instance.parent,
    };
解决方案

从代码和API响应来看,问题出在prevPage和nextPage的空值处理上:

  1. API返回的prev_page和next_page是null,但defaultValue仅在字段不存在时生效,字段存在但值为null时不会触发默认值;同时dynamic类型会导致空安全下的类型隐患。
  2. 生成代码中的json['prev_page'] ?? 0虽能处理null,但类型不明确,不符合空安全规范。

修改步骤如下:

1. 修正父类模型的字段定义

将prevPage和nextPage改为可空int?类型,移除defaultValue,并调整构造函数参数:

import 'package:json_annotation/json_annotation.dart';

part 'base_response_model.g.dart';

@JsonSerializable()
class BaseResponseModel {
  final int page;

  @JsonKey(name: 'page_size')
  final int pageSize;

  final int total;
  final int pages;

  @JsonKey(name: 'prev_page')
  final int? prevPage;

  @JsonKey(name: 'next_page')
  final int? nextPage;

  BaseResponseModel({
    required this.page,
    required this.pageSize,
    required this.total,
    required this.pages,
    this.prevPage,
    this.nextPage,
  });

  factory BaseResponseModel.fromJson(Map<String, dynamic> json) =>
      _$BaseResponseModelFromJson(json);

  Map<String, dynamic> toJson() => _$BaseResponseModelToJson(this);
}

2. 更新子类构造函数

因为父类参数不再是required,子类也要对应调整:

import 'package:delivery/app/data/models/andariego/andariego_models/municipality_model.dart';
import 'package:delivery/app/data/models/andariego/andariego_response_models/base_response_model.dart';
import 'package:json_annotation/json_annotation.dart';

part 'get_municipalities_by_province_response_model.g.dart';

@JsonSerializable()
class GetMunicipalitiesByProvinceResponseModel extends BaseResponseModel {
  final List<MunicipalityModel> data;

  GetMunicipalitiesByProvinceResponseModel({
    required super.page,
    required super.pageSize,
    required super.total,
    required super.pages,
    super.prevPage,
    super.nextPage,
    required this.data,
  });

  factory GetMunicipalitiesByProvinceResponseModel.fromJson(
          Map<String, dynamic> json) =>
      _$GetMunicipalitiesByProvinceResponseModelFromJson(json);

  @override
  Map<String, dynamic> toJson() =>
      _$GetMunicipalitiesByProvinceResponseModelToJson(this);
}

3. 重新生成序列化代码

执行命令重新生成代码:

flutter pub run build_runner build --delete-conflicting-outputs

修改后即可正确处理API返回的null值,完全符合Flutter空安全规范,避免类型转换错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 07:45:29