Riverpod中FutureProvider获数据却无法更新UI问题求助
Riverpod FutureProvider数据已返回但UI持续加载的问题排查
我是Riverpod新手,目前有两个几乎完全相同的FutureProvider,其中一个API已成功返回数据(Provider内打印result.length为预期的10),但UI始终停留在加载状态,且未执行data分支的打印逻辑。相关代码如下:
UI代码
class ProductDetailSimilarProductsWidget extends ConsumerWidget { const ProductDetailSimilarProductsWidget(this.product, {super.key}); final ProductDetailModel product; @override Widget build(BuildContext context, WidgetRef ref) { var value = ref.watch(getSimilarProductsFutureProvider([product.Id, product.KategoriId])); return Container( height: MediaQuery.of(context).size.height * 0.5, width: MediaQuery.of(context).size.width, child: value.when( data: (data) { print("data length:${data.length}"); return ListView.builder( itemCount: data.length, shrinkWrap: true, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return Padding( padding: EdgeInsets.only(left: AppTheme.mediumPadding, right: AppTheme.mediumPadding), child: ProductGridContainerWidget(product: data[index]), ); }, ); }, error: (err, trace) { return Text(err.toString()); }, loading: () => AppTheme.spinkit, ), ); } }
FutureProvider代码
final getSimilarProductsFutureProvider = FutureProvider.family<List<Products>, List<dynamic>>((ref, value) async { var result = await ProductDetailData().getSimilarProducts(productId: value[0], categoryId: value[1]); print("result: ${result.length}"); return result; });
API请求方法代码
var service = ApiService(); var sharedPreferencesManager = SharedPreferencesManager(); await sharedPreferencesManager.init(); var langId = await sharedPreferencesManager.getLanguageId(); var countryId = await sharedPreferencesManager.getCountryId(); var result = await service.getRequest(subUrl: "${ApiRoutes.getSimilarProducts}?productId=$productId&langId=$langId&countryId=$countryId&categoryId=$categoryId"); List<Products> products = []; var elements = json.decode(result.body); await Future.forEach(elements, (element) => products.add(Products.fromMap(element as Map<String, dynamic>))); return products;
问题排查与解决
1. 核心问题:family参数的不稳定性
你使用List<dynamic>作为family的参数类型,但List是可变对象。每次Widget重建时,[product.Id, product.KategoriId]都会创建一个新的List实例,导致FutureProvider判定参数发生变化,不断重新发起请求,陷入"加载-请求-重建-重新加载"的循环,永远无法进入data分支。
解决方法:使用不可变参数类型
方案一:使用Tuple类型(需导入
package:tuple/tuple.dart)import 'package:tuple/tuple.dart'; // 修改Provider定义 final getSimilarProductsFutureProvider = FutureProvider.family<List<Products>, Tuple2<int, int>>((ref, params) async { var result = await ProductDetailData().getSimilarProducts(productId: params.item1, categoryId: params.item2); print("result: ${result.length}"); return result; }); // UI中调用 var value = ref.watch(getSimilarProductsFutureProvider(Tuple2(product.Id, product.KategoriId)));方案二:自定义不可变参数类
// 定义不可变参数类,重写==和hashCode class SimilarProductsParams { final int productId; final int categoryId; SimilarProductsParams({required this.productId, required this.categoryId}); @override bool operator ==(Object other) => identical(this, other) || other is SimilarProductsParams && runtimeType == other.runtimeType && productId == other.productId && categoryId == other.categoryId; @override int get hashCode => productId.hashCode ^ categoryId.hashCode; } // 修改Provider定义 final getSimilarProductsFutureProvider = FutureProvider.family<List<Products>, SimilarProductsParams>((ref, params) async { var result = await ProductDetailData().getSimilarProducts(productId: params.productId, categoryId: params.categoryId); print("result: ${result.length}"); return result; }); // UI中调用 var value = ref.watch(getSimilarProductsFutureProvider(SimilarProductsParams(productId: product.Id, categoryId: product.KategoriId)));
2. 辅助排查:捕获未处理的解析异常
虽然API返回了数据,但Products.fromMap可能在解析JSON时抛出异常,FutureProvider会内部捕获异常,但当前error分支未打印日志,无法发现问题。
可以在error分支添加日志,并在API请求方法中增加try-catch:
// UI的error分支 error: (err, trace) { print("加载相似商品出错: $err"); print(trace); return Text(err.toString()); }, // API请求方法添加异常捕获 try { var elements = json.decode(result.body); await Future.forEach(elements, (element) => products.add(Products.fromMap(element as Map<String, dynamic>))); } catch (e, trace) { print("JSON解析出错: $e"); print(trace); rethrow; }
3. 额外检查:避免父Widget频繁重建
如果父Widget因状态变化频繁重建,也会加剧重复请求问题,但解决参数稳定性后,该情况会自然改善。
内容的提问来源于stack exchange,提问作者Zahid Tekbaş
相关产品推荐
相关产品推荐

