简体   繁体   中英

How to modify Flutter Firebase Stream listener based on Firebase Auth stream when using Provider for DI?

In my Flutter Firebase App with Provider for state management, I have a stream for reacting to FirebaseAuth.instance.authStateChanges() and a separate stream for listening to the my app related metadata for the logged in uid provided by FirebaseAuth .

    return MultiProvider(
      providers: [

        // This returns a stream of firebase user auth events so my app can react to 
        // login, force logout, etc. 
        StreamProvider<fireauth.User>.value(
          value: FirebaseAuth.instance.authStateChanges(),
        ),
        
        // conditional on non-null FirebaseAuth User, I would like to register a Firestore listener 
        // for the provided userId.
        // For security purposes, if the authenticated uid changes, the listener should be dereigstered.
        // After logout, if a different user is logged in, the this stream should listen to that uid's doc.
        StreamProvider<MyUser>.value(
          value: FirebaseFirestore.instance.collection('users')
                   .doc(/* use the userId from firebaseAuth here! */)
                   .snapshots()
                   .map((ds) => MyUser.fromJson(ds.data()))
        ),

      ],
  );

I think I can use ProxyProvider to allow the MyUser stream to take a dependency on the FirebaseAuth.User stream, but once the MyUser stream is registered for this uid , it seems to be immutable. How can I "reload" the Firestore stream based on the result from the FirebaseAuth.User ?

Ran into the same problem but with Firebase Realtime Database. Using the async package I created a StreamGroup . A StreamGroup.

Create StreamGroup: StreamGroup group = StreamGroup();

Create a stream Variable: Stream streamvar;

created function with this scheme:


    Stream<PlaceHolderClass> functionName(User user) async*{
      if (user != null) {
          await for (var x in streamOfFirestoreDocuments) {
            yield PlaceHolderClass();
          }
        } else {
          yield PlaceHolderClass();
        }
    }

Created an initializer:

where I listened to: FirebaseAuth.instance.authStateChanges() and added it to group by group.add() , also set the stream variable and added it to the group: streamvar = functionName(FirebaseAuth.instance.currentUser); group.add(streamvar)

then created:

Stream<FirestoreDocuments> providerFunc(){
  return group.stream.map((event) {
   if(isUserChangetest){
     
     group.remove(streamvar);
     streamvar = newStreamvar;
     group.add(newStreamvar)
     fetch = newFirestoreDocs;
     return fetch;
}else{
 return sth;
}

})
}


Now streamVar always holds a reference to the last firestore stream input to group . Swap stream from Firestore and you can listen to both kind of changes Firestore and Auth.
Incase I missed something:


    class Wallet {
      static final Wallet _singleton = Wallet._internal();
      factory Wallet() {
        return _singleton;
      }
      Wallet._internal();
      Stream coins;
      create() {
        Stream<CrossOver> signin = FirebaseAuth.instance
            .authStateChanges()
            .map((event) => CrossOver(user: event, isnum: false));
        group.add(signin);
        coins = replace(FirebaseAuth.instance.currentUser);
        group.add(coins);
      }
    
      Stream<CrossOver> replace(User user) async* {
        if (user != null) {
          await for (var x in FirebaseDatabase.instance
              .reference()
              .child('users/' + user.uid + '/scans')
              .onValue) {
            yield CrossOver(coins: Coins.load(x.snapshot.value), isnum: true);
          }
        } else {
          yield CrossOver(coins: Coins.load(0), isnum: true);
        }
      }
    
      Stream<Coins> real() {
        return group.stream.map((event) {
          Coins val;
          if (event.isnum == true) {
            print('new money');
            val = event.coins;
          }
          if (event.isnum == false) {
            Stream<CrossOver> nStream = replace(event.user);
            print('new user');
            group.remove(coins);
            coins = nStream;
            group.add(nStream);
            FirebaseDatabase.instance
                .reference()
                .child('users/' + event.user.uid + '/scans')
                .once()
                .then((value) => val = Coins.load(value.value));
          }
          return val;
        });
      }
    
      StreamGroup<CrossOver> group = StreamGroup();
    }
    
    class Coins {
      int money;
      Coins(this.money);
      factory Coins.load(int i) {
        return Coins(i);
      }
    }
    
    class CrossOver {
      bool isnum;
      User user;
      Coins coins;
      CrossOver({this.user, this.coins, this.isnum});
    }


I used flatMap() in rxdart to merge and combine the auth user stream and the firestore user stream so the resulting stream listens to both auth and firestore changes.

I wrote a more detailed answer with the code here: https://stackoverflow.com/a/66234728/10394353

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