45
votes

I am using a long list in Flutter. All the items are rendering fine but I also receive the following error:

RangeError (index): Invalid value: Not in range 0..2, inclusive: 3

The following is my code:

@override
Widget build(BuildContext context) {
return Container(
  child: getList(),
 );
}

The following is my getList() method:

Widget getList (){
List<String> list = getListItems();
ListView myList = new ListView.builder(itemBuilder: (context, index){
  return new ListTile(
    title: new Text(list[index]),
  );
});
return myList;
}

And the following is my getListItem() method:

List<String> getListItems(){
return ["Faizan", "Usman", "Naouman"];
}

the following is the screenshot of error:

enter image description here

6
add itemCount parameter to ListView.builder() constructor - pskink

6 Answers

109
votes

You should pass the itemCount parameter to the ListView.builder to allow it to know the item count

Widget getList() {
  List<String> list = getListItems();
  ListView myList = new ListView.builder(
    itemCount: list.length,
    itemBuilder: (context, index) {
    return new ListTile(
      title: new Text(list[index]),
    );
  });
  return myList;
}
2
votes

If you are using StreamBuilder then you must use this line of code:

  StreamBuilder(
                  stream: FirebaseFirestore.instance.collection("Tooth")
                  .orderBy("date", descending: false).snapshots() ,
                
                  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
                 
                    if(snapshot.hasData)
                    {
                      return ListView.builder(
                        itemCount: snapshot.data.docs.length,
                          padding: const EdgeInsets.only( top: 20.0),
                        itemBuilder: (BuildContext context, int index) {
                           DocumentSnapshot ds = snapshot.data.docs[index];
 },
                      );
                    }
                   
                  },
                ),
0
votes

For Limited Scroll

The solution is simple you just have to add itemCount to the builder so that the builder allows it to know the item count. Just like the above Code As stated in the this answer

For Infinite Scroll

To do Infinite Scroll use ListView.builder without specifying the itemCount parameter.

  body: ListView.builder(
   itemCount: _photos.length + (_hasMore ? 1 : 0),
    itemBuilder: (context, index) {
      if (index == item.length - _nextPageThreshold) {
        // Here is your manuplated data code
      } else {
        getMoreData();
        return Center(child: CircularProgressIndicator());
      }
    },
  ),

Full code example

class ItemListScreen extends StatefulWidget {
  ItemListScreen({Key key}) : super(key: key);
  @override
  _PhotosListScreenState createState() => _PhotosListScreenState();
}

class _PhotosListScreenState extends State<ItemListScreen> {
  bool _hasMore;
  int _pageNumber;
  bool _error;
  bool _loading;
  final int _defaultPhotosPerPageCount = 10;
  List<Photo> _photos;
  final int _nextPageThreshold = 5;
  @override
  void initState() {
    super.initState();
    _hasMore = true;
    _pageNumber = 1;
    _error = false;
    _loading = true;
    _photos = [];
    fetchPhotos();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Photos App")),
      body: getBody(),
    );
  }

  Widget getBody() {
    if (_photos.isEmpty) {
      if (_loading) {
        return Center(
            child: Padding(
              padding: const EdgeInsets.all(8),
              child: CircularProgressIndicator(),
            ));
      } else if (_error) {
        return Center(
            child: InkWell(
              onTap: () {
                setState(() {
                  _loading = true;
                  _error = false;
                  fetchPhotos();
                });
              },
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Text("Error while loading photos, tap to try agin"),
              ),
            ));
      }
    } else {
      return ListView.builder(
          itemCount: _photos.length + (_hasMore ? 1 : 0),
          itemBuilder: (context, index) {
            if (index == _photos.length - _nextPageThreshold) {
              fetchPhotos();
            }
            if (index == _photos.length) {
              if (_error) {
                return Center(
                    child: InkWell(
                      onTap: () {
                        setState(() {
                          _loading = true;
                          _error = false;
                          fetchPhotos();
                        });
                      },
                      child: Padding(
                        padding: const EdgeInsets.all(16),
                        child: Text("Error while loading photos, tap to try agin"),
                      ),
                    ));
              } else {
                return Center(
                    child: Padding(
                      padding: const EdgeInsets.all(8),
                      child: CircularProgressIndicator(),
                    ));
              }
            }
            final Photo photo = _photos[index];
            return Card(
              child: Column(
                children: <Widget>[
                  Image.network(
                    photo.thumbnailUrl,
                    fit: BoxFit.fitWidth,
                    width: double.infinity,
                    height: 160,
                  ),
                  Padding(
                    padding: const EdgeInsets.all(16),
                    child: Text(photo.title,
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16)),
                  ),
                ],
              ),
            );
          });
    }
    return Container();
  }

  Future<void> fetchPhotos() async {
    try {
      final response = await http.get("https://jsonplaceholder.typicode.com/photos?_page=$_pageNumber");
      List<Photo> fetchedPhotos = Photo.parseList(json.decode(response.body));
      setState(() {
        _hasMore = fetchedPhotos.length == _defaultPhotosPerPageCount;
        _loading = false;
        _pageNumber = _pageNumber + 1;
        _photos.addAll(fetchedPhotos);
      });
    } catch (e) {
      setState(() {
        _loading = false;
        _error = true;
      });
    }
  }
}

class Photo {
  final String title;
  final String thumbnailUrl;
  Photo(this.title, this.thumbnailUrl);
  factory Photo.fromJson(Map<String, dynamic> json) {
    return Photo(json["title"], json["thumbnailUrl"]);
  }
  static List<Photo> parseList(List<dynamic> list) {
    return list.map((i) => Photo.fromJson(i)).toList();
  }
}

to infinite scroll answer credit and for more info Infinite Scrolling ListView

0
votes

This mainly occurs when you are using wrong index value to fetch data from list. in my case, I was doing the same mistake.

-1
votes

I had this problem inside a GridView, but it had nothing to do with my GridView. I was splitting addresses by commas, ex addresses[index].split(',')[0], but I came across a address that had no commas which is why I suddenly got this error. Look closely through the debug console to find the exact line of the error, and test every piece of code in your GridView to pinpoint the error.

-2
votes
Widget getListView(){
  var itemList = getListElement();
   var list = ListView.builder(
     itemCount: itemList.length,
       itemBuilder:(context, index){
         return ListTile(
           title: Text(itemList[index]),   
         );
         }
   );
   return list;
}