Flutter API请求报错:type 'List<dynamic>' is not a subtype of type 'String'
解决Flutter调用MockAPI时
type 'List<dynamic>' is not a subtype of type 'String'错误 问题核心原因
- API返回的是数组(List),但原代码试图将整个数组当作单个
Groceries对象解析,类型不匹配。 - API返回的
id是字符串类型,而Groceries类中定义为int,直接赋值会引发类型转换错误。 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
相关产品推荐
相关产品推荐

