flutterlistviewindexoutofrangeexception

Flutter ListView.Builder() dynamic list - shows data but with error


Hi I'm trying to show new (and old) versions of app with list of changes to users. I created some functions and pop-up message where they can see version and list of changes.

It works but when it shows versions and changes it also shows infinite red field with error under new versions:

RangeError (index): Index out of range: index should be less than 1: 1

And same under older verions:

RangeError (index): Index out of range: index should be less than 6: 6

I think there could be problem with the ListView.Builder() or what I send to it. Can anyone help?

Code:

    Future<void> displayNewVersion() async {
    List _versions = await readJson(); //geting data from Json
    Map<String, bool> _control = await controlSeenVersions(_versions); //sorting what version changes was seen or not
    Map<String, List<dynamic>> _listSeen = {}, _listNotSeen = {};

    for (var version in _versions) {
      if (_control[version["vName"]]) {
        _listSeen[version["vName"]] = version["changes"];
      } else {
        _listNotSeen[version["vName"]] = version["changes"];
      }
    }

    if (_listNotSeen.isNotEmpty) {
      showChangelog(_listNotSeen, _listSeen, _versions);
    }
  }

function to show the new versions

showChangelog(Map<String, List<dynamic>> _listNotSeen, Map<String, List<dynamic>> _listSeen, List _versions) async {
var shortestSide = MediaQuery.of(context).size.shortestSide;
final bool isPhone = shortestSide < 600;
customDialogWithHeaderSE(
    context,
    isPhone,
    LocaleKeys.newVersionTitle + _versions[0]["vName"],
    //StatefulBuilder must be here coz you need context and context of dialog itself coz one pop dialog and second navigate..
    Container(
      margin: const EdgeInsets.all(8.0),
      width: 300.0,
      child: Expanded(
        child: ListView.builder(
          scrollDirection: Axis.vertical,
          shrinkWrap: true,
          itemCount: _versions.length,
          itemBuilder: (context, int index) {
            String key = _listNotSeen.keys.elementAt(index);
            return Container(
                child: Column(
                  children: <Widget>[
                  new ListTile(
                    title: new Text(key.toString(),
                        style: TextStyle(fontWeight: FontWeight.bold)),
                    subtitle: Container(
                      child: ListView.builder(
                          physics: const NeverScrollableScrollPhysics(),
                          shrinkWrap: true,
                          itemCount: _listNotSeen[key].length,
                          itemBuilder: (context, int index) {
                            return Container(
                                child:
                                    Text("- " + _listNotSeen[key][index]));
                          }),
                    )),
                new Divider(
                  height: 2.0,
                ),
              ],
            ));
          },
        ),
      ),
    ),
    [
      if (_listSeen.isNotEmpty)
        Column(
          children: [
            ExpansionTile(
              title: Text(
                'Older releases',
                style: TextStyle(
                    fontWeight: FontWeight.w400,
                    color: ColorsApp.blue75_storaenso,
                    height: 1.5,
                    fontSize: (isPhone) ? 18.0 : 20.0),
              ),
              children: [
                // Change as per your requirement
                SizedBox(
                  height: 300.0, // Change as per your requirement
                  width: 300.0,
                  child: ListView.builder(
                      scrollDirection: Axis.vertical,
                      shrinkWrap: true,
                      itemCount: _versions.length,
                      itemBuilder: (context, int index) {
                        String key = _listSeen.keys.elementAt(index);
                        return Column(children: <Widget>[
                          new ListTile(
                            title: new Text(key.toString(), style: TextStyle(fontWeight: FontWeight.bold)),
                            subtitle: Container(
                              child: ListView.builder(
                                  physics: const NeverScrollableScrollPhysics(),
                                  shrinkWrap: true,
                                  itemCount: _listSeen[key].length,
                                  itemBuilder: (context, int index) {
                                    return Container(child: Text("- " + _listSeen[key][index]));
                                  }),
                            ),
                          ),
                          new Divider(
                            height: 2.0,
                          ),
                        ]);
                      }),
                ),
              ],
            ),
          ],
        ),

Solution

  • Looks like you set itemCount: in your ListView.builder widgets to _versions.length. itemCount is how many times your builder: function will run. If itemCount is 7 then builder: function will run 7 times, and each time a new index will be passed in, i.e., 0, 1, 2, 3, 4, 5, 6. This is not a problem, however in your builder: function you try to index _listNotSeen and _listSeen. Issues will arise when these have fewer elements than _versions. For example, if _listNotSeen has 4 elements while _versions has 7 then when the index gets to 4 or higher you will be out of range. You need to replace _versions.length with something that has equal length to _listNotSeen or whatever you are indexing in your builder: function