StreamProvider от Riverpod выдает значение потока только один раз | Flutter

#flutter #dart #riverpod #flutter-hive

#flutter #dart #riverpod #flutter-улей

Вопрос:

Я написал StreamProvider, который я прослушиваю сразу после запуска, чтобы получить всю информацию о потенциально вошедшем в систему пользователе. Если пользователя нет, поэтому результат будет нулевым, слушатель остается в состоянии загрузки, поэтому я решил отправить обратно значение по умолчанию для пустого пользователя, чтобы сообщить мне, что загрузка завершена. Я должен был это сделать, потому что метод watch() улья запускается только при изменении данных, чего он не делает при запуске. Итак, после этого я хочу, чтобы метод watch() выполнял свою работу, но проблема с этим заключается в следующих сценариях:

  1. При запуске: пользователь не вставляет метод user —> watch -> Я получаю вставленные данные пользователей -> Удаление вошедшего в систему пользователя -> метод watch не запускается.
  2. При запуске: запускается полный пользователь — удаление метода user -> watch -> Я получаю пустой user -> Вставка метода user -> watch не запускается.

Через некоторое время я обнаружил, что могу использовать все операции CRUD так часто, как захочу, и окно улья делает то, что должно, но метод watch() больше не запускается после того, как он был запущен один раз.

Streamprovider (ы):

 final localUsersBoxFutureProvider = FutureProvider<Box>((ref) async {
  final usersBox = await Hive.openBox('users');
  return usersBox;
});

final localUserStreamProvider = StreamProvider<User>((ref) async* {
  final usersBox = await ref.watch(localUsersBoxFutureProvider.future);

  yield* Stream.value(usersBox.get(0, defaultValue: User()));
  yield* usersBox.watch(key: 0).map((usersBoxEvent) {
    return usersBoxEvent.value == null ? User() : usersBoxEvent.value as User;
  });
});
 

Слушатель:

 return localUserStream.when(
  data: (data) {
    if (data.name == null) {
      print('Emitted data is an empty user');
    } else {
      print('Emitted data is a full user');
    }

    return Container(color: Colors.blue, child: Center(child: Row(children: [
      RawMaterialButton(
        onPressed: () async {
          final globalResponse = await globalDatabaseService.signup({
            'email' : 'name@email.com',
            'password' : 'password',
            'name' : 'My Name'
          });

          Map<String, dynamic> jsonString = jsonDecode(globalResponse.bodyString);
          await localDatabaseService.insertUser(User.fromJSON(jsonString));
        },
        child: Text('Insert'),
      ),
      RawMaterialButton(
        onPressed: () async {
          await localDatabaseService.removeUser();
        },
        child: Text('Delete'),
      )
    ])));
  },
  loading: () {
    return Container(color: Colors.yellow);
  },
  error: (e, s) {
    return Container(color: Colors.red);
  }
);
 

Методы CRUD:

 Future<void> insertUser(User user) async {
    Box usersBox = await Hive.openBox('users');
    await usersBox.put(0, user);
    await usersBox.close();
  }

  Future<User> readUser() async {
    Box usersBox = await Hive.openBox('users');
    User user = usersBox.get(0) as User;
    await usersBox.close();
    return user;
  }

  Future<void> removeUser() async {
    Box usersBox = await Hive.openBox('users');
    await usersBox.delete(0);
    await usersBox.close();
  }
 

Есть идеи, как я могу сообщить StreamProvider, что метод watch() следует поддерживать в рабочем состоянии, даже если одно значение уже было передано?

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

1. не могли бы вы сказать мне, пожалуйста, какова ваша localDatabaseService реализация? я пытаюсь реализовать Hive инициалы с Riverpod

2. Каждый метод, который я реализовал, можно найти в моем вопросе в разделе «методы CRUD».

3. я снова прочитал, localDatabaseService и localUserStream реализаций в вашем сообщении не существует

4. @DolDurma Я действительно не понимаю, что вы ищете. «localDatabaseService» — это переменная, используемая для экземпляра класса «LocalDatabaseService», операции CRUD которого вы должны видеть в моем вопросе. Также «localUserStream» является переменной для того, что я получаю от localUserStreamProvider.

Ответ №1:

но метод watch() больше не запускается после того, как он был запущен один раз

Это потому, что после каждого CRUD вы закрываете окно, поэтому поток (который использует это поле) перестает выдавать значения. Не имеет значения, вызываете ли вы его откуда-то извне riverpod ( await Hive.openBox('users') ), вызывая ту же ссылку. Вы должны закрывать поле только тогда, когда вы перестанете его использовать, я бы рекомендовал использовать autodispose с riverpod, чтобы закрыть его, когда он больше не используется, и, возможно, поместить эти методы CRUD в класс, контролируемый riverpod, чтобы вы могли полностью контролировать жизненный цикл этого поля

 final localUsersBoxFutureProvider = FutureProvider.autoDispose<Box>((ref) async {
  final usersBox = await Hive.openBox('users');

  ref.onDispose(() async => await usersBox?.close()); //this will close the box automatically when the provider is no longer used
  return usersBox;
});

final localUserStreamProvider = StreamProvider.autoDispose<User>((ref) async* {
  final usersBox = await ref.watch(localUsersBoxFutureProvider.future);

  yield* Stream.value(usersBox.get(0, defaultValue: User()) as User);
  yield* usersBox.watch(key: 0).map((usersBoxEvent) {
    return usersBoxEvent.value == null ? User() : usersBoxEvent.value as User;
  });
});
 

И в ваших методах используйте одно и то же поле экземпляра из localUsersBoxFutureProvider и не закрывайте поле после каждого, когда вы перестанете слушать stream , или localUsersBoxFutureProvider оно закроется само

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

1. Я включу твое имя в свою молитву!