ใช้ Firebase Stream เป็นอินพุตสำหรับสตรีมอื่นใน Flutter หรือไม่
บริบท:ฉันมีสตรีม 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-loop ซึ่งมีลักษณะดังนี้:
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>>
ที่สำคัญของแผนที่จะเป็นหมายเลขผู้ใช้ ฉันคิดว่าประเภทนี้มีประโยชน์เพราะคุณไม่ต้องกังวลเกี่ยวกับลำดับของผู้ใช้ที่จะซิงค์กับสตรีม
ประการที่สามเมื่อคุณสร้างสตรีมคุณไม่สามารถย้อนกลับได้ แต่ต้องยอมจำนนจากฟังก์ชัน คุณต้องทำเครื่องหมายฟังก์ชันด้วย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;
}
}
}
}
}
ฉันหวังว่าคุณจะชอบวิธีนี้ โปรดแจ้งให้เราทราบหากมีบางอย่างไม่ใช่สิ่งที่คุณต้องการ ฉันสามารถปรับเปลี่ยนได้