为何我的Riverpod中AsyncValue Provider一直处于加载状态?
问题:Riverpod在BottomSheetModal中持续加载无法返回Firestore数据
我正在学习Riverpod,尝试在二维码扫描触发的BottomSheetModal中使用它。扫描完成后想从Firestore获取数据并在模态框展示,但AsyncValue.when始终处于加载状态,无法返回数据。用FutureBuilder可以正常工作,但希望用Riverpod实现。
相关代码
Scanned_Shipment_Modal.dart 部分代码
showModalBottomSheet( context: context, backgroundColor: Colors.transparent, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)), isScrollControlled: true, builder: (context) { return FutureBuilder( future: Future.value(capture), builder: (context, snapshot) { if (snapshot.hasData) { var dateNow = DateTime.now(); var shipmentoutdataref = ref.watch(getShipmentOutDataProvider( shipmentTrack: barcode.barcodes.first.rawValue!, dateNow: dateNow, )); return shipmentoutdataref.when( data: (data) { debugPrint(data.toString()); return Wrap( children: [ ScannedShipmentOutModal(shipmentoutdataref: data) ], ); }, error: (error, stackTrace) => ErrorPopup().errorPopup( context, "Error", "No Da"), loading: () { return const LoadingCircle( height: 200, width: 200, ); }); } else { return ErrorPopup() .errorPopup(context, "Error", "No Data Avaliable"); } }); });
scanned_shipment_out_provider.dart 代码
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:intl/intl.dart'; import 'package:logistec/models/Firebase/model/firestore_data_models.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'scanned_shipment_out_provider.g.dart'; @riverpod Future<FirestoreShipmentOutData?> getShipmentOutData(GetShipmentOutDataRef ref, {required String shipmentTrack, required DateTime dateNow}) async { FirebaseFirestore firestore = FirebaseFirestore.instance; String getformattedDate = DateFormat("dd-MM-yy").format(dateNow); var dataref = firestore.collection("DHL_Service_RecheckData").doc(getformattedDate); var data = await dataref.get(); try { var dataList = data.get(shipmentTrack); return FirestoreShipmentOutData.fromJSON(dataList); } catch (e) { return null; } }
问题原因分析
- AutoDispose Provider 被提前销毁:生成代码显示这是
AutoDisposeFutureProvider,BottomSheet的builder会多次重建,每次重建时DateTime.now()生成新时间对象,导致Provider参数变化、创建新实例,旧实例被销毁,新实例重新请求数据,陷入循环加载。 - FutureBuilder与Riverpod混用冲突:在FutureBuilder的builder中调用
ref.watch,builder重建时会重复执行ref.watch,结合AutoDispose特性,导致Provider无法稳定完成请求。 - 异常处理隐藏问题:Provider中try-catch直接返回null,Riverpod会将null视为正常数据;若Firestore请求或数据解析出现隐藏异常,会导致状态无法正确更新,表现为持续加载。
修正方案
方案1:稳定Provider参数并调整AutoDispose特性
步骤1:固定dateNow参数
扫描完成后立即获取固定时间,避免在builder中重复生成:
// 扫描完成后固定时间 final fixedDate = DateTime.now(); showModalBottomSheet( context: context, // 其他参数不变 builder: (context) { return Consumer(builder: (context, ref, _) { var shipmentoutdataref = ref.watch(getShipmentOutDataProvider( shipmentTrack: barcode.barcodes.first.rawValue!, dateNow: fixedDate, )); return shipmentoutdataref.when( data: (data) { if (data == null) { return ErrorPopup().errorPopup(context, "Error", "No Data Available"); } debugPrint(data.toString()); return Wrap( children: [ScannedShipmentOutModal(shipmentoutdataref: data)], ); }, error: (error, stackTrace) => ErrorPopup().errorPopup( context, "Error", error.toString()), loading: () => const LoadingCircle(height: 200, width: 200), ); }); }, );
步骤2:设置Provider保持存活(可选)
如果不需要自动销毁Provider,添加keepAlive: true并重新运行build_runner:
@riverpod(keepAlive: true) Future<FirestoreShipmentOutData?> getShipmentOutData(...) async { // 原有代码不变 }
方案2:优化Provider异常处理
捕获异常时抛出错误,让Riverpod的when方法进入error分支,便于排查问题:
@riverpod Future<FirestoreShipmentOutData?> getShipmentOutData(GetShipmentOutDataRef ref, {required String shipmentTrack, required DateTime dateNow}) async { FirebaseFirestore firestore = FirebaseFirestore.instance; String getformattedDate = DateFormat("dd-MM-yy").format(dateNow); var dataref = firestore.collection("DHL_Service_RecheckData").doc(getformattedDate); var data = await dataref.get(); if (!data.exists) { throw Exception("Document not found for date: $getformattedDate"); } try { var dataList = data.get(shipmentTrack); return FirestoreShipmentOutData.fromJSON(dataList); } catch (e) { throw Exception("Shipment track $shipmentTrack not found: ${e.toString()}"); } }
方案3:移除FutureBuilder,直接使用Consumer
已有扫描完成的capture数据,无需用FutureBuilder包裹,直接在BottomSheet中用Consumer监听Provider:
showModalBottomSheet( context: context, backgroundColor: Colors.transparent, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)), isScrollControlled: true, builder: (context) { final fixedDate = DateTime.now(); return Consumer(builder: (context, ref, _) { var shipmentoutdataref = ref.watch(getShipmentOutDataProvider( shipmentTrack: barcode.barcodes.first.rawValue!, dateNow: fixedDate, )); return shipmentoutdataref.when( data: (data) { return data != null ? Wrap(children: [ScannedShipmentOutModal(shipmentoutdataref: data)]) : ErrorPopup().errorPopup(context, "Error", "No Data Available"); }, error: (error, stackTrace) => ErrorPopup().errorPopup(context, "Error", error.toString()), loading: () => const LoadingCircle(height: 200, width: 200), ); }); }, );
内容的提问来源于stack exchange,提问作者Jirayuth Porka
相关产品推荐
相关产品推荐

