Search code examples
listviewflutterbuilder

ListView.builder itemCount not updating


I have a widget witch returns a ListView.builder, the items of the listview are passed as paramaters of the widget, when user has reached the bottom of the listview new items are loaded. But when the items are loaded the itemCount isn't updated, if print the value parameter I get the right amount of items but I get an index error with the precedent value as range... Here my log:

I/flutter ( 4654): 4
I/flutter ( 4654): 8

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building:
RangeError (index): Invalid value: Not in range 0..3, inclusive: 4

When the exception was thrown, this was the stack: 
#0      List.[] (dart:core-patch/growable_array.dart:146:60)
#1      _UsersListViewState.showFollowButton (package:the_spot/services/library/usersListView.dart:167:30)
#2      _UsersListViewState.showResultWidget (package:the_spot/services/library/usersListView.dart:147:43)
#3      _UsersListViewState.build.<anonymous closure> (package:the_spot/services/library/usersListView.dart:75:20)
#4      SliverChildBuilderDelegate.build (package:flutter/src/widgets/sliver.dart:446:22)
...
════════════════════════════════════════════════════════════════════════════════════════════════════

See that the first time items are loaded it print the itemCount (4) and no error happened (on my screen I have the list of items too) but when the user scroll down to update the list of items it print the new itemCount (8) but the range still 4...

Here the widget code (I removed unnecessary content):

import 'package:flutter/material.dart';
import 'package:the_spot/pages/home_page/profile.dart';
import 'package:the_spot/services/library/userProfile.dart';
import 'package:the_spot/services/library/configuration.dart';

import '../../theme.dart';
import '../database.dart';
import 'library.dart';

class UsersListView extends StatefulWidget{

  final Configuration configuration;
  final List<UserProfile> query;
  final VoidCallback onBottomListReachedCallback;

  const UsersListView({Key key, this.configuration, this.query, this.onBottomListReachedCallback}) : super(key: key);

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

class _UsersListViewState extends State<UsersListView> {

  List<bool> waitForFollowing = [];
  List<bool> friendRequestAlreadyDone = [];
  List<bool> waitForSendingFriendRequest = [];

  bool isLoadingData = false;

  @override
  void initState() {
    super.initState();
    waitForFollowing.clear();
    widget.query.forEach((element) {
      waitForFollowing.add(false);
      waitForSendingFriendRequest.add(false);
      if (element.pendingFriendsId
          .indexOf(widget.configuration.userData.userId) !=
          -1) {
        friendRequestAlreadyDone.add(true);
      } else {
        friendRequestAlreadyDone.add(false);
      }
    });
  }


  @override
  void didUpdateWidget(UsersListView oldWidget) {
    super.didUpdateWidget(oldWidget);
    isLoadingData = false;
  }

  @override
  Widget build(BuildContext context) {
    print(widget.query.length);
    return Expanded(
      child: NotificationListener<ScrollNotification>(
        onNotification: (ScrollNotification scrollInfo){
          if (scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent && isLoadingData == false) {
            isLoadingData = true;
            widget.onBottomListReachedCallback();
          }
          return true;
        },
        child: ListView.builder(
          padding: EdgeInsets.fromLTRB(
              widget.configuration.screenWidth / 20,
              widget.configuration.screenWidth / 40,
              widget.configuration.screenWidth / 20,
              widget.configuration.screenWidth / 40),
          itemCount: widget.query.length,
          itemBuilder: (BuildContext context, int itemIndex) {
            return showResultWidget(itemIndex);
          },
          shrinkWrap: false,
        ),
      ),
    );
  }

EDIT: as asked, how the widget is called:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:the_spot/services/database.dart';
import 'package:the_spot/services/library/configuration.dart';
import 'package:the_spot/services/library/userProfile.dart';
import 'package:the_spot/services/library/usersListView.dart';
import 'package:the_spot/theme.dart';


class FollowersFollowingFriendsPage extends StatefulWidget {
  final Configuration configuration;
  final UserProfile userProfile;
  final String type;

  const FollowersFollowingFriendsPage(
      {Key key, this.configuration, this.userProfile, this.type})
      : super(key: key);

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

class _FollowersFollowingFriendsPageState
    extends State<FollowersFollowingFriendsPage> {
  bool isWaiting = true;
  List<UserProfile> queryResult = [];
  Timestamp index = Timestamp.now();

  String noResultMessage;
  String appBarTitle;

  @override
  void initState() {
    super.initState();
    init();



  }

  void init() async {
    switch (widget.type) {
      case "Followers":
        {
          noResultMessage =
          "This user haven't been followed by anyone for the moment.";
          appBarTitle = "Users following " + widget.userProfile.pseudo;

          Map<String, Object> res = await Database().getFollowersOf(context, widget.configuration.userData.userId, widget.userProfile.userId, index, 10);

          queryResult.addAll(res['users']);
          index = res['lastTimestamp'];
          setState(() {
            isWaiting = false;
          });

        }
        break;

      case "Following":
        {
          noResultMessage = "This user doesn't follow anyone for the moment.";
          appBarTitle = "Users followed by " + widget.userProfile.pseudo;

          Map<String, Object> res = await Database().getFollowingOf(context, widget.configuration.userData.userId, widget.userProfile.userId, index, 4);

          queryResult.addAll(res['users']);

          index = res['lastTimestamp'];
          setState(() {
            isWaiting = false;
          });
        }
        break;
      case "Friends":
        {
          noResultMessage = "This user hasn't added friends yet.";
          appBarTitle = "Friends of " + widget.userProfile.pseudo;

          queryResult = await Database().getUsersByIds(context, widget.userProfile.friends, verifyIfFriendsOrFollowed: true, mainUserId: widget.userProfile.userId);
          setState(() {
            isWaiting = false;
          });
        }
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: PrimaryColorDark,
      appBar: AppBar(title: Text(appBarTitle),),
      body: Column(
        children: <Widget>[
          showQueryResultsWidget(),
        ],
      ),
    );
  }

  Widget showQueryResultsWidget() {
    if (isWaiting)
      return Padding(
          padding: EdgeInsets.only(top: widget.configuration.screenWidth / 20),
          child: Center(
            child: CircularProgressIndicator(),
          ));
    else if (queryResult.length == 0 || queryResult == null)
      return Padding(
        padding: EdgeInsets.only(top: widget.configuration.screenWidth / 20),
        child: Center(child: Text(noResultMessage)),
      );
    else
      return UsersListView(
        configuration: widget.configuration,
        query: queryResult,
        onBottomListReachedCallback: init,
      );
  }
}

Solution

  • The error was causing by my lists and not the listView, I just forgotted to call all the code in my initState in the didUpdate.