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

Flutter go_router:获取StatefulShellRoute真实当前路由以隐藏底部导航

解决StatefulShellRoute下隐藏特定子路由底部导航栏的问题

问题核心在于:StatefulShellRoute构建器里的GoRouterState对应的是shell路由本身的状态(比如/home或/categories),而非分支内嵌套的子路由(如/product/:productId),所以直接读取state.matchedLocation只能拿到分支的根路由。以下是三种可行的解决方式:


方案一:全局监听GoRouter路由变化

在承载底部导航栏的ScaffoldBottomNavigationBar中,监听GoRouter的全局路由变化,实时获取当前完整路由路径来判断是否隐藏导航栏:

class ScaffoldBottomNavigationBar extends StatefulWidget {
  final StatefulNavigationShell navigationShell;

  const ScaffoldBottomNavigationBar({super.key, required this.navigationShell});

  @override
  State<ScaffoldBottomNavigationBar> createState() => _ScaffoldBottomNavigationBarState();
}

class _ScaffoldBottomNavigationBarState extends State<ScaffoldBottomNavigationBar> {
  String _currentLocation = '';

  @override
  void initState() {
    super.initState();
    // 初始化当前路由并添加监听
    final router = GoRouter.of(context);
    _currentLocation = router.location;
    router.addListener(_onRouteChanged);
  }

  @override
  void dispose() {
    // 移除监听避免内存泄漏
    GoRouter.of(context).removeListener(_onRouteChanged);
    super.dispose();
  }

  void _onRouteChanged() {
    setState(() {
      _currentLocation = GoRouter.of(context).location;
    });
  }

  @override
  Widget build(BuildContext context) {
    // 判断当前是否是product路由
    final shouldHideNavBar = _currentLocation.startsWith('/product/');

    return Scaffold(
      body: widget.navigationShell,
      bottomNavigationBar: shouldHideNavBar ? null : BottomNavigationBar(
        currentIndex: widget.navigationShell.currentIndex,
        onTap: (index) {
          widget.navigationShell.goBranch(
            index,
            initialLocation: index == widget.navigationShell.currentIndex,
          );
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
        ],
      ),
    );
  }
}

方案二:子路由通过回调通知父组件控制导航栏

在ProductScreen页面初始化和销毁时,通过自定义的状态通知机制,告诉父组件隐藏/恢复底部导航栏:

  1. 先修改ScaffoldBottomNavigationBar,添加状态控制和传递回调的InheritedWidget:
class ScaffoldBottomNavigationBar extends StatefulWidget {
  final StatefulNavigationShell navigationShell;

  const ScaffoldBottomNavigationBar({super.key, required this.navigationShell});

  @override
  State<ScaffoldBottomNavigationBar> createState() => _ScaffoldBottomNavigationBarState();
}

class _ScaffoldBottomNavigationBarState extends State<ScaffoldBottomNavigationBar> {
  bool _hideNavBar = false;

  void setNavBarVisibility(bool visible) {
    setState(() {
      _hideNavBar = !visible;
    });
  }

  @override
  Widget build(BuildContext context) {
    return _NavBarVisibilityProvider(
      setVisibility: setNavBarVisibility,
      child: Scaffold(
        body: widget.navigationShell,
        bottomNavigationBar: _hideNavBar ? null : BottomNavigationBar(
          currentIndex: widget.navigationShell.currentIndex,
          onTap: (index) {
            widget.navigationShell.goBranch(
              index,
              initialLocation: index == widget.navigationShell.currentIndex,
            );
          },
          items: const [
            BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
            BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          ],
        ),
      ),
    );
  }
}

// 自定义InheritedWidget传递回调
class _NavBarVisibilityProvider extends InheritedWidget {
  final Function(bool) setVisibility;

  const _NavBarVisibilityProvider({
    super.key,
    required this.setVisibility,
    required super.child,
  });

  static _NavBarVisibilityProvider of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<_NavBarVisibilityProvider>()!;
  }

  @override
  bool updateShouldNotify(_NavBarVisibilityProvider oldWidget) {
    return oldWidget.setVisibility != setVisibility;
  }
}
  1. 再修改ProductScreen,在页面生命周期中触发回调:
class ProductScreen extends StatefulWidget {
  final String productId;

  const ProductScreen({super.key, required this.productId});

  @override
  State<ProductScreen> createState() => _ProductScreenState();
}

class _ProductScreenState extends State<ProductScreen> {
  @override
  void initState() {
    super.initState();
    // 进入页面时隐藏导航栏
    _NavBarVisibilityProvider.of(context).setVisibility(false);
  }

  @override
  void dispose() {
    // 离开页面时恢复导航栏
    _NavBarVisibilityProvider.of(context).setVisibility(true);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('商品 ${widget.productId}')),
      body: const Center(child: Text('商品详情页')),
    );
  }
}

方案三:通过StatefulNavigationShell获取当前分支活跃路由

利用navigationShell.getCurrentRoute()方法,直接获取当前分支内的活跃路由,通过路由名称判断是否隐藏导航栏:

class ScaffoldBottomNavigationBar extends StatelessWidget {
  final StatefulNavigationShell navigationShell;

  const ScaffoldBottomNavigationBar({super.key, required this.navigationShell});

  @override
  Widget build(BuildContext context) {
    // 获取当前分支的活跃路由
    final currentRoute = navigationShell.getCurrentRoute();
    // 通过路由名称判断是否需要隐藏导航栏
    final shouldHideNavBar = currentRoute.name == 'product';

    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: shouldHideNavBar ? null : BottomNavigationBar(
        currentIndex: navigationShell.currentIndex,
        onTap: (index) {
          navigationShell.goBranch(
            index,
            initialLocation: index == navigationShell.currentIndex,
          );
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
        ],
      ),
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:40:09