Flutter中GoRouter打开键盘时跳转至路由栈顶部问题排查
问题分析与解决方案
问题根源
- 重复创建GoRouter实例:每次
Consumer重建(如键盘弹出触发Widget树重绘)时,都会调用router(authService)生成新的GoRouter实例,而新实例会使用initialLocation重置路由,导致跳转到首页/登录页。 - AuthService不必要的notifyListeners:初始化时异步调用
_loadFromPrefs会触发notifyListeners,进而触发Consumer重建和GoRouter重新创建,引发路由跳转。
解决方案
1. 缓存GoRouter实例,避免重复创建
不要在Consumer的builder中每次都生成新的GoRouter,而是缓存实例,仅在认证状态变化时更新:
方法一:使用Memoizer(依赖provider包)
return Consumer<AuthService>( builder: (context, authService, child) { // 仅当认证状态变化时重新创建GoRouter final router = Memoizer().memoize( () => GoRouter.router(authService), [authService.isAuthenticated], ); return MaterialApp.router( routerConfig: router, ); }, );
方法二:用StatefulWidget保存实例
将MyApp改为StatefulWidget,在状态中缓存GoRouter并监听认证状态变化:
class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { late GoRouter _router; @override void didChangeDependencies() { super.didChangeDependencies(); final authService = Provider.of<AuthService>(context, listen: false); _router = GoRouter.router(authService); // 监听认证状态变化,更新路由 authService.addListener(() { setState(() { _router = GoRouter.router(authService); }); }); } @override Widget build(BuildContext context) { return MaterialApp.router(routerConfig: _router); } }
2. 修复AuthService初始化逻辑
避免初始化时不必要的notifyListeners,确保仅当认证状态实际变化时才通知:
class AuthService extends ChangeNotifier { static final AuthService _instance = AuthService._internal(); factory AuthService() => _instance; AuthService._internal() { _initAuthStatus(); } bool _isAuthenticated = false; bool get isAuthenticated => _isAuthenticated; Future<void> _initAuthStatus() async { // 异步读取本地缓存的认证状态 final savedStatus = await CorePrefs.isAuthenticatedAsync(); if (_isAuthenticated != savedStatus) { _isAuthenticated = savedStatus; notifyListeners(); } } Future<void> setAuthenticated(bool value) async { if (_isAuthenticated != value) { _isAuthenticated = value; await CorePrefs.isAuthenticated(value: value); notifyListeners(); } } }
如果CorePrefs只能同步读取,修改_loadFromPrefs:
void _loadFromPrefs() { final savedStatus = CorePrefs.isAuthenticated(); if (_isAuthenticated != savedStatus) { _isAuthenticated = savedStatus; notifyListeners(); } }
3. 优化GoRouter的redirect逻辑
确保仅在必要时跳转,避免重复触发路由变更:
redirect: (context, state) { final loggedIn = authService.isAuthenticated; final currentPath = state.subloc; // 未登录且不在登录/注册/重置密码页面,跳转登录页 if (!loggedIn) { final authPaths = [RouteNames.login, RouteNames.register, RouteNames.passwordReset]; if (!authPaths.contains(currentPath)) return RouteNames.login; } // 已登录但在登录页,跳转首页 else if (currentPath == RouteNames.login) { return RouteNames.dashboard; } return null; },
4. 统一AuthService实例获取方式
移除MyApp中手动创建的AuthService实例,统一通过Provider获取:
// 移除这行代码:final AuthService authService = AuthService(); // 在需要的地方通过Provider获取 final authService = Provider.of<AuthService>(context);
内容的提问来源于stack exchange,提问作者learnflutter
相关产品推荐
相关产品推荐

