使用StreamBuilder作为GoRouter导航栈根节点的路由栈问题
解决FirebaseAuth状态变化后路由栈残留AuthPage的问题
问题描述
通过StreamBuilder监听FirebaseAuth的认证状态切换HomePage与LandingPage,但用户登录后,从LandingPage推入栈的AuthPage仍停留在路由栈顶部,导致StreamBuilder下方已构建HomePage,但返回操作会回到AuthPage,不符合预期。
现有代码
AuthGate 代码
class AuthGate extends StatelessWidget { const AuthGate({super.key}); @override Widget build(BuildContext context) { return StreamBuilder<User?>( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { return snapshot.hasData ? const HomePage() : const LandingPage(); }, ); } }
LandingPage 跳转代码
class LandingPage extends StatelessWidget { const LandingPage({super.key}); // 省略其他代码... Row( children: <Widget>[ FloatingActionButton.extended( heroTag: UniqueKey(), onPressed: () { context.push('/auth'); }, label: const Text('Get started'), ), FloatingActionButton.extended( heroTag: UniqueKey(), onPressed: () { context.push('/auth'); }, label: const Text('Log in'), ), ], ) // 省略其他代码... }
解决方案
方案1:监听认证状态并主动清理路由栈(无路由框架)
将AuthGate改为StatefulWidget,在初始化时监听认证状态流,当状态变化时通过pushAndRemoveUntil清空路由栈,确保当前页面为唯一根页面:
class AuthGate extends StatefulWidget { const AuthGate({super.key}); @override State<AuthGate> createState() => _AuthGateState(); } class _AuthGateState extends State<AuthGate> { late StreamSubscription<User?> _authSubscription; @override void initState() { super.initState(); // 监听认证状态变化 _authSubscription = FirebaseAuth.instance.authStateChanges().listen((user) { if (user != null) { // 登录成功:清空路由栈并跳转HomePage Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => const HomePage()), (route) => false, // 移除所有之前的路由 ); } else { // 登出:清空路由栈并跳转LandingPage Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute(builder: (_) => const LandingPage()), (route) => false, ); } }); } @override void dispose() { _authSubscription.cancel(); // 取消监听避免内存泄漏 super.dispose(); } @override Widget build(BuildContext context) { // 初始加载状态 return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } }
方案2:使用GoRouter处理认证路由重定向(推荐,若使用GoRouter)
利用GoRouter的redirect和refreshListenable特性,自动根据认证状态重定向路由并清理栈:
final GoRouter _router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const LandingPage(), ), GoRoute( path: '/auth', builder: (context, state) => const AuthPage(), ), GoRoute( path: '/home', builder: (context, state) => const HomePage(), ), ], // 路由重定向逻辑 redirect: (context, state) { final currentUser = FirebaseAuth.instance.currentUser; final isLoggedIn = currentUser != null; final targetLocation = state.location; // 未登录且不在登录/landing页面,重定向到landing if (!isLoggedIn && targetLocation != '/' && targetLocation != '/auth') { return '/'; } // 已登录且在登录/landing页面,重定向到home if (isLoggedIn && (targetLocation == '/' || targetLocation == '/auth')) { return '/home'; } return null; // 无需重定向 }, // 监听认证状态变化,触发路由刷新 refreshListenable: GoRouterRefreshStream(FirebaseAuth.instance.authStateChanges()), );
说明
- 方案1通过主动监听认证状态并清空路由栈,彻底解决之前页面残留的问题;
- 方案2借助GoRouter的原生能力,更优雅地处理认证路由逻辑,适合使用GoRouter的项目;
- 两种方案都能避免登录后
AuthPage留在路由栈中的问题,确保返回操作符合用户预期。
内容的提问来源于stack exchange,提问作者Owain
相关产品推荐
相关产品推荐

