flutterdartflutter-go-router

How to hide Tabbar created with StatefulShellRoute of go_router in flutter?


I am making a screen where there is a Tabbar for two different screen. Lets say BarA and BarB.

Now, in the BarA Tab, There is a Details page, where I want to push, do some stuff and back to BarA tab.In the details page, I don't want to show the Tabbar, instead I want to show Back button. But I can't achieve it! The Tabbar is always there, which I dont want!

Bellow is what I am getting ==>
what I am getting!

But actually what I want from the tab bar: what I actually want!

My code snippet:

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,
                    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: () => context.push("/tabOne/details"),
              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(HomeScreen.path),
          child: const Text('Go back to the Home screen'),
        ),
      ),
    );
  }
}

Solution

  • You need to add parentNavigatorKey: rootNavigatorKey to GoRoute.

    Code:

        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, // here
                        builder: (context, state) => const DetailsScreen(),
                      )
                    ]),
              ],
            ),
            StatefulShellBranch(
              navigatorKey: trainTopTabNavigatorKey,
              routes: [
                GoRoute(
                  path: TabTwo.path,
                  name: TabTwo.name,
                  builder: (context, state) => const TabTwo(),
                ),
              ],
            ),
          ],
    

    parentNavigatorKey - An optional key specifying which Navigator to display this route's screen onto.