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

Riverpod中带参数调用ref.read()始终返回加载状态的解决方法

解决带参数Provider在ExpansionTile展开时始终返回Loading的问题

以下是几种可行的解决方案,核心思路是在展开时触发数据加载,再通过监听Provider状态更新UI:

1. 用StateProvider跟踪已加载ID,避免重复请求

先创建一个StateProvider记录已触发加载的请假单ID:

final loadedLeaveDetailIdsProvider = StateProvider<Set<String>>((ref) => {});

然后在ExpansionTile的展开回调中触发加载,并在子组件中监听状态:

ExpansionTile(
  onExpansionChanged: (expanded) {
    final id = value.items[index].id;
    final loadedIds = ref.read(loadedLeaveDetailIdsProvider);
    if (expanded && !loadedIds.contains(id)) {
      // 触发数据加载(调用future会让Provider开始执行异步逻辑)
      ref.read(leaveDetailsProvider(id).future);
      // 标记该ID已加载,防止重复请求
      ref.read(loadedLeaveDetailIdsProvider.notifier).state = {...loadedIds, id};
    }
  },
  title: Text(value.items[index].title),
  children: [
    Consumer(
      builder: (context, ref, child) {
        // 监听对应ID的Provider状态,数据更新时自动刷新UI
        final detailState = ref.watch(leaveDetailsProvider(value.items[index].id));
        return detailState.when(
          loading: () => const Padding(
            padding: EdgeInsets.all(16),
            child: CircularProgressIndicator(),
          ),
          error: (err, _) => Padding(
            padding: const EdgeInsets.all(16),
            child: Text('加载失败:$err'),
          ),
          data: (detail) => Padding(
            padding: const EdgeInsets.all(16),
            child: Text(detail.content),
          ),
        );
      },
    ),
  ],
)

2. 确保FamilyProvider逻辑正确处理缓存

如果你的leaveDetailsProvider是用FutureProvider.family创建的,默认会缓存请求结果,只要触发过一次加载,后续监听会直接返回数据。确保Provider定义正确:

final leaveDetailsProvider = FutureProvider.family<LeaveDetail, String>((ref, id) async {
  // 可以添加缓存时长,比如5分钟内复用结果
  ref.cacheFor(const Duration(minutes: 5));
  final apiService = ref.watch(apiServiceProvider);
  return apiService.getLeaveDetail(id);
});

3. 配合AutoDispose释放资源(可选)

如果不需要长期缓存展开过的请假单数据,可以用autoDispose版本的FamilyProvider,收起时自动释放资源:

final leaveDetailsProvider = FutureProvider.autoDispose.family<LeaveDetail, String>((ref, id) async {
  final apiService = ref.watch(apiServiceProvider);
  return apiService.getLeaveDetail(id);
});

然后在展开回调中添加收起时的清理逻辑:

onExpansionChanged: (expanded) {
  final id = value.items[index].id;
  if (expanded) {
    ref.read(leaveDetailsProvider(id).future);
  } else {
    // 收起时手动失效Provider,释放资源
    ref.invalidate(leaveDetailsProvider(id));
  }
},

关键注意点

  • 不要在build方法中用ref.read获取带参数Provider的状态,read不会监听状态变化,UI无法感知数据加载完成
  • 必须通过ref.watch在子组件中监听状态,才能在数据就绪后自动更新UI
  • 用StateProvider跟踪已加载ID是避免重复请求的高效方式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 08:43:17