You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Riverpod AsyncValue持续加载并无限发起API请求问题求助

问题:Riverpod AsyncValue卡在加载状态且无限发起API请求

我正在使用带代码生成(code gen)的Riverpod框架,遇到了一个久未解决的问题:AsyncValue始终卡在加载状态,且代码会无限发起API请求。我是参照Riverpod仓库中的pub app示例进行开发的。

相关代码

Consumer Widget的build方法

@override
Widget build(BuildContext context, WidgetRef ref) {
final postingBalances = ref.watch(
  fetchPostingBalancesProvider(
    merchantId: hiveStorage.readString(key: StorageKey.merchantId)!,
    selectedDates: DateTimeRange(
      start: DateTime.now().subtract(
        const Duration(days: 5),
      ),
      end: DateTime.now(),
    ),
  ),
);

return postingBalances.when(
  data: (data) {
    postingBalancesList = data!.postingBalances!.items!;

    return Scaffold(
      appBar: AppBar(
        title: Text(tr.reports, style: TextStyles.title4),
        centerTitle: true,
      ),
      body: ListTile(
        title: Text(postingBalancesList.first.id),
        subtitle: Text(postingBalancesList.first.totalAmount.toString()),
      ),
    );
  },
  error: (err, stack) => Text('Error $err'),
  loading: () => const CircularProgressIndicator(),
);
}

fetchPostingBalancesProvider代码

@riverpod
Future<PostingBalancesResponse?> fetchPostingBalances(
FetchPostingBalancesRef ref, {
required String merchantId,
required DateTimeRange selectedDates,
}) async {
final cancelToken = ref.cancelToken();
return ref.watch(reportsRepositoryImplProvider).getPostingBalances(merchantId, 
selectedDates, cancelToken);
}

reportsRepositoryImplProvider代码

@riverpod
ReportsRepositoryImpl reportsRepositoryImpl(ReportsRepositoryImplRef ref) => 
ReportsRepositoryImpl(
  dioState: ref.read(dioProvider), // a constant defined elsewhere
);

API调用本身能正常执行,但组件始终卡在加载状态,请问该如何解决这个问题?


解决方案

1. 消除build方法中的不稳定参数

每次组件build时,你都会创建一个新的DateTimeRange实例。Riverpod会将这个对象作为provider的依赖,由于每次实例引用不同,会不断触发provider重新执行,导致无限API请求和加载状态循环。

解决方式:用StateProvider管理日期范围,确保参数实例稳定:

// 先定义一个StateProvider来管理日期范围
@riverpod
DateTimeRange selectedDates(SelectedDatesRef ref) {
  return DateTimeRange(
    start: DateTime.now().subtract(const Duration(days: 5)),
    end: DateTime.now(),
  );
}

// 修改build方法
@override
Widget build(BuildContext context, WidgetRef ref) {
  final merchantId = hiveStorage.readString(key: StorageKey.merchantId)!;
  // 从StateProvider中读取稳定的日期范围实例
  final selectedDates = ref.watch(selectedDatesProvider);
  final postingBalances = ref.watch(
    fetchPostingBalancesProvider(
      merchantId: merchantId,
      selectedDates: selectedDates,
    ),
  );

  return postingBalances.when(
    data: (data) {
      // 用局部变量替代外部变量,避免不必要的状态变化
      final postingBalancesList = data!.postingBalances!.items!;
      return Scaffold(
        appBar: AppBar(
          title: Text(tr.reports, style: TextStyles.title4),
          centerTitle: true,
        ),
        body: ListTile(
          title: Text(postingBalancesList.first.id),
          subtitle: Text(postingBalancesList.first.totalAmount.toString()),
        ),
      );
    },
    error: (err, stack) => Text('Error $err'),
    loading: () => const CircularProgressIndicator(),
  );
}

2. 改用值类型参数替代引用类型

DateTimeRange是引用类型,默认的相等性判断是比较实例引用而非内容。如果不想用StateProvider,可以将参数拆分为单独的DateTime(值类型,相等性按时间值判断):

修改fetchPostingBalancesProvider:

@riverpod
Future<PostingBalancesResponse?> fetchPostingBalances(
  FetchPostingBalancesRef ref, {
  required String merchantId,
  required DateTime startDate,
  required DateTime endDate,
}) async {
  final cancelToken = ref.cancelToken();
  return ref.watch(reportsRepositoryImplProvider).getPostingBalances(
    merchantId,
    DateTimeRange(start: startDate, end: endDate),
    cancelToken,
  );
}

修改build方法中的参数传递:

@override
Widget build(BuildContext context, WidgetRef ref) {
  final merchantId = hiveStorage.readString(key: StorageKey.merchantId)!;
  // 定义稳定的日期值,DateTime是值类型,相同时间会被判定为相等
  final startDate = DateTime.now().subtract(const Duration(days: 5));
  final endDate = DateTime.now();
  final postingBalances = ref.watch(
    fetchPostingBalancesProvider(
      merchantId: merchantId,
      startDate: startDate,
      endDate: endDate,
    ),
  );

  // 后续when逻辑保持不变...
}

3. 避免在data分支修改外部变量

原代码中postingBalancesList = data!...修改了外部变量,可能触发组件不必要的重新build,加剧循环问题。改用局部变量存储数据即可。


内容的提问来源于stack exchange,提问作者Yigit Kaya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 04:53:19