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
相关产品推荐
相关产品推荐

