Flutter의 다른 스트림에 대한 입력으로 Firebase 스트림을 사용하십니까?

Dec 31 2020

컨텍스트 : 올바르게 작동하는 두 개의 Firebase 스트림이 있으며, i) 사용자 프로필 목록 ( '사용자'컬렉션), ii) 각 사용자 프로필에 속한 위치 목록 ( '위치'컬렉션)을 가져옵니다. 그런 다음 사용자 지정 사용자 및 위치 모델에 매핑합니다.

사용자 스트림 :

class DatabaseService {

final String uid;
final String friendUid;
final String locationId;
DatabaseService({ this.uid, this.locationId, this.friendUid });

// collection reference for users
final CollectionReference userCollection = FirebaseFirestore.instance.collection('users');

// get users stream
Stream<List<CustomUserModel>> get users {

final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;

List<CustomUserModel> userList = [];

List<CustomUserModel> _streamMapper(DocumentSnapshot snapshot) {
  CustomUserModel individualUser = CustomUserModel(
    uid: snapshot.id,
    name: snapshot.data()['name'],
    username: snapshot.data()['username'],
    email: snapshot.data()['email'],
  );
  userList.add(individualUser);
  return userList;
}

  return userCollection.doc(uid).snapshots().map(_streamMapper);
}

및 위치 스트림 :

  // collection reference for location
   final CollectionReference locationCollection = 
   FirebaseFirestore.instance.collection('locations');

  Stream<List<Location>> get locations {

  final FirebaseAuth auth = FirebaseAuth.instance;
  final User user = auth.currentUser;
  final uid = user.uid;

  List<Location> _locationListFromSnapshot(QuerySnapshot snapshot) {
   List<Location> locationList = [];
    snapshot.docs.forEach((element) {
     Location individualLocation = Location(
       locationId: element.id,
       locationName: element.data()['locationName'],
       city: element.data()['city'],
     );
    locationList.add(individualLocation);
  });
   return locationList;
 }

  return userLocationCollection.doc(uid).collection('locations').snapshots()
   .map(_locationListFromSnapshot);
 }

내가 원하는 것은 모든 사용자의 모든 위치를 출력하는 사용자 정의 스트림을 생성하는 것입니다. 즉, 사용자 스트림을 위치 스트림의 입력으로 사용하는 것입니다.

여기서 어떤 접근 방식이 작동하는지 잘 모르겠습니다. 사용자 스트림을 위치 스트림에 입력 매개 변수로 추가 한 다음 다음과 같은 for 루프를 만드는 것을 고려했습니다.

Stream<List<Location>> allLocations(Stream<List<CustomUserModel>> users) {

final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;

List<Location> locationList = [];

users.forEach((element) {

// append user's locations to empty list
locationList.add(locationCollection.doc(element.first.uid).collection('locations')
.snapshots().map(SOME FUNCTION TO MAP A DOCUMENT SNAPSHOT TO THE CUSTOM LOCATION MODEL)

}

return locationList;

그러나 물론 이것이 스트림이 아닌 목록을 반환하기 때문에 오류가 발생합니다. 그래서 어떻게 진행해야할지 모르겠네요 ...

답변

4 dshukertjr Dec 31 2020 at 07:52

당신의 고통이 들립니다 나는 거기에 있었다. 당신은 꽤 가까웠습니다. 내가 어떻게 하는지를 설명하겠습니다.

우선, 일부 정리 :

allLocations기능 에서 사용하지 않는 것 같아서 삭제했습니다

final FirebaseAuth auth = FirebaseAuth.instance;
final User user = auth.currentUser;
final uid = user.uid;

둘째, 나는에서 함수의 반환 형식을 변경 Stream<List<Location>>하는 Stream<Map<String, List<Location>>지도의 키는 사용자 ID 될 것이다 곳. 사용자가 스트림과 동기화되는 순서에 대해 걱정할 필요가 없기 때문에이 유형이 유용하다고 생각합니다.

셋째, 스트림을 생성 할 때 반환 할 수 없지만 함수에서 양보해야합니다. 또한 함수를 표시해야합니다 async*(*는 오타가 아닙니다).

이를 통해 다음과 같은 allLocations기능 을 사용할 것을 제안합니다 .

class DataService {
  List<Location> convertToLocations(QuerySnapshot snap) {
    // This is the function to convert QuerySnapshot into List<Location>
    return [Location()];
  }

  Stream<Map<String, List<Location>>> allLocations(
      Stream<List<CustomUserModel>> usersStream) async* {
    Map<String, List<Location>> locationsMap = {};

    await for (List<CustomUserModel> users in usersStream) {
      for (CustomUserModel user in users) {
        final Stream<List<Location>> locationsStream = locationCollection
            .doc(user.uid)
            .collection('locations')
            .snapshots()
            .map(convertToLocations);
        await for (List<Location> locations in locationsStream) {
          locationsMap[user.uid] = locations;
          yield locationsMap;
        }
      }
    }
  }
}

이 방법이 마음에 드 셨으면합니다. 원하는 것이 아닌 경우 알려주십시오. 조정할 수 있습니다.