Flutter GoRouter嵌套导航安卓返回键点击后响应异常问题
Flutter GoRouter嵌套导航安卓返回键异常问题解决
问题现象
- 用GoRouter实现嵌套导航后,正常导航功能正常,但点击安卓设备返回键时,底部导航栏包裹的嵌套导航出现加载缓慢或无法点击的情况
- 触发时伴随日志:
W/WindowOnBackDispatcher( 9978): sendCancelIfRunning: isInProgress=falsecallback=io.flutter.embedding.android.FlutterActivity$1@2a3415f D/EGL_emulation( 9978): app_time_stats: avg=165.14ms min=5.89ms max=2133.05ms count=14
- 再次点击返回键会直接回到手机主屏幕
- 此前曾触发错误:
OnBackInvokedCallback is not enabled for the application. Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
已尝试的无效操作
- 在
AndroidManifest.xml的application层级添加android:enableOnBackInvokedCallback="true" - 更新Flutter及GoRouter版本
- 升级AndroidX Activity依赖至
implementation "androidx.activity:activity:1.6.0-alpha05"
路由配置代码
// GoRouter configuration static final GoRouter router = GoRouter( initialLocation: initial, debugLogDiagnostics: true, navigatorKey: _rootNavigatorKey, routes: [ GoRoute( path: '/', builder: (context, state) => WelcomeScreen(), routes: [ GoRoute( path: 'login', builder: (context, state) => LoginScreen(), routes: [ GoRoute( path: 'register', name: 'Register', builder: (context, state) => RegisterScreen()), GoRoute( path: 'forgotPassword', name: 'forgotPassword', builder: (context, state) => ForgotPasswordScreen(), routes: [ GoRoute( path: 'OTPVerification', name: 'OTPVerification', builder: (context, state) => OtpVerificationScreen(), routes: [ GoRoute( path: 'createNewPassword', name: 'createNewPassword', builder: (context, state) => CreateNewPasswordScreen(), routes: [ GoRoute( path: 'passwordChanged', name: 'passwordChanged', builder: (context, state) => PasswordChangedScreen()), ], ), ]), ]), ], ), ], ), /// MainWrapper StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { return CustomBottomBar( navigationShell: navigationShell, ); }, branches: <StatefulShellBranch>[ /// Branch Home StatefulShellBranch( navigatorKey: _shellNavigatorHome, routes: <RouteBase>[ GoRoute( path: "/home", name: "Home", builder: (BuildContext context, GoRouterState state) => HomePage(), routes: [ GoRoute( path: 'routesAndGamesList', name: 'routesAndGamesList', pageBuilder: (context, state) => CustomTransitionPage<void>( key: state.pageKey, child: RoutesAndGameList(), transitionsBuilder: (context, animation, secondaryAnimation, child) => FadeTransition(opacity: animation, child: child), ), ), GoRoute( path: 'routesList', name: 'routesList', pageBuilder: (context, state) => CustomTransitionPage<void>( key: state.pageKey, child: RoutesList(), transitionsBuilder: (context, animation, secondaryAnimation, child) => FadeTransition(opacity: animation, child: child), ), ), ], ), ], ), StatefulShellBranch( navigatorKey: _shellNavigatorInfo, routes: <RouteBase>[ GoRoute( path: "/info", name: "Info", builder: (BuildContext context, GoRouterState state) => InfoPage(), ), ], ), StatefulShellBranch( navigatorKey: _shellNavigatorProfile, routes: <RouteBase>[ GoRoute( path: "/profile", name: "Profile", builder: (BuildContext context, GoRouterState state) => ProfilePage(), routes: <RouteBase>[ GoRoute( path: 'achievements', name: 'Achievements', pageBuilder: (context, state) => CustomTransitionPage<void>( key: state.pageKey, child: AchievementsScreen(), transitionsBuilder: (context, animation, secondaryAnimation, child) => FadeTransition(opacity: animation, child: child), ), routes: [ GoRoute( path: 'trophy', name: 'Trophy', builder: (context, state) => TrophyScreen(), ), ], ), ], ), ], ), StatefulShellBranch( navigatorKey: _shellNavigatorStories, routes: <RouteBase>[ GoRoute( path: "/stories", name: "Stories", builder: (BuildContext context, GoRouterState state) => StoriesPage(), ), ], ), ], ), ], );
解决方案
1. 给GoRouter添加onPopPage回调处理嵌套栈返回
问题核心是安卓返回键触发时,GoRouter没有优先处理嵌套分支内的页面栈,直接触发了根导航的弹出。添加onPopPage可以让嵌套分支的导航栈优先处理返回:
static final GoRouter router = GoRouter( initialLocation: initial, debugLogDiagnostics: true, navigatorKey: _rootNavigatorKey, // 新增onPopPage回调 onPopPage: (route, result) { // 判断当前页面是否属于嵌套shell分支 final isShellRoute = route.settings.name?.startsWith('/home') == true || route.settings.name?.startsWith('/info') == true || route.settings.name?.startsWith('/profile') == true || route.settings.name?.startsWith('/stories') == true; if (isShellRoute) { // 获取对应分支的导航器 final navigator = switch (route.settings.name) { String name when name.startsWith('/home') => _shellNavigatorHome.currentState, String name when name.startsWith('/info') => _shellNavigatorInfo.currentState, String name when name.startsWith('/profile') => _shellNavigatorProfile.currentState, String name when name.startsWith('/stories') => _shellNavigatorStories.currentState, _ => null, }; // 如果分支导航器有可弹出的页面,优先弹出分支内页面 if (navigator != null && navigator.canPop()) { navigator.pop(); return false; // 阻止GoRouter处理根导航的pop } } // 非嵌套分支页面,正常处理返回 return route.didPop(result); }, routes: [ // 原路由配置... ], );
2. 替换AndroidX Activity依赖为稳定版
不要使用alpha测试版,改用最新稳定版依赖:
// 在app/build.gradle中替换 implementation "androidx.activity:activity:1.8.2"
3. 检查嵌套导航器Key初始化
确保所有StatefulShellBranch对应的导航器Key已正确初始化:
final GlobalKey<NavigatorState> _rootNavigatorKey = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> _shellNavigatorHome = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> _shellNavigatorInfo = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> _shellNavigatorProfile = GlobalKey<NavigatorState>(); final GlobalKey<NavigatorState> _shellNavigatorStories = GlobalKey<NavigatorState>();
原理说明
安卓的WindowOnBackDispatcher和GoRouter的嵌套导航栈未正确协同:点击返回键时,系统优先触发原生回调,跳过了GoRouter嵌套分支内的页面栈处理,直接触发根导航弹出,导致界面出现卡顿或交互失效。通过onPopPage回调可以强制GoRouter优先处理嵌套分支的返回逻辑,避免系统直接跳过嵌套栈。
内容的提问来源于stack exchange,提问作者curious_skeleton
相关产品推荐
相关产品推荐

