Ibrahim Yildirim
Ibrahim Yildirim

Reputation: 2771

Consumer not updating with notifyListeners()

I have created a simple Widget Tree of my app in flutter that adresses the issue.

The issue is that when I call the method in my SleepSessionData which is a ChangeNotifier class, the consumer does not get fired.

There is no issue if the method containing notifyListeners() is called within the same widget, but when it is two sibling widgets the consumer does not update unless a setState is called.

Widget Tree

And a image of the screen for reference

enter image description here

SleepSessionData extends ChangeNotifier

void updateSelectedPlaylists(List<String> selectedPlaylists) {
    this.selectedPlaylists = selectedPlaylists;
    notifyListeners();
    _saveData();
}

PlaylistSelectorWidget

void selectedIndex(int index) {
    ...
    context.read<SleepSessionData>().updateSelectedPlaylists(_selectedPlaylistIds);
    setState(() {});
}

In my SettingsWidget I tried using both Consumer Widget and and watch() method. But it is as the notifyListeners() does not trigger the consumer/watcher. If i run a setState() (triggered by user input) the value from the the ChangeNotifier updates.

Here's my MultiProvider in main.dart

child: MultiProvider(
  providers: [
    ChangeNotifierProvider<MySleepData>(create: (_) => MySleepData()),
    ChangeNotifierProvider<DirectoryData>(create: (_) => DirectoryData()),
    FutureProvider<UserData>(
        create: (_) => LocalPersistence.getUserData(),
        initialData: UserData()),
    FutureProvider<SleepSessionData>(
      create: (_) => LocalPersistence.getSleepSessionData(),
      initialData: SleepSessionData(),
    )
  ],

Upvotes: 3

Views: 6492

Answers (2)

Turbo
Turbo

Reputation: 688

You shouldn't wrap your ChangeNotifier inside the FutureProvider in order to make it works properly.

When you wrap the ChangeNotifier inside the FutureProvider it breaks the process of adding listeners to the ChangeNotifiers somehow. You always get 0 listeners when wrapping ChangeNotifier with FutureProvider. (You can verify this by debugging and check the listeners property of your ChangeNotifier instance.) That's why when calling notifyListeners(), the widgets don't rerender since they're not the listener of your ChangeNotifier instance.

So the solution to your problem would be:

  • Use ChangeNotifier in main.dart
child: MultiProvider(
  providers: [
    ChangeNotifierProvider<MySleepData>(create: (_) => MySleepData()),
    ChangeNotifierProvider<DirectoryData>(create: (_) => DirectoryData()),
    ChangeNotifierProvider<UserData>(create: (_) => UserData()),
    ChangeNotifierProvider<SleepSessionData>(create: (_) => SleepSessionData()),
  ],
  • In the SessionSetupPage, you get the data from your local store and load the changes to SleepSessionData
loadData(SleepSessionData sessionData) async {
    final data = await LocalPersistence.getData();
    sessionData.setData(data);
}

Widget build(BuildContext context) {
   loadData(context.read<SleepSessionData>());
   // ..
}
  • Then the code in your PlaylistSelector and SettingsWidget should work whenever notifyListeners() is called since the listeners are bound correctly now.

so when the future completes it will overwrite this value

This is not totally the root cause of the issue though. Even if we create the SleepSessionData instance beforehand and return the same instance inside the FutureProvider, the problem still persists.

Upvotes: 3

Pieter van Loon
Pieter van Loon

Reputation: 5648

You only have a FutureProvider that holds the SleepSessionData so that will only update once when the future completes. If you want the ui to update on changes of the result of the future then you need to listen to those changes, for instance by using a ChangeNotifierProvider.

Additionally you are giving the FutureProvider an initial data with a new instance of your SleepSessionData so when the future completes it will overwrite this value meaning that any actions you do on the initial value will be discarded.

So a better way of doing this would be to not use a FutureProvider but use a ChangeNotifierProvider and start the future to load the data within your SleepSessionData class.

Upvotes: 0

Related Questions