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

Flutter go_router 6.0.1:ShellRoute下返回键触发空安全错误

解决ShellRoute实现底部导航时返回键触发的空安全错误

这种空安全错误本质是GoRouter在处理返回操作时,ShellRoute的内部路由状态出现了空值访问,而WillPopScope的拦截时机晚于GoRouter内部的路由状态检查,所以无法阻止错误触发。以下是几种可行的解决思路:

1. 自定义导航拦截逻辑,提前处理子路由返回

通过navigatorBuilder在GoRouter的根层级添加拦截,优先处理ShellRoute子路由栈的返回,避免GoRouter直接访问空状态:

final GoRouter router = GoRouter(
  navigatorBuilder: (context, state, child) {
    return WillPopScope(
      onWillPop: () async {
        // 获取当前ShellRoute的状态
        final shellState = ShellRouteState.of(context);
        if (shellState?.shellContext != null) {
          final subNavigator = Navigator.of(shellState!.shellContext!);
          // 如果子路由栈可以返回,优先处理子栈pop
          if (subNavigator.canPop()) {
            subNavigator.pop();
            return false; // 阻止GoRouter执行默认的全局pop逻辑
          }
        }
        // 子栈无法返回时,再处理全局路由的pop
        return router.canPop();
      },
      child: child,
    );
  },
  routes: [
    ShellRoute(
      builder: (context, state, child) => BottomNavigationBar(
        currentIndex: selectedIndex,
        onTap: (idx) {
          // 切换标签页使用go方法,避免栈堆积
          idx == 0 ? router.go('/') : router.go('/settings');
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
        ],
      ),
      routes: [
        GoRoute(path: '/', builder: (_, __) => const HomePage()),
        GoRoute(path: '/settings', builder: (_, __) => const SettingsPage()),
      ],
    ),
  ],
);

2. 重写GoRouterDelegate的popRoute方法

如果需要更底层的控制,可以自定义GoRouterDelegate,在popRoute方法中先检查路由状态的有效性,再执行pop操作:

class CustomRouterDelegate extends GoRouterDelegate {
  CustomRouterDelegate({required super.configuration});

  @override
  Future<bool> popRoute() async {
    final context = navigatorKey.currentContext;
    if (context == null) return false;

    // 检查ShellRoute的子导航栈
    final shellState = ShellRouteState.of(context);
    if (shellState?.shellContext != null) {
      final subNavigator = Navigator.of(shellState!.shellContext!);
      if (subNavigator.canPop()) {
        subNavigator.pop();
        return Future.value(true);
      }
    }

    // 全局路由栈可pop时,执行默认逻辑
    return canPop() ? super.popRoute() : Future.value(false);
  }
}

然后在初始化GoRouter时使用自定义Delegate:

final GoRouter router = GoRouter(
  routerDelegate: CustomRouterDelegate(
    configuration: GoRouterConfiguration(routes: [...]),
  ),
  // 其他配置项
);

3. 规范路由切换逻辑

切换底部导航标签时,使用go方法而非push,避免子路由栈堆积导致返回时出现异常的空状态。确保每个标签页对应的路由是单例栈,而非多层栈。

内容的提问来源于stack exchange,提问作者Zelkreps

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 00:20:28