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

Flutter API请求报错:type 'List<dynamic>' is not a subtype of type 'String'

解决Flutter调用MockAPI时type 'List<dynamic>' is not a subtype of type 'String'错误

问题核心原因

  1. API返回的是数组(List),但原代码试图将整个数组当作单个Groceries对象解析,类型不匹配。
  2. API返回的id是字符串类型,而Groceries类中定义为int,直接赋值会引发类型转换错误。
  3. FutureBuilder泛型与实际返回类型不匹配,且内部嵌套ListView未正确处理,分支逻辑存在遗漏返回的问题。

修复步骤

1. 修改API请求与解析逻辑(store_groceries_api.dart)

将返回类型改为List<Groceries>,遍历解析数组中的每个对象,并处理id的类型转换:

import 'package:http/http.dart' as http;
import 'dart:convert';

class Groceries {
  final int id;
  final String name;
  final String images;

  const Groceries({required this.id, required this.name, required this.images});

  factory Groceries.fromJson(Map<String, dynamic> json) {
    return Groceries(
      // 将字符串id转为int
      id: int.parse(json['id']),
      name: json['name'],
      images: json['images'],
    );
  }
}

// 返回类型改为List<Groceries>
Future<List<Groceries>> fetchGroceries() async {
  final response = await http.get(Uri.parse(
      'https://6453dd71e9ac46cedf31d94e.mockapi.io/api/v1/groceries'));

  if (response.statusCode == 200) {
    final List<dynamic> responseJson = jsonDecode(response.body);
    // 遍历数组,逐个转为Groceries对象
    return responseJson.map((item) => Groceries.fromJson(item)).toList();
  } else {
    throw Exception("Failed to load groceries");
  }
}

2. 修改页面UI逻辑(explore_screen.dart)

更新Future类型与FutureBuilder泛型,修复嵌套ListView的显示问题,并正确遍历数据:

import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'store_groceries_api.dart';

class ExplorePage extends StatefulWidget {
  const ExplorePage({Key? key}) : super(key: key);

  @override
  _ExplorePageState createState() => _ExplorePageState();
}

class _ExplorePageState extends State<ExplorePage>
    with AutomaticKeepAliveClientMixin {
  // 更新Future类型为List<Groceries>
  late Future<List<Groceries>> futureGroceries;

  @override
  void initState() {
    super.initState();
    futureGroceries = fetchGroceries();
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      body: ListView(
        children: [
          CarouselSlider(
            items: const [
              Padding(
                padding: EdgeInsets.all(20.0),
                child: Text('Placeholder'),
              ),
              Padding(
                padding: EdgeInsets.all(20.0),
                child: Text('Placeholder'),
              ),
              Padding(
                padding: EdgeInsets.all(20.0),
                child: Text('Placeholder'),
              ),
              Padding(
                padding: EdgeInsets.all(20.0),
                child: Text('Placeholder'),
              )
            ],
            options: CarouselOptions(
              autoPlay: true,
            ),
          ),
          // 更新FutureBuilder泛型为List<Groceries>
          FutureBuilder<List<Groceries>>(
            future: futureGroceries,
            builder: (context, AsyncSnapshot<List<Groceries>> snapshot) {
              if (snapshot.hasData) {
                // 返回ListView,设置shrinkWrap避免嵌套滚动问题
                return ListView.builder(
                  shrinkWrap: true,
                  physics: const NeverScrollableScrollPhysics(),
                  padding: const EdgeInsets.all(10),
                  itemCount: snapshot.data!.length,
                  itemBuilder: (context, index) {
                    final grocery = snapshot.data![index];
                    return ListTile(
                      title: Text(grocery.name),
                    );
                  },
                );
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }
              // 加载中状态
              return const Padding(
                padding: EdgeInsets.all(20.0),
                child: CircularProgressIndicator(),
              );
            },
          )
        ],
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}

关键修复点说明

  • 数组解析:API返回数组时,必须遍历每个元素转换为对象,不能直接将整个数组传入单个对象的构造函数。
  • 类型转换:API返回的字段类型需与实体类定义一致,字符串id要转为int。
  • 嵌套ListView:外层是ListView时,内部ListView需设置shrinkWrap: true和physics: NeverScrollableScrollPhysics()避免滚动冲突。
  • FutureBuilder泛型:泛型必须与Future返回类型一致,确保类型安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:02:52