Flutter RangeError(index)问题:API数据正常却无法展示
解决Flutter中RangeError(index)列表展示错误
从API获取数据并存储到模型后,使用ProductCardList组件通过GridView.builder展示数据时,出现范围错误:RangeError (index): Invalid value: Only valid value is 0: -1,数据已正确接收但无法正常渲染。
错误原因
- 索引越界未校验:
ProductCardList组件直接通过widget.dataModel[widget.index]访问数据,未对数据列表的有效性、索引的合法性做判断,当索引为-1(如数据加载过程中的临时状态)或列表长度不足时触发错误。 - 控制器重复实例化:在
build方法中使用Get.put(HomeScreenController())会重复创建控制器实例,可能导致状态异常。 - 空数据场景未处理:父组件未判断产品列表是否为空,
GridView.builder在空列表时仍可能尝试构建item。
解决方案
方案1:添加数据有效性校验(快速修复)
在ProductCardList的build方法开头增加校验,非法状态返回占位Widget:
@override Widget build(BuildContext context) { // 新增:校验数据和索引合法性 if (widget.dataModel == null || widget.dataModel.isEmpty || widget.index < 0 || widget.index >= widget.dataModel.length) { return const SizedBox.shrink(); // 或自定义加载/空占位UI } // 替换Get.put为Get.find,避免重复创建控制器 final homeController = Get.find<HomeScreenController>(); // ... 原有组件代码 }
方案2:优化父组件空数据处理
在GridView.builder外层判断列表是否为空,展示对应空状态:
Expanded( child: (campaignDetailsModel?.data?.products?.isEmpty ?? true) ? Center(child: Text("暂无产品")) // 自定义空状态UI : GridView.builder( padding: EdgeInsets.symmetric(horizontal: isMobile(context)? 15.w:10.w, vertical: 8.h), itemCount: campaignDetailsModel!.data!.products!.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: isMobile(context)? 2:3, childAspectRatio: .75, mainAxisSpacing: isMobile(context)? 15:20, crossAxisSpacing: isMobile(context)? 15:20, ), itemBuilder: (context, index) { return ProductCardList( dataModel: campaignDetailsModel!.data!.products!, index: index, ); }, ), ),
方案3:重构组件(更优雅的写法)
将ProductCardList改为接收单个产品模型,彻底避免索引操作:
// 修改ProductCardList参数 class ProductCardList extends StatefulWidget { const ProductCardList({ Key? key, required this.product, // 改为单个产品模型 }) : super(key: key); final dynamic product; @override State<ProductCardList> createState() => _ProductCardListState(); } // 组件内部直接访问widget.product class _ProductCardListState extends State<ProductCardList> { final currencyConverterController = Get.find<CurrencyConverterController>(); @override Widget build(BuildContext context) { final homeController = Get.find<HomeScreenController>(); return Ribbon( farLength: widget.product.isNew! ? isMobile(context) ? 20 : 30 : 1, nearLength: widget.product.isNew! ? isMobile(context) ? 40 : 50 : 1, title: widget.product.isNew! ? AppTags.neW.tr : "", // ... 所有原widget.dataModel[widget.index]替换为widget.product ); } } // 父组件调用时传入单个产品 itemBuilder: (context, index) { final product = campaignDetailsModel!.data!.products![index]; return ProductCardList(product: product); },
内容的提问来源于stack exchange,提问作者Noman Hassan
相关产品推荐
相关产品推荐

