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

Flutter解析API返回JSON渲染ListView时提示plans字段未初始化

问题原因

一共4个核心问题导致报错:

  • JSON字段映射不匹配:Datum类的fromJson方法中,读取operator_id、circle_id字段时错误使用了operatorid、circleid作为key,和接口返回的JSON key名不一致,解析时会触发空值错误,导致plans对象赋值失败。
  • 异步流程处理缺失:API请求是异步操作,你用late修饰plans字段承诺一定会初始化,但没有在组件初始化阶段触发请求、等待数据返回,首次build执行时直接访问未赋值的plans,直接触发LateInitializationError。
  • 组件语法错误:Scaffold的body属性下同时写了Text和ListView两个组件,没有父容器包裹属于语法错误;同时ListView中错误使用widget.plans.data.length访问字段,plans是State内部变量,不属于Widget属性,访问路径错误。
  • 异常处理缺失:接口请求逻辑没有判断HTTP响应状态码,一旦接口返回401/403/500等错误状态,直接解析响应体会抛出异常,导致plans永远无法被赋值。
修复方案

1. 修正模型类字段映射

对齐接口返回的JSON key,同时增加类型兼容处理,修正后的Datum.fromJson代码如下:

factory Datum.fromJson(Map<String, dynamic> json) => Datum(
  id: json["id"].toString(),
  operatorid: json["operator_id"].toString(),
  circleid: json["circle_id"].toString(),
  rechargeAmount: json["recharge_amount"].toString(),
  rechargeTalktime: json["recharge_talktime"].toString(),
  rechargeValidity: json["recharge_validity"].toString(),
  rechargeShortdesc: json["recharge_short_desc"].toString(),
  rechargeLongdesc: json["recharge_long_desc"].toString(),
  rechargeType: json["recharge_type"].toString(),
  updatedAt: json["updated_at"].toString(),
);

2. 修正接口请求逻辑

增加HTTP状态码判断,避免错误响应直接触发解析异常:

Future<Plans> getPlanDetails(
      String operator, String circle, String rechargeType) async {
    var planUrl = Uri.parse(
        'https://api.datayuge.com/v6/rechargeplans/?apikey=$datayugeApiKey&operator_id=$operator&circle_id=$circle&recharge_type=$rechargeType&limit=50&order_by=recharge_amount');
    debugPrint(planUrl.toString());
    final response = await http.get(planUrl);
    debugPrint(response.body);
    if (response.statusCode == 200) {
      String jsonString = response.body;
      return plansFromJson(jsonString);
    } else {
      throw Exception('请求失败,状态码:${response.statusCode}');
    }
  }

3. 重写页面组件逻辑

去掉不安全的late修饰,增加加载、错误状态处理,在组件初始化阶段触发数据请求,修正后的页面代码:

class RechargePlansView extends StatefulWidget {
  final String operator;
  final String circle;
  final String rechargeType;
  const RechargePlansView({
    Key? key,
    required this.operator,
    required this.circle,
    required this.rechargeType
  }) : super(key: key);

  @override
  State<RechargePlansView> createState() => _RechargePlansViewState();
}

class _RechargePlansViewState extends State<RechargePlansView> {
  List<Datum> planList = [];
  bool isLoading = true;
  String? errorMsg;

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

  Future<void> fetchPlanData() async {
    try {
      final res = await getPlanDetails(
        widget.operator,
        widget.circle,
        widget.rechargeType
      );
      setState(() {
        planList = res.data;
        isLoading = false;
      });
    } catch (e) {
      setState(() {
        errorMsg = e.toString();
        isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Recharge Plans')),
      body: isLoading
        ? const Center(child: CircularProgressIndicator())
        : errorMsg != null
          ? Center(child: Text('加载失败:$errorMsg'))
          : ListView.builder(
              itemCount: planList.length,
              itemBuilder: (context, index) {
                final currentItem = planList[index];
                return ListTile(
                  leading: CircleAvatar(
                    radius: 28,
                    child: Center(
                      child: Text('\u{20B9}${currentItem.rechargeAmount}'),
                    ),
                  ),
                  title: Text('Talktime: ${currentItem.rechargeTalktime} Validity: ${currentItem.rechargeValidity}'),
                  subtitle: Text(currentItem.rechargeShortdesc),
                );
              }
            ),
    );
  }
}
注意事项
  • 所有异步加载的数据不要用late强制标记非空,必须预留空状态、加载状态、错误状态的UI逻辑,避免组件首次渲染时访问未初始化的字段。
  • JSON序列化时必须严格对齐接口返回的key名,建议通过代码生成工具自动生成序列化逻辑,减少手写key的错误。
  • Flutter组件的布局属性(如Scaffold的body、Container的child)只能接收单个组件,多组件布局需要用Column、Row、Stack等容器类组件包裹。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 09:03:51