Riverpod:是否有在另一个 StreamProvider 中读取 StreamProvider 的正确方法?

Riverpod: Is there a correct way to read a StreamProvider within another StreamProvider?

我一直在尝试使用从我的身份验证 Provider:

获得的 uid 创建到 Firestore 文档的流
class AuthService {
  ...

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

但是,我正在努力根据来自 auth Provider 的值实际创建 StreamProvider

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 到 return uid 或流本身,但是你不能从 [= 中读取 Computed 14=](回想起来是有道理的)。

与此主题最相关,但它处理的是 Provider,而不是 Riverpod。

P.S。可以创建 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 的地方,它会更新流,但是在任何更多的 auth 事件发生时,只会触发侦听器,await for loop 不会捕获任何事件。

有趣的是,使用 flutter 检查器,auth 提供者发出的值永远不会改变,或者:

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

坚持 login/logout 事件,这可以解释这种行为,但我不确定如何解决它。

有什么想法吗?我已经坚持了一段时间,无法解决这个问题。

问题是,您的提供商没有创建 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);
  });
  ...
}

我想先说我只使用 Dart/Flutter 几个月,所以请随时纠正我所说的任何内容,我会更新答案。

经过反复试验并多次重新审查文档,我解决了这个问题。我想我做了一个错误的假设,即当 Provider 依赖更改时 Provider 会更新。

我发现一旦 StreamProvider returns(或 yields),在它被处置之前它总是 return 它最初的相同值,无论依赖性变化或事件来自溪流。这是 Computed 有用的地方,但当您希望 return 来自提供商的 AsyncValue(StreamProvider 的行为方式)时,它并不能很好地工作。

另外,Flutter Inspector 没有正确更新 ProviderScope 小部件导致了额外的混乱。我必须四处点击并刷新几次才能看到提供商或其状态的更新。无论如何...

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);
  });
  ...

如果您的 UI 在用户退出您的应用程序时不再依赖提供商(允许他们被妥善处理),则此解决方案有效。