使用Go Router与Bloc实现认证:认证通过后无法跳转首页
问题分析与解决方案
核心问题
- 多Bloc实例冲突:你在GetIt注册了AuthBloc单例,但LoginScreen里又通过BlocProvider创建了新的AuthBloc实例,导致登录后的状态更新只在新实例中,AuthStream监听的单例实例状态未变化。
- GoRouter未监听状态变化:GoRouter的redirect逻辑默认只在路由导航时执行,未配置
refreshListenable,所以认证状态变化时不会重新触发redirect。 - redirect逻辑错误:根路由的redirect无论认证状态如何都返回
/login,未处理已认证的情况。
修复步骤
1. 统一AuthBloc实例
删除LoginScreen中BlocProvider的create方法,改用BlocProvider.value复用GetIt中的单例:
GoRoute( path: "login", builder: (context, state) { return BlocProvider.value( value: locator.get<AuthBloc>(), child: const LoginScreen(), ); }, )
同时在main函数中提前初始化AuthBloc并触发启动事件:
void main() async { WidgetsFlutterBinding.ensureInitialized(); Setup(); // 初始化单例AuthBloc并触发启动事件 final authBloc = locator.get<AuthBloc>(); authBloc.add(AuthStarted()); HydratedBloc.storage = await HydratedStorage.build( storageDirectory: kIsWeb ? HydratedStorage.webStorageDirectory : await getTemporaryDirectory(), ); runApp(const TedtrisApp()); }
2. 配置GoRouter的refreshListenable
让GoRouter监听AuthStream的变化,状态更新时自动重新执行redirect:
final _router = GoRouter( // 添加refreshListenable,监听认证状态变化 refreshListenable: AuthStream(locator.get<AuthBloc>().stream), routes: [ GoRoute( path: "/", redirect: (context, state) { final authStatus = AuthStreamScope.of(context).isSignedIn(); // 根据状态跳转:已认证去首页,未认证去登录页 return authStatus == AuthStatus.authenticated ? "/home" : "/login"; }, routes: [ // login路由(已修改为复用单例) ]), GoRoute( path: "/home", builder: (context, state) { return const Scaffold(body: HomeScreen()); }, ), ], );
3. 优化AuthStream(可选,避免重复依赖GetIt)
修改AuthStream直接持有AuthBloc引用,减少依赖耦合:
class AuthStream extends ChangeNotifier { late final StreamSubscription<dynamic> _subscription; final AuthBloc _authBloc; AuthStream(this._authBloc) { _subscription = _authBloc.stream.listen((_) => notifyListeners()); } AuthStatus isSignedIn() => _authBloc.state.authStatus; @override void dispose() { _subscription.cancel(); super.dispose(); } } class AuthStreamScope extends InheritedNotifier<AuthStream> { AuthStreamScope({super.key, required super.child, required AuthBloc authBloc}) : super(notifier: AuthStream(authBloc)); static AuthStream of(BuildContext ctx) = ctx.dependOnInheritedWidgetOfExactType<AuthStreamScope>()!.notifier!; }
然后在TedtrisApp中传入AuthBloc:
class _TedtrisAppState extends State<TedtrisApp> { final AuthBloc _authBloc = locator.get<AuthBloc>(); @override Widget build(BuildContext context) { return AuthStreamScope( authBloc: _authBloc, child: MaterialApp.router( debugShowCheckedModeBanner: false, routerConfig: _router, theme: lightTheme, ), ); } }
内容的提问来源于stack exchange,提问作者Chan May Lan
相关产品推荐
相关产品推荐

