Flutter Provider: Provide 2 streams with one dependent on the other
Asked Answered
J

1

12

I'm using the provider package. In the root of the widget tree I have a multiprovider:

Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        StreamProvider<FirebaseUser>.value(
            value: FirebaseConnection.getAuthenticationStream()),
        StreamProvider<User>.value(
            value: FirebaseConnection.getUserStream(uid: ???))
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        initialRoute: LoginScreen.id,
        onGenerateRoute: RouteGenerator.generateRoute,
      ),
    );
  }

The first StreamProvider provides the logged in user from firebase_auth. The second StreamProvider is supposed to provide additional information to that user (stored in the users collection). The problem is that to get the second stream I need the uid of the FirebaseUser provided with the first stream but I don't know how to access it.

Nesting the StreamProviders didn't work because in the second stream I can only access the (wrong) context of the build method (for Provider.of(context))

Jeane answered 2/1, 2020 at 20:2 Comment(6)
Is FirebaseConnection a class you made? I can't find it in the firebase documentation.Grampositive
Yes, it is the class that contains the functions that return the streamsJeane
How many responses are you expecting to be getting from the first stream? Are you only using it for the second stream?Grampositive
Checking if a user is logged in or not and getting his uid to provide it for the second stream is the only reason I need the first stream (first stream returns null when no user is logged in). If I can get the second stream to also return null when no user is logged in I wouldn’t need the first stream.Jeane
Then maybe instead of supplying the first stream to its own provider, create the stream separately, listen for the first event, and use the event data to create the second stream. Put all this in an async method that returns the stream asynchronously and call it from a FutureBuilder. Once the future completes and the stream is returned, pass it to the provider.Grampositive
Thank you. Your suggestion helped me come up with the solution.Jeane
J
3

The solution is to save the second stream (loggedInUserStream) in the state and change it whenever the first stream (authenticationStream) emits a new value (by listening to it) like in the code below:

class _FloatState extends State<Float> {
  StreamSubscription<FirebaseUser> authenticationStreamSubscription;
  Stream<User> loggedInUserStream;

  StreamSubscription<FirebaseUser> setLoggedInUserStream() {
    authenticationStreamSubscription =
        FirebaseConnection.getAuthenticationStream().listen((firebaseUser) {
      loggedInUserStream =
          FirebaseConnection.getUserStream(uid: firebaseUser?.uid);
    });
  }

  @override
  void initState() {
    super.initState();
    authenticationStreamSubscription = setLoggedInUserStream();
  }

  @override
  void dispose() {
    super.dispose();
    authenticationStreamSubscription.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
      value: loggedInUserStream,
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        initialRoute: LoginScreen.id,
        onGenerateRoute: RouteGenerator.generateRoute,
      ),
    );
  }
}

I only needed the first stream (authenticationStream) to get the second one (loggedInUserStream) so I didn't provide it to the widgets below.

Jeane answered 3/1, 2020 at 11:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.