Flutter中API调用产生数据变更后如何刷新当前页面UI
问题根因
你的购物车操作后UI不刷新是三个代码逻辑错误导致的:
updateCart方法内调用fetchCartDetailsList()是异步网络请求,但你没有等待请求返回最新数据就直接执行setState,此时拿到的还是修改前的旧缓存数据- 你将
updateCart方法定义在build方法内部,且所有通过Provider获取数据的位置都设置了listen: false,页面无法感知Provider内的数据变化 - 删除操作的执行时序混乱:点击确认删除按钮后,删除请求刚发起就直接关闭了确认弹窗,没有等待接口返回成功结果就执行后续逻辑,容易出现状态不同步
- 额外隐患:
itemCount直接取cartDetailsList.elementAt(0).cartgrid!.length没有做空判断,购物车为空时会直接抛出数组越界错误
修复方案
- 将
updateCart、数据获取逻辑移到build方法外,作为状态类的成员方法,所有异步请求前加await等待执行完成再刷新UI - 调整删除操作的执行顺序:等待删除接口返回成功结果后,再弹出操作成功提示、拉取最新购物车数据
- 页面初始化外的其他位置获取Provider数据时,不要强制设置
listen: false,确保数据变更能触发页面重绘 - 补充空安全判断,避免购物车为空时页面崩溃
修正后核心代码
class CartPage extends StatefulWidget{ @override _CartPageState createState()=>_CartPageState(); } class _CartPageState extends State<CartPage>{ TextEditingController dateinput = TextEditingController(); bool isLoading=true; String imageurl="http://192.168.10.8/DigicatDemo/Upload/Images/1/"; late CartVM cartVM; List<CartdetailsModel> cartDetailsList = []; // 拉取购物车数据方法移到类成员位置,等待请求完成再更新状态 Future<void> fetchData()async{ setState(() { isLoading = true; }); await Provider.of<CartDetailsVM>(context,listen: false).fetchCartDetailsList(); if(mounted){ setState((){ cartDetailsList = Provider.of<CartDetailsVM>(context,listen: false).getCartDetailsList(); isLoading=false; }); } } // 更新购物车方法移到类成员位置,等待数据拉取完成再刷新 Future<void> updateCart()async{ await Provider.of<CartDetailsVM>(context,listen: false).fetchCartDetailsList(); if(mounted){ setState(() { cartDetailsList = Provider.of<CartDetailsVM>(context,listen: false).getCartDetailsList(); }); } } @override void initState() { super.initState(); cartVM = CartVM(context); fetchData(); } @override Widget build(BuildContext context) { return isLoading? const Center(child: CircularProgressIndicator()) : Scaffold( body: Column( children: [ const SizedBox(height: 5.0,), Expanded( child:Padding( padding: const EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 10.0), child:SingleChildScrollView( child: GridView.builder( physics: const ScrollPhysics(), shrinkWrap: true, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 5, childAspectRatio: 0.75, crossAxisSpacing: 10, mainAxisSpacing: 10, ), // 补充空判断,避免购物车为空时崩溃 itemCount: cartDetailsList.isEmpty ? 0 : cartDetailsList[0].cartgrid?.length ?? 0, itemBuilder: (BuildContext ctx, index) { final currentProduct = cartDetailsList[0].cartgrid![index]; return GestureDetector( onTap: (){}, child: Material( elevation: 9, shadowColor: Colors.red, child: Container( alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), ), child: Center( child: Column( children: [ IconButton(icon: const Icon(FontAwesome.trash),onPressed: (){ showDialog( context: context, barrierDismissible: false, builder: (BuildContext dialogContext){ return AlertDialog( title: const Text("Are you sure you want to delete this product"), actions: [ ElevatedButton(onPressed: () async { // 等待删除接口返回结果 await cartVM.deleteCartProduct(product_code: currentProduct.ProductCode); if(mounted){ // 先关闭确认删除弹窗 Navigator.of(dialogContext).pop(); // 弹成功提示 showDialog(context: context, builder: (BuildContext successContext){ return AlertDialog( title: const Text("Success"), content: const Text('Successfully deleted from the cart.'), actions: [ ElevatedButton(onPressed: () async { // 刷新购物车数据 await updateCart(); if(mounted){ Navigator.of(successContext).pop(); } }, child: const Text("Close")), ], ); }); } }, child: const Text("Yes")), ElevatedButton(onPressed: (){ Navigator.of(dialogContext).pop(); }, child: const Text("No")), ], ); }); }), ], ), ), ), ), ); }), ) ) ), ], ), ); } }
注意:代码中所有
context操作前都加了mounted判断,避免异步请求返回时页面已经销毁导致的报错。如果你的CartDetailsVM是用ChangeNotifier实现的,在fetchCartDetailsList方法获取到最新服务端数据后,记得调用notifyListeners()通知所有监听位置更新。
内容的提问来源于stack exchange,提问作者Ajith S B
相关产品推荐
相关产品推荐

