使用FirebaseStream作为Flutter中另一个Stream的输入?

上下文:我有两个可以正常工作的 Firebase Stream,它们获取 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;

但当然我得到一个错误,因为它返回一个列表,而不是一个流。所以我不知道如何继续......

回答

我听到你的痛苦。我去过那里。你非常接近。让我解释一下我喜欢怎么做。

首先,一些清理:

看起来你没有在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;
        }
      }
    }
  }
}

我希望你喜欢这个方法。如果有什么不是你想要的,请告诉我。我可以做出调整。


以上是使用FirebaseStream作为Flutter中另一个Stream的输入?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>