Riverpod触发重建后读取未初始化Provider状态报错的解决方法
Riverpod Notifier重建时保留选中状态的报错解决
问题背景
我用Riverpod实现了一个Notifier,期望数据更新后触发重建,同时不丢失之前选中对象的引用。代码如下:
@Riverpod(keepAlive:true) Future<List<MyModel>> modelList (ModelListRef ref) { return ref.watch(modelRepositoryProvider).getModels(); } @Riverpod(keepAlive:true) class MyController extends _$MyController { @override MyModel? build() { return ref.watch(modelListProvider).maybeWhen( // 此处访问state会触发错误 ↓ data: (models) => models.singleWhereOrNull((model) => model.id == state?.id), orElse: () => null, ); } @override set state (MyModel value){ state = value; } }
修改数据后调用ref.invalidate(modelListProvider)触发重建,读取Notifier状态时抛出错误:
StateError (Bad state: Tried to read the state of an uninitialized provider)
堆栈信息指向Notifier的state属性。
问题根源
- 初始化阶段访问未就绪的state:
build方法是用来初始化Notifier状态的,此时Notifier还未完成初始化,直接访问state会触发未初始化错误。 - 无限递归的setter:自定义的
set state方法中直接赋值state = value,会无限递归调用自身,导致状态无法正确更新。
解决方案
通过单独存储选中对象的ID,避免在build阶段依赖未初始化的state,同时修复setter的递归问题:
@Riverpod(keepAlive:true) Future<List<MyModel>> modelList (ModelListRef ref) { return ref.watch(modelRepositoryProvider).getModels(); } @Riverpod(keepAlive:true) class MyController extends _$MyController { // 单独存储选中模型的ID,脱离对state的依赖 String? _selectedModelId; @override MyModel? build() { return ref.watch(modelListProvider).maybeWhen( data: (models) { // 用存储的ID匹配新列表中的模型 if (_selectedModelId == null) return null; return models.singleWhereOrNull((model) => model.id == _selectedModelId); }, orElse: () => null, ); } // 修复setter的递归问题,调用父类的state setter @override set state (MyModel? value){ _selectedModelId = value?.id; super.state = value; } }
说明
- 新增
_selectedModelId变量存储选中对象的ID,build方法通过这个ID从更新后的模型列表中匹配对应对象,既实现了状态同步,又避免了初始化阶段访问state的问题。 - 自定义
statesetter时,先更新_selectedModelId,再调用super.state = value,既保留了选中状态,又解决了无限递归的问题。 - 当调用
ref.invalidate(modelListProvider)后,modelListProvider重新获取数据,MyController会自动用存储的ID匹配新模型,完成状态更新,同时保留选中对象的关联。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

