Riverpod: есть ли правильный способ чтения StreamProvider в другом StreamProvider? - PullRequest
0 голосов
/ 14 июля 2020

Я пытался создать потоки в документы Firestore, используя uid , полученный из моего auth Provider:

class AuthService {
  ...

  static final provider = StreamProvider.autoDispose((ref) => FirebaseAuth.instance.onAuthStateChanged);
  ...
}

Однако я изо всех сил пытаюсь создать StreamProvider в зависимости от значения из auth Provider.

class User {
  ...
  static final provider = StreamProvider((ref) {
    final stream = ref.read(AuthService.provider);
    // Returns AsyncValue<Stream<User>> instead of desired AsyncValue<User>
    return stream.map((auth) => Service.user.stream(auth.uid));
  });
  ...
}

Я также пробовал использовать Computed для возврата uid или самого потока, но вы не можете прочитать Computed из Provider (что имеет смысл в ретроспективе).

Этот вопрос является наиболее актуальным в этом топе c, но он касается провайдера, а не Riverpod.

PS Можно ли создать тег Riverpod?

Изменить:

Ответ работает не совсем правильно. await for loop запускается только один раз, тогда как слушатель улавливает все события.

static final provider = StreamProvider((ref) async* {
  final stream = ref.read(AuthService.provider);
  print('userProvider init');

  stream.listen((auth) {
    print('LISTENED: ${auth?.uid}');
  });

  await for (final auth in stream) {
    print('uid: ${auth?.uid}');
    yield* Service.user.stream(auth?.uid);
  }
});

Этот код при входе в систему дает следующее:

userProvider init
LISTENED: <redacted UID>
uid: <redacted UID>

И затем при выходе из системы:

LISTENED: null

Там, где я ожидал бы увидеть uid: null, который обновит поток, но при любых дополнительных событиях аутентификации запускается только слушатель, и никакие события не перехватываются await for loop.

Интересно, что при использовании инспектора флаттера значение, выдаваемое провайдером аутентификации, никогда не меняется:

AutoDisposeStreamProvider<FirebaseUser>#95f11: AsyncValue<FirebaseUser>.data(value: FirebaseUser(Instance of 'PlatformUser'))

сохраняется через события входа / выхода , который мог бы объяснить такое поведение, но я не уверен, что делать, чтобы исправить это.

Есть идеи? Я застрял на этом некоторое время и не могу исправить проблему.

Ответы [ 2 ]

2 голосов
/ 14 июля 2020

Проблема в том, что ваш провайдер создает не Stream<User>, а Stream<Stream<User>>

В рамках 0.6.0-dev вы можете использовать ref.watch для простого объединения потоков:

class User {
  ...
  static final provider = StreamProvider((ref) {
    final auth = ref.watch(AuthService.provider);
    return Service.user.stream(auth.uid);
  });
  ...
}
1 голос
/ 17 июля 2020

Я хочу предисловие к этому, сказав, что я работаю с Dart / Flutter всего несколько месяцев, поэтому не стесняйтесь исправлять все, что я скажу, и я обновлю ответ.

Я решил эту проблему после долгих проб и ошибок и многократного пересмотра документации. Я полагаю, я сделал плохое предположение, что поставщики будут обновлять, когда поставщик, от которого они зависят, изменяется.

Я обнаружил, что после того, как StreamProvider вернется (или уступит), пока он не будет удален, он всегда будет возвращать то же значение, что и изначально сделал, независимо от изменений зависимостей или событий, исходящих из Stream. Здесь Computed полезен, но на самом деле не работает, когда вы хотите вернуть AsyncValue от своего провайдера (как ведет себя StreamProvider).

Кроме того, дополнительная путаница была вызвана тем, что Flutter Inspector не обновлял виджет ProviderScope правильно . Мне нужно щелкнуть и обновить sh несколько раз, чтобы увидеть обновления поставщиков или их состояние. В любом случае ...

class AuthService {
  ...
  static final provider = StreamProvider.autoDispose((ref) {
    final sub = FirebaseAuth.instance.onAuthStateChanged.listen((auth) => ref.read(uidProvider).state = auth?.uid);
    ref.onDispose(() => sub.cancel());
    return FirebaseAuth.instance.onAuthStateChanged;
  });

  static final uidProvider = StateProvider<String>((_) => null);
  ...
}
class User {
  ...
  static final provider = StreamProvider.autoDispose((ref) {
    final uid = ref.read(AuthService.uidProvider)?.state;
    return Service.user.stream(uid);
  });
  ...

Это решение работает, учитывая, что ваш пользовательский интерфейс больше не зависит от провайдеров (что позволяет им правильно удалять), когда пользователь выходит из вашего приложения.

...