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

Riverpod构建方法中嵌套watch调用的正确方式

在Riverpod中嵌套watch调用的正确姿势

你的代码里在position.when的data回调中使用ref.watch是不符合Riverpod规范的,会导致状态监听失效、UI不更新甚至内存泄漏问题。下面是正确的处理方式:

核心问题原因

Riverpod要求所有ref.watch调用必须放在build方法的顶层(或Consumer的builder回调顶层),不能嵌套在诸如when这类异步状态回调里。因为这类回调只会在特定时机执行一次,框架无法跟踪到内部的watch依赖,当子provider状态变化时,无法触发组件重建。


方案1:拆分为独立的ConsumerWidget

把依赖位置数据的医院列表逻辑抽成单独组件,让每个组件的ref.watch都处于build顶层,是最推荐的做法,能保证重建范围精准、代码职责清晰。

// 独立的医院列表组件
class NearbyHospitalsWidget extends ConsumerWidget {
  final Position position;

  const NearbyHospitalsWidget({super.key, required this.position});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 这里的watch处于build顶层,合规
    final hospitalsAsync = ref.watch(getNearbyHospitalsUseCaseProvider(position));
    
    return hospitalsAsync.when(
      data: (hospitals) => Column(
        // 渲染医院列表的UI
        children: hospitals.map((hospital) => Text(hospital.name)).toList(),
      ),
      loading: () => const CircularProgressIndicator(),
      error: (err, stack) => Text('加载医院失败: $err'),
    );
  }
}

// 原父组件
@override
Widget build(BuildContext context, WidgetRef ref) {
  final positionAsync = ref.watch(positionProvider);

  return positionAsync.when(
    data: (position) => NearbyHospitalsWidget(position: position),
    loading: () => const CircularProgressIndicator(),
    error: (err, stack) => Text('获取位置失败: $err'),
  );
}

方案2:用Consumer包裹嵌套逻辑

如果不想拆分组件,可以用Consumer把需要监听医院provider的部分包裹起来,在Consumer的builder回调中执行ref.watch(该回调属于组件重建周期的顶层)。

@override
Widget build(BuildContext context, WidgetRef ref) {
  final positionAsync = ref.watch(positionProvider);

  return positionAsync.when(
    data: (position) => Consumer(
      builder: (context, ref, child) {
        // 在Consumer的builder顶层执行watch,合规
        final hospitalsAsync = ref.watch(getNearbyHospitalsUseCaseProvider(position));
        
        return hospitalsAsync.when(
          data: (hospitals) => Column(
            // 渲染医院列表UI
            children: hospitals.map((hospital) => Text(hospital.name)).toList(),
          ),
          loading: () => const CircularProgressIndicator(),
          error: (err, stack) => Text('加载医院失败: $err'),
        );
      },
    ),
    loading: () => const CircularProgressIndicator(),
    error: (err, stack) => Text('获取位置失败: $err'),
  );
}

这种方式下,当医院provider状态变化时,只会重建Consumer内部的部分,不会触发整个父组件的重建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 03:05:06