skip to Main Content

I have something like this

  int _selectedIndex = 0;

  final List<Widget> _screens = const [
    TopNavigator(),
    SearchScreen(),
    SettingsScreen(),
    SelectedAlbumScreen()
  ];

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


 BottomNavigationBar(
                      type: BottomNavigationBarType.fixed,
                      backgroundColor: Colors.black,
                      items: const <BottomNavigationBarItem>[
                        BottomNavigationBarItem(
                            icon: Icon(Icons.home), label: 'Home'),
                        BottomNavigationBarItem(
                            icon: Icon(Icons.search), label: 'Search'),
                        BottomNavigationBarItem(
                            icon: Icon(Icons.settings), label: 'Settings'),
                      ],
                      currentIndex: _selectedIndex,
                      selectedItemColor: Theme.of(context).primaryColor,
                      unselectedItemColor: Colors.grey,
                      onTap: (int index) {
                        setSelectedScreen(index);
                      },
                    ),

As you can see, I have 4 screens in my list, but only 3 declared in my BottomNavigationBar, so I need to have those 4 screens available to go to them but the number 4 must be hidden for the user so that it can only be accessed by tapping on some button.

When I switch to the screen via code, I get an assertion error.

GestureDetector(
            onTap: () {
              context.read<HandleBottomNavigationIndex>().setSelectedScreen(3);
            }

Failed assertion: line 251 pos 15: '0 <= currentIndex && currentIndex < items.length': is not true.

2

Answers


  1. Chosen as BEST ANSWER

    It seems that by the very nature of the BottomNavigationBar it is impossible to have a number of screens different from the number of items in the BottomNavigationBar.

    but I was able to solve it just by creating my own BottomNavigationBar.

    here is the code:

    item:

    import 'package:flutter/material.dart';
    
    class CustomBottomNavigationBarItem {
      final IconData icon;
      final String label;
    
      const CustomBottomNavigationBarItem(
          {required this.icon, required this.label});
    }
    

    BottomNavigatonBar:

    import 'package:flutter/material.dart';
    import 'package:palm_player/presentation/widgets/bottom_navigator/custom_bottom_navigation_bar_item.dart';
    
    class CustomBottomNavigationBar extends StatefulWidget {
      final List<CustomBottomNavigationBarItem> items;
      final void Function(int index) onTap;
      final int selectedIndex;
      final Color? selectedItemColor;
      final Color? unselectedItemColor;
      final Color? backgroundColor;
    
      const CustomBottomNavigationBar(
          {super.key,
          required this.items,
          required this.onTap,
          required this.selectedIndex,
          this.backgroundColor = Colors.white,
          this.selectedItemColor = Colors.black,
          this.unselectedItemColor = Colors.grey})
          : assert(items.length >= 2);
    
      @override
      State<CustomBottomNavigationBar> createState() =>
          _CustomBottomNavigationBarState();
    }
    
    class _CustomBottomNavigationBarState extends State<CustomBottomNavigationBar> {
      @override
      Widget build(BuildContext context) {
        const double bottomNavigationBarHeight =
            kBottomNavigationBarHeight + (kBottomNavigationBarHeight * 0.2);
        return Container(
          height: bottomNavigationBarHeight,
          decoration: BoxDecoration(color: widget.backgroundColor),
          padding: const EdgeInsets.only(top: 10),
          child: Row(
            children: [
              const Spacer(),
              ...widget.items.asMap().entries.map((currentItem) => Expanded(
                      child: InternalItem(
                    icon: currentItem.value.icon,
                    label: currentItem.value.label,
                    onTap: widget.onTap,
                    index: currentItem.key,
                    color: widget.selectedIndex == currentItem.key
                        ? widget.selectedItemColor!
                        : widget.unselectedItemColor!,
                  ))),
              const Spacer()
            ],
          ),
        );
      }
    }
    
    class InternalItem extends StatelessWidget {
      final int index;
      final IconData icon;
      final String label;
      final Color color;
      final void Function(int index) onTap;
      const InternalItem(
          {super.key,
          required this.icon,
          required this.label,
          required this.onTap,
          required this.index,
          required this.color});
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTap: () {
            onTap(index);
          },
          child: Column(
            children: [
              Icon(
                icon,
                color: color,
              ),
              Text(
                label,
                style: TextStyle(color: color, fontSize: 11),
              )
            ],
          ),
        );
      }
    }
    
    
    

    implementation:

    CustomBottomNavigationBar(
                          selectedItemColor: Theme.of(context).primaryColor,
                          selectedIndex: _selectedIndex,
                          backgroundColor: Colors.black,
                          items: const <CustomBottomNavigationBarItem>[
                            CustomBottomNavigationBarItem(
                                icon: Icons.home, label: 'Home'),
                            CustomBottomNavigationBarItem(
                                icon: Icons.search, label: 'Search'),
                            CustomBottomNavigationBarItem(
                                icon: Icons.settings, label: 'Settings')
                          ],
                          onTap: (int index) {
                            setSelectedScreen(index);
                          },
                        ),
    
    ```
    
    

  2. Try below code…

    bool showItem = true;
    
    void setSelectedScreen(int index) {
        if (index >= 0 && index < _screens.length) {
          setState(() {
            _selectedIndex = index;
          });
        }
      }
    
    List<BottomNavigationBarItem> get _visibleItems {
    final items = <BottomNavigationBarItem>[
      const BottomNavigationBarItem(
        icon: Icon(Icons.home),
        label: 'Home',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.search),
        label: 'Search',
      ),
      const BottomNavigationBarItem(
        icon: Icon(Icons.settings),
        label: 'Settings',
      ),
      if (showItem)
        const BottomNavigationBarItem(
          icon: Icon(Icons.album), 
          label: 'Album',
        ),
    ];
    return items;
     }
    

    And in your build method

    final visibleScreens = showItem
            ? _screens
            : _screens.sublist(0, 3); 
    
    body: visibleScreens[_selectedIndex],
          bottomNavigationBar: BottomNavigationBar(
            type: BottomNavigationBarType.fixed,
            backgroundColor: Colors.black,
            items: _visibleItems,
            currentIndex: _selectedIndex,
            selectedItemColor: Theme.of(context).primaryColor,
            unselectedItemColor: Colors.grey,
            onTap: (int index) {
              setSelectedScreen(index);
            },
          ),
    

    Change showItem true/false as your requirement in setState

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search