AkshayT
AkshayT

Reputation: 3021

Flutter: Bottom Navigation bar with nested navigation and Restoring the Root page when tabs are changed

I am new to Flutter development. And I have been going through multiple tutorials to understand the Bottom Navigation bar.

I have tried these tutorials but I am not able to achieve the requirement that I have. Tutorials I have followed:

  1. https://codewithandrea.com/articles/multiple-navigators-bottom-navigation-bar/
  2. https://medium.com/flutter/getting-to-the-bottom-of-navigation-in-flutter-b3e440b9386
  3. https://medium.com/@theboringdeveloper/common-bottom-navigation-bar-flutter-e3693305d2d

I personally liked the 1st tutorial because there are nested routes.

Information:

I have bottom navigation with 3 tabs: Home, Calendar, Profile.

Home tabs has a screen: Screen2. Calendar has a screen: Screen3. Profile has a screen: Screen4

Problem:

My bottom navigation bar persisting the state of the screen(which is good).

Home screen has a button which opens Screen2. When user clicks, it pushes the Screen2. And when user clicks on Calendar (tab) user sees Calendar screen. Now, user again clicks on Home button(tab), user sees the Screen2. Because It was part of that route(Home). Where he should see Home screen only.

And I just want to reset it. Basically Home screen should push Home screen and pop all the children of Home page. When tabs are switched.

Code:

main.dart

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MainScreen(),
    );
  }
}

main_screen.dart

class MainScreen extends StatefulWidget {
  MainScreen({Key key}) : super(key: key);

  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  int _selectedIndex = 0;

  List<GlobalKey<NavigatorState>> _navigatorKeys = [
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>()
  ];

  List<Widget> _widgetOptions = <Widget>[
    HomePage(),
    CalendarPage(),
    ProfilePage(),
  ];

  Map<String, WidgetBuilder> _routeBuilders(BuildContext context, int index) {
    return {
      '/': (context) {
        return [
          HomePage(),
          CalendarPage(),
          ProfilePage(),
        ].elementAt(index);
      },
    };
  }


  Widget _buildOffstageNavigator(int index) {
    var routeBuilders = _routeBuilders(context, index);

    return Offstage(
      offstage: _selectedIndex != index,
      child: Navigator(
        key: _navigatorKeys[index],
        onGenerateRoute: (routeSettings) {
          return MaterialPageRoute(
            builder: (context) => routeBuilders[routeSettings.name](context),
          ); 
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        final isFirstRouteInCurrentTab =
        !await _navigatorKeys[_selectedIndex].currentState.maybePop();

        // let system handle back button if we're on the first route
        return isFirstRouteInCurrentTab;
      },
      child: Scaffold(
        backgroundColor: Colors.white,
        body: SafeArea(
          child: Stack(
            children: [
              _buildOffstageNavigator(0),
              _buildOffstageNavigator(1),
              _buildOffstageNavigator(2),
            ],
          ),
        ),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: _selectedIndex,
          showSelectedLabels: false,
          showUnselectedLabels: false,
          items: [
            BottomNavigationBarItem(
              icon: Icon(
                Feather.home,
                color: Colors.grey[300],
              ),
              label: 'HOME',
              activeIcon: Icon(
                Feather.home,
                color: Colors.purple[300],
              ),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                FontAwesome.calendar,
                color: Colors.grey[300],
              ),
              label: 'CALENDAR',
              activeIcon: Icon(
                FontAwesome.calendar,
                color: Colors.purple[300],
              ),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                EvilIcons.user,
                color: Colors.grey[300],
                size: 36,
              ),
              label: 'PROFILE',
              activeIcon: Icon(
                EvilIcons.user,
                color: Colors.purple[300],
                size: 36,
              ),
            ),
          ],
          onTap: (index) {
            setState(() {
              _selectedIndex = index;
            });
          },
        ),
      ),
    );
  }
}

home_page.dart

class HomePage extends StatefulWidget {
  HomePage();

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.lightBlueAccent,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Container(
              child: Text(
                'Screen 1',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
              margin: EdgeInsets.all(16),
            ),
            FlatButton(
              onPressed: () {
                // Navigator.push(context, MaterialPageRoute(
                //   builder: (context) => Screen2()
                // ));

                Navigator.push(context, PageRouteBuilder(pageBuilder: (_,__,___) => Screen2()));
              },
              child: Text('Go to next screen'),
              color: Colors.white,
            ),
          ],
        ));
  }
}

calendar_page.dart

class CalendarPage extends StatefulWidget {
  CalendarPage({Key key}) : super(key: key);

  @override
  _CalendarPageState createState() => _CalendarPageState();
}

class _CalendarPageState extends State<CalendarPage> {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.red,
      child: Center(
        child: FlatButton(
          onPressed: (){
            Navigator.push(context, MaterialPageRoute(
                builder: (context) => Screen3()
            ));
          },
          child: Text('Go to next screen'),
          color: Colors.white,
        ),
      ),
    );
  }
}

I would really appreciate if anyone could point me to direction. Thanks in Advance.

Requirement: There are tabs, each tab will respective screens(ScreenX-> ScreenY-> ScreenN). And when tab is switched it should pop all the children of the tabs. I hope this understandable(Sorry, my English is not good).

What am I missing here?

Upvotes: 3

Views: 5227

Answers (1)

AkshayT
AkshayT

Reputation: 3021

So the logic is if I move away from Home screen(tab) to any other tab. I should clear the stack(not referring to the widget).

Assuming you are following 1st tutorial.

There are 3 tabs. Home, Calendar and Profile.

Now from Home screen I added "Screen2". So, right now my currentTab is "Home". If I click Calendar tab my selectedTtab is "Calendar".

I will pop everything from my current tab until the first route is met. Once this is done I will set the state.

Code:


void _selectTab(TabItem tabItem) {
    if (tabItem == _currentTab) {
      _navigatorKeys[tabItem]?.currentState?.popUntil((route) => route.isFirst);
    } else {
      //! Added logic to Pop everything from Home tab, if any other tab is clicked
      if (_currentTab == TabItem.HOME) {
        _navigatorKeys[_currentTab]
            ?.currentState
            ?.popUntil((route) => route.isFirst);
      }
      setState(() => _currentTab = tabItem);
    }
  }

And I am calling this method from Bottom navigation.

bottomNavigationBar: BottomNavigation(
          currentTab: _currentTab,
          onSelectTab: _selectTab,
        ),

Hope this helps. Let me know if this is sufficient.

Upvotes: 1

Related Questions