3

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?

AkshayT
  • 2,901
  • 2
  • 28
  • 32

1 Answers1

1

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.

AkshayT
  • 2,901
  • 2
  • 28
  • 32
  • Sorry for responding slowly, was caught up in festivities. I will test this over the weekend and update you, thank you for sharing this solution. Hopefully it works :) – Himadhar H Sep 15 '21 at 06:39
  • Hey Akshay, I was finally able to get to code and check this. Your changes help in managing the state which is awesome. But my problem has a second part to it. How do I exit the persistent tab view approach? As in, from `menu` screen to `login` screen for example. – Himadhar H Nov 06 '21 at 14:43