You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 01:00:59