Home > front end >  setState() or markNeedsBuild() called during build with Provider
setState() or markNeedsBuild() called during build with Provider

Time:10-05

Looked everywhere but couldn't find a solution for it. In my app I call FutureBuilder to load a list of documents from firebase. Although it works, I get setState() or markNeedsBuild() called during build.. Do you guys have any idea on how I can get rid of these error?

Here is my function:

  Future<void> loadFollowers(List profilesId) async {

    loading = true;

    profilesId.forEach((id) async {
     final DocumentSnapshot doc = await userRef.doc(id).get();
     profile = Profile.fromDocument(doc);
    followersList.add(profile);
    });

   loading = false;

   notifyListeners();
 }

and where I get the error:

@override
   Widget build(BuildContext context) {
    return Stack(
     children: [
      gradientBackground(),
      Scaffold(
        appBar: AppBar(
          brightness: Brightness.dark,
          elevation: 0,
          backgroundColor: Colors.transparent,
          centerTitle: true,
          title: Text('Seguidores',
              style:
                  TextStyle(color: Colors.white, fontFamily: 'MontSerrat')),
        ),
        backgroundColor: Colors.transparent,
        body: FutureBuilder(
          future: Provider.of<ProfileManager>(context, listen: false)
              .loadFollowers(widget.followersId),
          builder: (context, snapshot) {
            return Consumer<ProfileManager>(
              builder: (_, profileManager, __) {
                if (profileManager.loading)
                  return circularProgress(Colors.white);
                else
                  return ListView.builder(
                    itemCount: followersList.length,
                    itemBuilder: (context, index) {
                      //print(followersList[index].name);

                      return GestureDetector(
                        onTap: () {
                          Navigator.push(
                              context,
                              MaterialPageRoute(
                                  builder: (_) => ProfileScreen(
                                      /*posts: postManager
                                    .getPosts(profiles[index].id),*/
                                      home: false)));
                        },
                        child: Card(
                          margin: EdgeInsets.symmetric(
                              horizontal: 16, vertical: 8),
                          child: ListTile(
                            leading: CircleAvatar(
                              backgroundImage: CachedNetworkImageProvider(
                                  profileManager
                                          .followersList[index].picture ??
                                      ''),
                            ),
                            title: Text(
                              profileManager.followersList[index].name ??
                                  '',
                              style: TextStyle(
                                  fontFamily: 'BebasNeue', fontSize: 20),
                            ),
                          ),
                        ),
                      );
                    },
                  );
              },
            );
          },
        ))
      ],
    );
  }

CodePudding user response:

What might be going wrong is that inside the FutureBuilder you are returning a Consumer.

The Consumer is used to see if the Future loadFollowers that is being called is still loading or not. A better way to do this is to check the snapshot in a series of if statements like so:

if (snapshot.hasData) {Do what you want to do when the Future is finished}
else if (snapshot.hasError) {do some error handling, ${snapshot.error} contains the error}
else {show a CircularProgressIndicator() because the Future has not returned anything yet.}

To make this working, you need to make sure the Future is returning something, for example a bool, so turn your Future code into:

Future<bool> loadFollowers(List profilesId) async {

loading = true;

profilesId.forEach((id) async {
 final DocumentSnapshot doc = await userRef.doc(id).get();
 profile = Profile.fromDocument(doc);
followersList.add(profile);
});

loading = false;

notifyListeners();
return true;
}

CodePudding user response:

What's happening is that your FutureBuilder is calling the loadFollowers method that will notify listeners of profileManager . Then you're building the widget with a Consumer that will listen to profileManager.

When loadFollowers reaches the end, FutureBuilder will ask for a rebuild (since it gots the future it waited for) at the same time a setState() will be called by the listener.

There is another confusing thing in your code : you included a FutureBuilder without using it (not waiting for future nor using the data the future returned) but instead you manage the async loading through the profileManager provider. You should do one or the other.

I suggest you try to remove FutureBuilder.

  • Related