Behnam
Behnam

Reputation: 329

FutureBuilder conflict with Notifylistener

I have a method like this in my provider file to update a list of items from server :

Future<void> getServerMeals(String userName, String date) async {
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('UsersEaten'));

    parseQuery
      ..whereContains('eatenBy', userName)
      ..whereEqualTo('eatenDate', date);

    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      List<dynamic>? apiRes = apiResponse.results;

      List<dynamic> newMeals = apiRes!
          .map((e) => EatenItem(
                id: e['objectId'],
                eatenCal: e['eatenCal'],
                eatenTitle: e['eatenTitle'],
                eatenImageUrl: e['eatenImg'],
                userId: e['objectId'],
              ))
          .toList();
      _eatenMeals = newMeals;
      print(apiResponse.results);
     
    }
  }

and in my main screen I've wrapped that list into the FutureBuilder it works fine and return a list:


 Container(
          height: MediaQuery.of(context).size.height * 0.6,
          margin: EdgeInsets.only(left: 30, right: 30),
          child: FutureBuilder(
            builder: (ctx, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Center(
                  child: CircularProgressIndicator(),
                );
              }

              return ListView.builder(
                itemCount: mealsList.eatenMeals.length,
                itemBuilder: (ctx, index) =>
                    DailyEaten(mealsList.eatenMeals[index]),
              );
            },
            future: Provider.of<EatenMeals>(context)
                .getServerMeals(currentUser.username, now),
          ),
        ),

but when I've added notifylistener() in my method my future builder list stuck on waiting state and won't update list what's wrong here

Upvotes: 1

Views: 1388

Answers (3)

Behnam
Behnam

Reputation: 329

@nvoigt

here is my widget and I've removed those Providers from build method


class TimelineWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  
    return Column(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [

        Container(
          child: FutureBuilder(
           future: Provider.of<EatenMeals>(context, listen: false).getServerMeals(),
            builder: (ctx, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Center(
                  child: CircularProgressIndicator(),
                );
              }

              return Consumer<EatenMeals>(
                builder: (ctx, mealsList, child) => ListView.builder(
                  itemCount: mealsList.eatenMeals.length,
                  itemBuilder: (ctx, index) =>
                      DailyEaten(mealsList.eatenMeals[index]),
                ),
              );
            },
          ),
        ),
      ],
    );
  }

as you can see there is nothing in my build method , and I've used Consumer to avoid this loop , but still build method getting rebuild , would you explain with code snippets I appreciate it

Upvotes: 0

Ehsan Askari
Ehsan Askari

Reputation: 891

It is because calling notifyListeners will rebuild the build method(The one from which you have called Provider.of...), which will create a loop.(The build method call the future and notifyListener will rebuild the build method, and this process continues for ever).

To prevent this, provide another argument to the of method: listen: false

Provider.of<EatenMeals>(context, listen: false).getServerMeals(currentUser.username, now);

This will prevent the build method to rebuild.

Upvotes: 2

nvoigt
nvoigt

Reputation: 77304

The problem is that you are creating your future in your build method.

That means on every build, you create this future again.

That is a minor nuisance and a bug in your program, until you also add that the future itself triggers a rebuild, which means it will run, trigger a rebuild, the rebuild will wait for a new future, that will run, trigger a rebuild, create yet again a new future... you get it. It's an endless circle of running a future only to rebuild and run it again.

You have to create your Future outside of the build method. The build method can be triggered many times. Only reference a Future<> type variable in your build method and assign that variable in another method, maybe init or in some setState call when someone clicks a button.

Upvotes: 2

Related Questions