Flutter FutureBuilder持续加载且出现int转double类型转换错误
看起来你碰到了两个关联的问题:FutureBuilder一直卡在加载状态,同时控制台抛出type 'int' is not a subtype of type 'double'的类型转换错误。我来一步步帮你分析和解决:
一、先解决FutureBuilder持续加载的问题
你当前的FutureBuilder代码里,future: AllProductService().getAllProducts()是直接在build方法中创建的Future实例。这会导致每次Widget重建(比如屏幕旋转、父组件状态变化)时,都会重新发起一次全新的网络请求,FutureBuilder会一直等待新的Future完成,从而看起来“永远在加载”。
解决方案:把Future实例存在State变量中,只初始化一次
修改你的home_page.dart代码,将Future的创建移到initState里:
class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { // 用late延迟初始化,只在组件初始化时赋值一次 late final Future<List<ProductModel>> _productsFuture; @override void initState() { super.initState(); // 仅在组件第一次创建时发起请求 _productsFuture = AllProductService().getAllProducts(); } @override Widget build(BuildContext context) { return Scaffold( body: FutureBuilder<List<ProductModel>>( // 使用存储好的Future实例,避免重复发起请求 future: _productsFuture, builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator.adaptive()); } else if (snapshot.hasError) { return Center(child: Text('加载出错: ${snapshot.error}')); } final products = snapshot.data!; return GridView.builder( itemCount: products.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 1.6, ), itemBuilder: (_, i) => Text(products[i].title), ); }, ), ); } }
二、解决int转double的类型转换错误
虽然你在fromJson方法里已经用了(json['price'] as num).toDouble()这类处理,但还是报错,大概率是下面两个原因之一:
1. Api类的get方法没有正确解析JSON响应
检查你的Api类实现,确保它正确调用了jsonDecode解析响应体。如果Api.get()返回的是原始字符串(response.body)而不是解析后的JSON对象/数组,那么res as List<dynamic>会抛出类型错误,进而引发后续的转换异常。
正确的Api类get方法示例:
import 'package:http/http.dart' as http; import 'dart:convert'; class Api { Future<dynamic> get({required String url}) async { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { // 关键:解析JSON响应为动态类型(这里返回List或Map) return jsonDecode(response.body); } else { throw Exception('请求失败,状态码: ${response.statusCode}'); } } }
2. 给JSON解析添加安全防护
为了避免意外的空值或类型不匹配,建议在fromJson方法中加入空值检查和默认值,这样即使某个字段异常,也不会直接崩溃:
// 修改ProductModel.fromJson factory ProductModel.fromJson(Map<String, dynamic> json) { return ProductModel( id: (json['id'] as num?)?.toInt() ?? 0, title: json['title'] as String? ?? '无标题', price: (json['price'] as num?)?.toDouble() ?? 0.0, description: json['description'] as String? ?? '无描述', image: json['image'] as String? ?? '', rating: (json['rating'] as Map<String, dynamic>?) != null ? RatingModel.fromJson(json['rating'] as Map<String, dynamic>) : RatingModel(rate: 0.0, count: 0), ); } // 修改RatingModel.fromJson factory RatingModel.fromJson(Map<String, dynamic> json) { return RatingModel( rate: (json['rate'] as num?)?.toDouble() ?? 0.0, count: (json['count'] as num?)?.toInt() ?? 0, ); }
三、调试验证
可以在getAllProducts方法中添加打印语句,确认数据是否正确解析:
Future<List<ProductModel>> getAllProducts() async { final res = await Api().get( url: 'https://fakestoreapi.com/products', ); print('解析后的原始数据: $res'); print('数据长度: ${(res as List).length}'); final List<dynamic> data = res as List<dynamic>; return data .map((e) => ProductModel.fromJson(e as Map<String, dynamic>)) .toList(); }
这样能直观看到解析后的数据结构是否符合预期。
总结一下:先修复Future重复创建的问题,再确保JSON解析逻辑正确,最后给解析加一层安全防护,你的产品列表应该就能正常显示了。
内容来源于stack exchange

