Search code examples
flutterflutter-bottomnavigation

Dynamic BottomNavigationBarItems throwing error 'items.length >= 2': is not true


I want to create a dynamic BottomNavigationBar so that for each screen I can pass the icons/routes I need on that screen. My bottom.dart file looks like this:

import 'package:flutter/material.dart';

class BottomBar extends StatefulWidget {
  final List routesToShow;

  BottomBar({Key? key, required this.routesToShow}) : super(key: key);

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

class _BottomBar extends State<BottomBar> {
  final Map allBottomMenus = {
    'home': {'icon': Icon(Icons.home), 'title': Text('Home'), 'route': '/homepage'},
    'profile': {'icon': Icon(Icons.verified_user), 'title': Text('Profile'), 'route':     '/profile'},
    'info': {'icon': Icon(Icons.info_outline), 'title': Text('Info'), 'route': '/info'},
    'previousScreen': {'icon': Icon(Icons.backspace), 'title': Text('Back'), 'route': ''},
  };

  List<BottomNavigationBarItem> bottomNavigationItems = [];

  void _buildBottomNavigation() {
    widget.routesToShow.forEach((route) {
      if (allBottomMenus.containsKey(route)) {
        bottomNavigationItems.add(BottomNavigationBarItem(
          icon: allBottomMenus[route]['icon'],
          label: allBottomMenus[route]['title'],
        ));
      }
    });
  }

  @override
  void initState() {
    super.initState();
    _buildBottomNavigation();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      items: List.of(bottomNavigationItems),
      onTap: (int index) {
        print(index);
      },
    );
  }
}

and in the homepage, I call this widget like following:

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final routesToShow = List.filled(3, ['homepage', 'info', 'profile']);

    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          child: Column(
            children: [
              buildHeader(),
              const SizedBox(height: 32.0),
            ],
          ),
        ),
      ),
      bottomNavigationBar: BottomBar(routesToShow: routesToShow),
    );
  }
}

But I get an error saying:

Failed assertion: line 307 pos 15: 'items.length >= 2': is not true.

Seems like the widget is never executed as there are no items. Any help will be appreciated.


Solution

  • There were two problems with your code, first this line:

    final routesToShow = List.filled(3, ['homepage', 'info', 'profile']);
    

    it makes a 2D list, therefore you can not retrieve desired data from it with your forEach method; so replace it with:

      final routesToShow = ['homepage', 'info', 'profile'];
    

    the second problem was your map, BottomNavigationBar expects to find a String for label not a Text widget, so replace it with:

    final Map allBottomMenus = {
        'home': {'icon': Icon(Icons.home), 'title': 'Home', 'route': '/homepage'},
        'profile': {
          'icon': Icon(Icons.verified_user),
          'title': 'Profile',
          'route': '/profile'
        },
        'info': {
          'icon': Icon(Icons.info_outline),
          'title': 'Info',
          'route': '/info'
        },
        'previousScreen': {
          'icon': Icon(Icons.backspace),
          'title': 'Back',
          'route': ''
        },
      };