Riverpod+Freezed下合并FutureProviders到StateNotifierProvider重复实例问题
重复实例问题根因
你在StateNotifierProvider的创建回调中直接使用ref.watch监听两个FutureProvider,任意一个FutureProvider状态更新时,都会触发创建回调重新执行,生成新的ClockingNotifier实例,因此init方法会被多次调用。
修复方案
不要在创建回调中直接传入watch的结果,改为将ref传入ClockingNotifier内部,使用ref.listen监听两个FutureProvider的状态变化,listen仅会触发回调逻辑,不会导致StateNotifierProvider重建生成新实例。
完整实现代码
import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; final clockingState = StateNotifierProvider<ClockingNotifier, ClockingState>( (ref) => ClockingNotifier(ref) ); class ClockingNotifier extends StateNotifier<ClockingState> { final Ref ref; Timer? _countdownTimer; // 标记DPS数据是否已加载完成 bool _dpsLoaded = false; // 缓存加载完成的DPS数据 ClockingState? _cachedDpsState; ClockingNotifier(this.ref) : super(ClockingState.init()) { init(); } void init() { logger.d("the init method"); // 监听行程加载Provider ref.listen<AsyncValue<ClockingState>>(loadingItineraryProvider, (previous, next) { next.when( loading: () => state = ClockingState.loading(), error: (e, s) => state = ClockingState.error(e), data: (clockingState) { logger.d("clocking state"); // DPS未加载完成时才展示行程状态 if (!_dpsLoaded) { state = clockingState; } }, ); }); // 监听DPS数据Provider ref.listen<AsyncValue<ClockingState>>(clockingDps, (previous, next) { next.when( loading: () {}, // 加载中不修改状态,保持行程展示 error: (e, s) => state = ClockingState.error(e), data: (dpsState) { logger.d("DPS state"); _cachedDpsState = dpsState; _dpsLoaded = true; // 启动10秒倒计时 _countdownTimer?.cancel(); _countdownTimer = Timer(const Duration(seconds: 10), () { state = _cachedDpsState!; }); }, ); }); } @override void dispose() { _countdownTimer?.cancel(); super.dispose(); } }
逻辑说明
- Notifier全程仅初始化一次,
init方法只会执行一次,不会出现重复实例问题 - 行程数据加载完成后直接渲染,作为加载页展示
- DPS数据加载完成后缓存数据,启动10秒倒计时,倒计时结束后再切换为DPS状态
- 两个Provider的错误状态都会同步到页面,也可以根据业务需求自定义错误处理逻辑
- 组件销毁时自动取消定时器,避免内存泄漏
内容的提问来源于stack exchange,提问作者aidanmack
相关产品推荐
相关产品推荐

