Flutter中如何隐藏go_router的StatefulShellRoute创建的TabBar
Flutter中使用GoRouter实现Tab页跳转详情页隐藏TabBar
我正在开发一个带有两个Tab(BarA和BarB)的Flutter界面。在BarA Tab中,跳转到详情页后,希望隐藏TabBar并显示返回按钮,完成操作后返回BarA Tab,但目前TabBar始终显示,无法实现需求。
当前效果:
期望效果:
问题原因
现在的DetailsScreen是作为TabOne的子路由嵌套在StatefulShellBranch的分支导航器里,导致它始终处于TabBar所在的Scaffold容器层级内,所以TabBar会一直显示。
解决方案
让DetailsScreen使用根导航器打开,脱离TabBar的容器层级,具体修改如下:
1. 修改DetailsScreen的路由配置
在DetailsScreen的GoRoute中添加parentNavigatorKey: rootNavigatorKey,指定使用根导航器:
GoRoute( path: DetailsScreen.path, name: DetailsScreen.name, parentNavigatorKey: rootNavigatorKey, // 新增此行,使用根导航器 builder: (context, state) => const DetailsScreen(), )
2. 调整详情页的返回逻辑
把DetailsScreen的返回按钮改为跳回TabOne页面,确保返回后回到原Tab:
ElevatedButton( onPressed: () => context.go(TabOne.name), // 改为返回TabOne child: const Text('返回TabOne'), )
3. 优化HomeScreen的直接跳转逻辑(可选)
如果要从HomeScreen直接跳转到详情页,需要先进入TabOne再跳转,确保分支导航器已初始化:
ElevatedButton( onPressed: () async { await context.go(TabOne.name); context.pushNamed(DetailsScreen.name); }, child: const Text('To Details screen'), )
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; final rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'RootNavigator'); final homeNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'HomeNavigator'); final topNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'TopTabNavigator'); final carTopTabNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'CarTopTabNavigation'); final trainTopTabNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'TrainTopTabNavigation'); void main() => runApp(const MyApp()); /// The route configuration. final GoRouter _router = GoRouter( debugLogDiagnostics: true, navigatorKey: rootNavigatorKey, initialLocation: HomeScreen.path, routes: [ GoRoute( path: HomeScreen.path, name: HomeScreen.name, builder: (context, state) => const HomeScreen(), ), StatefulShellRoute( builder: (context, state, navigationShell) => navigationShell, navigatorContainerBuilder: (context, navigationShell, children) => ScaffoldWithTabBarView( navigationShell: navigationShell, children: children, ), branches: [ StatefulShellBranch( navigatorKey: carTopTabNavigatorKey, routes: [ GoRoute( path: TabOne.path, name: TabOne.name, builder: (context, state) => const TabOne(), routes: [ GoRoute( path: DetailsScreen.path, name: DetailsScreen.name, parentNavigatorKey: rootNavigatorKey, // 新增:使用根导航器 builder: (context, state) => const DetailsScreen(), ) ]), ], ), StatefulShellBranch( navigatorKey: trainTopTabNavigatorKey, routes: [ GoRoute( path: TabTwo.path, name: TabTwo.name, builder: (context, state) => const TabTwo(), ), ], ), ], ), ], ); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( routerConfig: _router, ); } } /// The home screen class HomeScreen extends StatelessWidget { static String get path => "/home"; static String get name => "/home"; /// Constructs a [HomeScreen] const HomeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Home Screen')), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Center( child: ElevatedButton( onPressed: () => context.go(TabOne.name), child: const Text('Go to the Tab One screen'), ), ), Center( child: ElevatedButton( onPressed: () async { await context.go(TabOne.name); context.pushNamed(DetailsScreen.name); }, child: const Text('To Details screen'), ), ), ], ), ); } } class ScaffoldWithTabBarView extends StatelessWidget { const ScaffoldWithTabBarView({ super.key, required this.navigationShell, required this.children, }); final StatefulNavigationShell navigationShell; final List<Widget> children; @override Widget build(BuildContext context) { return DefaultTabController( length: 2, initialIndex: navigationShell.currentIndex, child: Builder( builder: (context) { final tabController = DefaultTabController.of(context); tabController.addListener(() { if (tabController.indexIsChanging) { navigationShell.goBranch( tabController.index, initialLocation: tabController.index == navigationShell.currentIndex, ); } print("*****"); }); return Scaffold( appBar: AppBar( bottom: const TabBar( tabs: [ Tab(icon: Icon(Icons.directions_car)), Tab(icon: Icon(Icons.directions_transit)), ], ), backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('Top tab page'), ), body: TabBarView(children: children), ); }, ), ); } } class TabOne extends StatelessWidget { static String get path => "/tabOne"; static String get name => "/tabOne"; /// Constructs a [TabOne] const TabOne({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Tab One Screen')), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Center( child: ElevatedButton( onPressed: () => context.go(HomeScreen.path), child: const Text('Go back to the Home screen'), ), ), Center( child: ElevatedButton( onPressed: () => context.pushNamed(DetailsScreen.name), child: const Text('Go back to the details screen'), ), ), ], ), ); } } class TabTwo extends StatelessWidget { static String get path => "/tabTwo"; static String get name => "/tabTwo"; /// Constructs a [TabTwo] const TabTwo({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Tab Two Screen')), body: Center( child: ElevatedButton( onPressed: () => context.go(HomeScreen.path), child: const Text('Go back to the Home screen'), ), ), ); } } class DetailsScreen extends StatelessWidget { static String get path => "details"; static String get name => "details"; const DetailsScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Details Screen')), body: Center( child: ElevatedButton( onPressed: () => context.go(TabOne.name), // 修改:返回TabOne child: const Text('返回TabOne'), ), ), ); } }
效果说明
修改后,从TabOne跳转到详情页时,TabBar会被隐藏,详情页单独显示带返回按钮的AppBar;点击返回按钮后,会回到TabOne页面,TabBar重新显示,符合需求。
内容的提问来源于stack exchange,提问作者xahid_rocks
相关产品推荐
相关产品推荐

