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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 07:52:05