Riverpod状态变更时触发GoRouter redirect方法的正确实现
问题:GoRouter与Riverpod集成后,Auth状态变更时redirect不触发
问题场景与代码
我在Flutter应用中用Riverpod的Auth Provider管理登录状态:
@riverpod class Auth extends _$Auth { @override bool build() { return false; } void login() { state = true; } void logout() { state = false; } }
同时将GoRouter封装在Riverpod Provider中,通过ref.watch(authProvider)获取登录状态,在redirect方法中做权限校验:
@riverpod GoRouter router(ref) { final isLoggedIn = ref.watch(authProvider); return GoRouter( routes: [ GoRoute( path: "/", builder: (context, state) => const HomeScreen(), ), GoRoute( path: "/login", builder: (context, state) => LoginScreen(), ), ], redirect: (context, state) { final bool wantsToLogin = state.matchedLocation == "/login"; if (wantsToLogin && isLoggedIn) { return "/"; } if (!wantsToLogin && !isLoggedIn) { return "/login"; } return null; }, ); }
目前遇到的问题:ConsumerWidget中监听authProvider能正常触发组件重建,但Auth状态变更时,GoRouter的redirect方法不会自动执行。
原因分析
- 一次性值捕获:当前代码中
isLoggedIn是创建GoRouter实例时捕获的静态值,后续Auth状态变化会触发routerProvider重建生成新的GoRouter,但现有导航栈中的GoRouter实例不会自动重新执行redirect逻辑。 - GoRouter的redirect触发逻辑:redirect方法默认仅在初始化导航或用户主动触发新导航操作时执行,不会主动监听外部状态变化来触发重定向。
正确集成方案
方案一:用refreshListenable绑定Auth状态监听
通过ValueNotifier创建可监听对象,绑定到GoRouter的refreshListenable参数,Auth状态变化时自动通知GoRouter重新校验redirect:
@riverpod GoRouter router(ref) { // 创建可监听对象,同步Auth状态 final refreshNotifier = ValueNotifier(ref.watch(authProvider)); // 监听Auth状态变化,更新Notifier ref.listen<Auth>(authProvider, (_, next) { refreshNotifier.value = next; }); return GoRouter( refreshListenable: refreshNotifier, routes: [ GoRoute( path: "/", builder: (context, state) => const HomeScreen(), ), GoRoute( path: "/login", builder: (context, state) => LoginScreen(), ), ], redirect: (context, state) { // 直接读取最新Auth状态 final isLoggedIn = ref.read(authProvider).state; final wantsToLogin = state.matchedLocation == "/login"; if (wantsToLogin && isLoggedIn) return "/"; if (!wantsToLogin && !isLoggedIn) return "/login"; return null; }, ); }
方案二:监听Auth状态主动刷新GoRouter
在routerProvider中监听Auth状态变化,主动调用GoRouter的refresh()方法触发redirect重校验:
@riverpod GoRouter router(ref) { final router = GoRouter( routes: [ GoRoute( path: "/", builder: (context, state) => const HomeScreen(), ), GoRoute( path: "/login", builder: (context, state) => LoginScreen(), ), ], redirect: (context, state) { final isLoggedIn = ref.read(authProvider).state; final wantsToLogin = state.matchedLocation == "/login"; if (wantsToLogin && isLoggedIn) return "/"; if (!wantsToLogin && !isLoggedIn) return "/login"; return null; }, ); // Auth状态变化时主动刷新GoRouter ref.listen<Auth>(authProvider, (_, __) { router.refresh(); }); return router; }
注意:redirect方法中使用
ref.read()而非ref.watch(),因为redirect不属于组件build生命周期,避免不必要的监听绑定。
内容的提问来源于stack exchange,提问作者Leonardo Mantovani
相关产品推荐
相关产品推荐

