Flutter中Cannot use "ref" after widget disposed错误解决求助
问题解决:Riverpod 「Cannot use "ref" after the widget was disposed」错误修复
错误根源
执行nightContext.go('/day/$yesterday')后,当前页面的Widget已被销毁,但后续代码仍尝试通过已失效的ref修改loadingProvider状态,触发报错。同时GlobalLoading组件未在Widget销毁时清理OverlayEntry,存在潜在内存泄漏问题。
修复方案
1. 修正异步任务的状态更新逻辑
将状态重置操作移至导航前,确保在Widget销毁前完成状态修改,同时添加异常捕获避免加载框永久显示:
MyButton( title: 'go to day', onPressed: () async { ref.read(loadingProvider.notifier).state = true; try { final isar = await ref.read(isarServiceProvider.future); final yesterday = await isar.getDayNumber(); await isar.putGameStatus( dayNumber: yesterday, isDay: true, situation: MyStrings.dayPage, ); await ref.read(currentPlayersProvider.notifier).action(MyStrings.dayPage); // 先重置加载状态,再执行导航 ref.read(loadingProvider.notifier).state = false; nightContext.go('/day/$yesterday'); } catch (e) { // 异常时强制重置状态 ref.read(loadingProvider.notifier).state = false; debugPrint('任务执行失败: $e'); } }, )
2. 完善GlobalLoading的资源清理
在Widget销毁时自动清理所有OverlayEntry,同时显式设置监听的cancelOnDispose属性,避免内存泄漏:
class GlobalLoading extends HookConsumerWidget { const GlobalLoading({required this.child, Key? key}) : super(key: key); final Widget child; @override Widget build(BuildContext context, WidgetRef ref) { final _entries = useState<List<OverlayEntry?>>([]); // 监听加载状态,显式声明销毁时取消监听 ref.listen<bool>( loadingProvider, (previous, next) { if (previous == next) return; if (next) { final barrierEntry = OverlayEntry( builder: (_) => ModalBarrier(color: AppColors.backGround.withOpacity(0.85)), ); final loadingEntry = OverlayEntry( builder: (_) => Center( child: LoadingAnimationWidget.inkDrop( color: AppColors.secondaries[1], size: 160, ), ), ); _entries.value.addAll([barrierEntry, loadingEntry]); Overlay.of(context).insertAll(_entries.value.whereType<OverlayEntry>()); } else { _entries.value.forEach((entry) => entry?.remove()); _entries.value.clear(); } }, cancelOnDispose: true, ); // Widget销毁时强制清理OverlayEntry ref.onDispose(() { _entries.value.forEach((entry) => entry?.remove()); _entries.value.clear(); }); return child; } }
3. 可选:改用AsyncNotifier优化加载状态管理
如果想更优雅地处理异步任务的加载/成功/失败状态,可以替换StateProvider为AsyncNotifierProvider,自动管理状态生命周期:
// 定义AsyncNotifier class DayTransitionNotifier extends AsyncNotifier<void> { @override FutureOr<void> build() {} Future<void> transitionToDay() async { state = const AsyncLoading(); try { final isar = await ref.read(isarServiceProvider.future); final yesterday = await isar.getDayNumber(); await isar.putGameStatus( dayNumber: yesterday, isDay: true, situation: MyStrings.dayPage, ); await ref.read(currentPlayersProvider.notifier).action(MyStrings.dayPage); state = const AsyncData(null); ref.read(goRouterProvider).go('/day/$yesterday'); } catch (e, stackTrace) { state = AsyncError(e, stackTrace); // 这里可添加用户提示等异常处理逻辑 } } } // 声明Provider final dayTransitionProvider = AsyncNotifierProvider<DayTransitionNotifier, void>( DayTransitionNotifier.new, ); // UI中调用 MyButton( title: 'go to day', onPressed: () { ref.read(dayTransitionProvider.notifier).transitionToDay(); }, ) // GlobalLoading监听状态变化 ref.listen<AsyncValue<void>>( dayTransitionProvider, (previous, next) { if (next is AsyncLoading) { // 显示加载框逻辑 } else { // 隐藏加载框逻辑 } }, )
内容的提问来源于stack exchange,提问作者Behzad Ayubifar
相关产品推荐
相关产品推荐

