Потребитель не обновляет с помощью notifyListeners()

#flutter #provider #flutter-provider

Вопрос:

Я создал простое дерево виджетов своего приложения в flutter, которое решает проблему.

Проблема в том, что когда я вызываю метод в своем SleepSessionData классе, который является ChangeNotifier классом, потребитель не увольняется.

Нет никаких проблем, если метод, содержащий notifyListeners() , вызывается в одном и том же виджете, но когда это два родственных виджета, потребитель не обновляется, если setState не вызывается a.

Дерево виджетов

И изображение экрана для справки

введите описание изображения здесь

Данные о сеансе сна расширяют определитель изменений

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

ПлейлистСелекторВиджет

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

В моем SettingsWidget я пробовал использовать как Consumer виджет, так и watch() метод и. Но это так, как notifyListeners() не вызывает потребителя/наблюдателя. Если я запущу setState() (инициируемое пользовательским вводом) значение из ChangeNotifier обновлений.

Вот мой MultiProvider в 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(),
    )
  ],
 

Комментарии:

1. Я бы предположил, что это потому, что вы используете FutureProvider для своего SleepSessionData , а не ChangeNotifierProvider для . В документации для FutureProvider говорится: «Принимает будущее и обновляет зависимых, когда будущее завершается». — таким образом, он обновляется только при обновлении в будущем, но не самой модели / класса. Я предлагаю ChangeNotifierProvider также использовать там и оценить вашу асинхронную getSleepSessionData функцию до этого

2. @kounex Спасибо за ваш ответ! Но я не думаю, что в этом проблема. Поставщик и notifyListeners() работает нормально, если он read() и watch() вызывается из одного и того же виджета. Но в данном случае это не так. Но я проверю это, просто чтобы быть уверенным!

3. вы пробовали использовать listen:false ?

Ответ №1:

Вы не должны заворачивать себя ChangeNotifier FutureProvider внутрь, чтобы он работал должным образом.

Когда вы заворачиваете ChangeNotifier его внутрь FutureProvider , это нарушает процесс добавления слушателей к ChangeNotifiers каким-либо образом. Вы всегда получаете 0 слушателей при обертывании ChangeNotifier FutureProvider . (Вы можете проверить это, отладив и проверив listeners свойство своего ChangeNotifier экземпляра.) Вот почему при вызове notifyListeners() виджеты не перенаправляются , так как они не прослушивают ваш ChangeNotifier экземпляр.

Таким образом, решением вашей проблемы было бы:

  • Использование ChangeNotifier в main.dart
 child: MultiProvider(
  providers: [
    ChangeNotifierProvider<MySleepData>(create: (_) => MySleepData()),
    ChangeNotifierProvider<DirectoryData>(create: (_) => DirectoryData()),
    ChangeNotifierProvider<UserData>(create: (_) => UserData()),
    ChangeNotifierProvider<SleepSessionData>(create: (_) => SleepSessionData()),
  ],
 
  • В SessionSetupPage , вы получаете данные из своего локального хранилища и загружаете изменения в SleepSessionData
 loadData(SleepSessionData sessionData) async {
    final data = await LocalPersistence.getData();
    sessionData.setData(data);
}

Widget build(BuildContext context) {
   loadData(context.read<SleepSessionData>());
   // ..
}
 
  • Тогда код в вашем PlaylistSelector и SettingsWidget должен работать всякий notifyListeners() раз, когда вызывается, так как прослушиватели теперь связаны правильно.

поэтому, когда будущее завершится, оно перезапишет это значение

Однако это не совсем основная причина проблемы. Даже если мы создадим SleepSessionData экземпляр заранее и вернем тот же экземпляр внутри FutureProvider , проблема все равно сохранится.

Ответ №2:

У вас есть только то FutureProvider , что содержит SleepSessionData so, которое будет обновляться только один раз, когда будущее завершится. Если вы хотите, чтобы пользовательский интерфейс обновлялся в связи с изменениями результата в будущем, вам нужно прослушать эти изменения, например, с помощью a ChangeNotifierProvider .

Кроме того, вы предоставляете FutureProvider исходные данные с новым экземпляром вашего SleepSessionData , поэтому, когда будущее завершится, оно перезапишет это значение, что означает, что любые действия, которые вы выполняете с исходным значением, будут отброшены.

Поэтому лучшим способом сделать это было бы не использовать a FutureProvider , а использовать a ChangeNotifierProvider и начать в будущем загружать данные в свой SleepSessionData класс.