Flutter: Persist BottomNavigationBar and route user to the correct screen depending on the selected index of the BottomNavigationBar

Issue

I want the BottomNavigationBar to persist and the user to be sent to a different screen depending on which index that they have selected and that index highlighted on the BottomNavigationBar.

The way I’m attempting to do this is to change the body value of the Scaffold according to the currently selected index of the BottomNavigationBar(). I am able to highlight the selected index of the BottomNavigationBar() once it is selected but the body is not routing to the appropriate widget.

The current method referenced for onTap: in the BottomNavBar correctly highlights the selected index but does not route to the correct widget.

There is an unused method (_switchScreen) in my BottomNavBar. Inserting this as the value to onTap: for the BottomNavBar() appropriately routes to the correct screen but then I have to have the BottomNavBar rebuilt on every view and is inaccessible behind the BottomNavBar which is referenced in the NavModel (Navigation Model).

Here is my BottomNavBar():

import 'package:flutter/material.dart';

import '../screens/Add_Media_Screen.dart';
import '../screens/Profile_Screen.dart';
import '../screens/Venture_Feed_Screen.dart';
   
class BottomNavBar extends StatefulWidget {
  @override
  _BottomNavBarState createState() => _BottomNavBarState();
}

class _BottomNavBarState extends State<BottomNavBar> {
  int _selectedIndex = 0;

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  void _switchScreen(index) {
    switch (index) {
      case 0:
        Navigator.pushNamed(context, VentureFeedScreen.routeName);
        break;
      case 1:
        Navigator.pushNamed(context, ProfileScreen.routeName);
        break;
      case 2:
        Navigator.pushNamed(context, AddMediaScreen.routeName);
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Theme(
      data: ThemeData(
        primaryColor: Color(0xff84d1da),
        accentColor: Color(0xff62c2a2),
      ),
      child: BottomNavigationBar(
        selectedItemColor: Color(0xff84d1da),
        unselectedItemColor: Colors.grey,
        type: BottomNavigationBarType.fixed,
        items: const <BottomNavigationBarItem>[
          //Index 0
          BottomNavigationBarItem(
            icon: Icon(
              Icons.explore,
            ),
            label: 'Discover',
          ),
          //Index 1
          BottomNavigationBarItem(
            icon: Icon(
              Icons.account_circle,
              // color: Color(0xff84d1da),
            ),
            label: 'Profile',
            // backgroundColor: Color(0xff84d1da),
          ),
          //Index 2
          BottomNavigationBarItem(
            icon: Icon(
              Icons.add_circle_outline,
            ),
            label: 'Add Media',
          ),
        ],
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
    );
  }
}

Here is the NavModel:

import 'package:flutter/material.dart';

import '../screens/Profile_Screen.dart';
import '../screens/Venture_Feed_Screen.dart';
import '../screens/Add_Media_Screen.dart';

import '../widgets/BottomNavBar.dart';

class NavModel extends StatefulWidget {
  static const routeName = '/nav-model';
  @override
  _NavModelState createState() => _NavModelState();
}

class _NavModelState extends State<NavModel> {
  int _currentIndex = 0;

  final tabs = [
    VentureFeedScreen(),
    ProfileScreen(),
    AddMediaScreen(),
  ];

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: tabs[_currentIndex],
        bottomNavigationBar: BottomNavBar(),
      ),
    );
  }
}

Solution

Splitting it into two different classes would not work in this scenario. In the NavModel() class for the body: property of the Scaffold, I was attempting to update the value of the tabs list according to the _currentIndex which had a static index value of 0. I thought that by calling on the BottomNavBar() class and setting the onTap: value to update according to the _onItemTapped method that it would update the body: value according to what was defined in the _onItemTapped method within the BottomNavBar class. This was incorrect.

The code was running correctly as I had defined it: the body: tabs[_currentIndex] of the Scaffold was calling on the correct logic – display the item at the 0 index of the tabs list. While this was occurring the index of the BottomNavBar was updating to reflect the correct user selected index and values thus causing confusion of why the BottomNavBar selected index was updating while the body of the Scaffold remained a constant.

Here is the correct code:

class BottomNavBar extends StatefulWidget {
  static const routeName = '/navigator-model';
  @override
  _BottomNavBarState createState() => _BottomNavBarState();
}

class _BottomNavBarState extends State<BottomNavBar> {
  int _selectedIndex = 0;

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  final List<Widget> tabs = [
    VentureFeedScreen(),
    ProfileScreen(),
    AddMediaScreen(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: tabs.elementAt(_selectedIndex),
      bottomNavigationBar: BottomNavigationBar(
        selectedItemColor: Color(0xff84d1da),
        unselectedItemColor: Colors.grey,
        type: BottomNavigationBarType.fixed,
        items: const <BottomNavigationBarItem>[
          //Index 0
          BottomNavigationBarItem(
            icon: Icon(
              Icons.explore,
            ),
            label: 'Discover',
          ),
          //Index 1
          BottomNavigationBarItem(
            icon: Icon(
              Icons.account_circle,
              // color: Color(0xff84d1da),
            ),
            label: 'Profile',
            // backgroundColor: Color(0xff84d1da),
          ),
          //Index 2
          BottomNavigationBarItem(
            icon: Icon(
              Icons.add_circle_outline,
            ),
            label: 'Add Media',
          ),
        ],
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
    );
  }
}

I am, however, yet to see if the BottomNavBar persists on navigation to another screen (have not yet built that functionality).

Answered By – Matt

Answer Checked By – Robin (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *