Как изменить прослушиватель Flutter Firebase Stream на основе потока Firebase Auth при использовании Provider для DI?

В моем приложении Flutter Firebase с поставщиком для управления состоянием у меня есть поток для реагирования на FirebaseAuth.instance.authStateChanges() и отдельный поток для прослушивания метаданных, связанных с моим приложением, для вошедших в систему uid, предоставленных 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()))
        ),

      ],
  );

Я думаю, что могу использовать ProxyProvider, чтобы позволить потоку MyUser получить зависимость от потока FirebaseAuth.User, но как только поток MyUser зарегистрирован для этого uid, он кажется неизменным. Как я могу перезагрузить поток Firestore на основе результата FirebaseAuth.User?


comment
Вы пытаетесь перезагрузить при каких-либо изменениях или на основании чего? Я спрашиваю, потому что вам может быть лучше использовать метод userChanges (), чтобы он обновлял поток без необходимости перезагружать его, как указано в firebase.flutter.dev/docs/auth/usage   -  person rsalinas    schedule 08.10.2020


Ответы (2)


Возникла та же проблема, но с базой данных Firebase Realtime. Используя пакет async, я создал StreamGroup. StreamGroup.

Создать StreamGroup: StreamGroup group = StreamGroup();

Создать переменную потока: Stream streamvar;

создал функцию с такой схемой:


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

Создал инициализатор:

где я прослушал: FirebaseAuth.instance.authStateChanges() и добавил его в группу по group.add(), также установил переменную потока и добавил ее в группу: streamvar = functionName(FirebaseAuth.instance.currentUser); group.add(streamvar)

затем создал:

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

})
}


Теперь streamVar всегда содержит ссылку на последний вход потока firestore в group. Поменяйте местами поток из Firestore, и вы сможете прослушивать оба типа изменений Firestore и Auth.
Если я что-то пропустил:


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


person Tndevl    schedule 13.12.2020

Я использовал flatMap() в rxdart, чтобы объединить и объединить пользовательский поток auth и пользовательский поток firestore, чтобы полученный поток слушает изменения как авторизации, так и firestore.

Я написал более подробный ответ с кодом здесь: https://stackoverflow.com/a/66234728/10394353

person Karen    schedule 17.02.2021