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页面初始化和销毁时,通过自定义的状态通知机制,告诉父组件隐藏/恢复底部导航栏:
- 先修改
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; } }
- 再修改
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
相关产品推荐
相关产品推荐

