Riverpod仅认证场景可用的Provider登出后被重新实例化问题
问题根因
- 核心问题出在
onlyIfAuthenticatedProvider内部对isAuthenticated的监听方式:你使用了ref.watch(isAuthenticated),这会让Riverpod将isAuthenticated标记为该Provider的依赖,只要isAuthenticated状态发生变化,无论当前Provider是否即将被销毁,都会触发该Provider的重新实例化逻辑。 - 登出时的执行时序导致异常抛出:
- 点击登出修改
isAuthenticated为false - 状态变更首先通知所有依赖
isAuthenticated的Provider,此时AuthenticatedPage还未完成卸载,onlyIfAuthenticatedProvider仍处于被监听状态,因此触发重新执行创建逻辑 - 创建逻辑中判断登录状态为false,直接抛出异常
- 之后才会执行UI层的页面切换,
AuthenticatedPage卸载后onlyIfAuthenticatedProvider才会被销毁
- 点击登出修改
- 额外说明:你当前没有给Provider加
autoDispose修饰符,默认状态下Provider即使失去订阅者也会在内存中保留,后续依赖发生变化时依然会触发重建逻辑,进一步提升了异常出现的概率。
修复方案
- 替换
ref.watch为ref.read:你创建Provider时只需要获取当前的登录状态,不需要监听后续变化,直接修改对应代码即可:
- 替换
final _isAuthenticated = ref.read(isAuthenticated); // 把原有的watch改成read
改动后Provider只会在首次创建时读取一次登录状态,不会再因为isAuthenticated后续变化触发重建。
- 给Provider添加
autoDispose修饰符:
- 给Provider添加
final onlyIfAuthenticatedProvider = StateNotifierProvider.autoDispose<OnlyIfAuthenticatedStateNotifier, List<int>>((ref) { // 原有创建逻辑保持不变 });
加了autoDispose之后,只要AuthenticatedPage卸载、Provider失去所有订阅者,就会被立即销毁,不会再响应后续的依赖变化。
- 若确实需要在Provider中监听登录状态做清理操作,使用
ref.listen而非ref.watch:
- 若确实需要在Provider中监听登录状态做清理操作,使用
// 在Provider创建逻辑中添加如下代码即可 ref.listen(isAuthenticated, (previous, current) { if (!current.state) { // 执行登出后的清理操作,比如清空本地缓存数据 } });
ref.listen只会监听状态变化执行副作用,不会触发Provider自身的重建。
内容的提问来源于stack exchange,提问作者Fabrizio Tognetto
相关产品推荐
相关产品推荐

