简体   繁体   中英

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

I have been attempting to create streams to Firestore documents using the uid obtained from my auth Provider :

class AuthService {
  ...

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

However, I am struggling to actually create a StreamProvider dependent on the value from the 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));
  });
  ...
}

I also tried using Computed to return the uid or the stream itself but you cannot read a Computed from a Provider (which makes sense in retrospect).

This question is the most relevant on this topic but it is dealing with Provider, not Riverpod.

PS Can a Riverpod tag be created?

Edit:

The answer isn't working quite right. The await for loop is only ever triggering once, whereas a listener catches all events.

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

This code yields the following on login:

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

And then on logout:

LISTENED: null

Where I would expect to see uid: null as well, which would update the stream, but upon any more auth events, only the listener is triggered and no events are caught by the await for loop .

Interestingly, using the flutter inspector, the value emitted by the auth provider never changes, either:

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

persists through login/logout events, which could explain this behavior, but I am not sure what to do to fix it.

Any ideas? I have been stuck on this for a while and can't correct the issue.

The problem is, your provider doesn't create a Stream<User> but a Stream<Stream<User>>

As part of 0.6.0-dev, you can use ref.watch to easily combine streams:

class User {
  ...
  static final provider = StreamProvider((ref) {
    final auth = ref.watch(AuthService.provider);
    return Service.user.stream(auth.uid);
  });
  ...
}

I want to preface this by saying I've only been working with Dart/Flutter for a few months, so feel free to correct anything I say and I will update the answer.

I solved this issue after much trial and error and re-reviewing documentation many times. I guess I made a poor assumption that Providers would update when a Provider they depend on changes.

I found that once a StreamProvider returns (or yields), until it is disposed it will always return the same value it originally did, regardless of dependency changes or events coming from a Stream. This is where Computed is useful but doesn't really work well when you desire to return AsyncValue from your provider (how StreamProvider behaves).

Also, additional confusion was caused by the Flutter Inspector not updating the ProviderScope widget correctly. I have to click around and refresh a few times to see updates to Providers or their state. Anyways...

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

This solution works given that your UI no longer depends on providers (allowing them to be properly disposed) when a user signs out of your app.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM