Get value from one future and use it in another future, with Flutter - flutter

I have a favourites collection saved under a users collection. Each of the favourite documents has one field which contains a product_Id. I want to retrieve this product_id value and use it to query another collection. This second collection holds the actual products documents.
Retrieving all the documents in the favourite collection. What do I do next to get the value of the product_id fields as strings?
getIdsfromUserFavs(userId) async {
var _favData = await _usersCollectionReference
.doc(userId)
.collection('favourites')
.get();
}
This is the second method that is used to query the products collection. This method needs the String value from above in order to successfully make the query.
Future<QuerySnapshot<Object?>> queryFavsCollection(value) async {
var _favedProducts = await _productsCollectionReference
.where('prod_id', isEqualTo: value)
.get();
print(value);
return _favedProducts;
}
I am using a futureBuilder in the UI.
THis is one way I have tried(The problem with this is that I don't get any data returned):
getIdsfromUserFavs(userId) async {
var _favData = await _usersCollectionReference
.doc(userId)
.collection('favourites')
.get();
var allData = _favData.docs.map((doc) => doc.data()).toList();
allData.forEach((element) async {
String value = element['prod_id'];
print(value);
await queryFavsCollection(value);
});
}
Future<QuerySnapshot<Object?>> queryFavsCollection(value) async {
var _favedProducts = await _productsCollectionReference
.where('prod_id', isEqualTo: value)
.get();
print(value);
return _favedProducts;
}
I can see that the above methods print the ids to the console. But the FutureBuilder doesn't receive any data:
I/flutter ( 4628): 3nHHEWuCDXvbhYfT8ljY
I/flutter ( 4628): MptYFV1oXhflDYkdQyIP
I/flutter ( 4628): Fd2ntXyNVmjn0D6mG3RA

Below function will return all data from favourite collection
Future<QuerySnapshot<Map<String, dynamic>>> getIdsfromUserFavs(userId) async {
QuerySnapshot<Map<String, dynamic>> _favData = await _usersCollectionReference
.doc(userId)
.collection('favourites')
.get();
return _favData; // This will return all data of favourite collection
}
After that you can return List of desire data as shown in below function
Future<List<QueryDocumentSnapshot<Map<String, dynamic>>>> queryFavsCollection(userId) async {
// Will store data in this list so at the end we can return this
List<QueryDocumentSnapshot<Map<String, dynamic>>> favData = [];
QuerySnapshot<Map<String, dynamic>> _favData =
await getIdsfromUserFavs(userId);
for (QueryDocumentSnapshot<Map<String, dynamic>> data in _favData.docs) {
String value = data['prod_id'];
QuerySnapshot<Map<String, dynamic>> _fav = await
_productsCollectionReference
.where('prod_id', isEqualTo: value)
.get();
if (_fav.docs.isNotEmpty) {
_fav.docs.forEach((element) {
favData.add(element);
});
}
}
return favData;
}
Now you can use FutureBuilder as shown below
FutureBuilder<List<QueryDocumentSnapshot<Map<String, dynamic>>>>(
future: queryFavsCollection(userId),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading...');
}
return Text('you data');
},
);
For better practice kindly refer this. This is from flutter documentation
"The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted."

Related

type 'Future<Null>' is not a subtype of type 'String' in type cast in flutter and Firebase

I am trying to query list by mentioned location in user document.
1. All_Data is a main Collection in Firebase.
2. User_In is a SubCollection inside a document in a main collection in Firebase.
3. Area is a Field in a Document which stores user Area in String form.
final user_loc = FirebaseFirestore.instance
.collection('All_Data')
.doc(user.uid)
.collection('User_In')
.doc(user.uid)
.get()
.then((value) async {
print(await value.get('Area'));
}) as String;
final Stream<QuerySnapshot> datastream = FirebaseFirestore.instance
.collection('All_Data')
.where("private", isEqualTo: false)
.where("Area", isEqualTo: user_loc)
.snapshots();
Here is What I want...
Every User has their own specific area mentioned in subcollection('User_In').doc(user.uid), I want Streambuilder to show only those documents from the main collection that contains Area is equal to the Area in subcollection('User_In').doc(user.uid).
Here is What I tried...
I have Mentioned the code above, I am trying to insert values to query them in the final stream datastream
Here is What I get...
Error I am Getting is "type 'Future' is not a subtype of type 'String' in type cast".
I am new to it please help me with what needs to be done, Or is it just fundamentally not possible with Firebase.
You can get it like
() async {
final value = await FirebaseFirestore.instance
.collection('All_Data')
.doc(user.uid)
.collection('User_In')
.doc(user.uid)
.get();
final user_loc = value.get('Area') as String?;
}
Edit:
class _FaState extends State<Fa> {
Future<String?> fetchUserloc() async {
final value = await FirebaseFirestore.instance
.collection('All_Data')
.doc(user.uid)
.collection('User_In')
.doc(user.uid)
.get();
final user_loc = value.get('Area') as String?;
return user_loc;
}
Future<Stream<QuerySnapshot>> getMyStream() async {
final user_loc = await fetchUserloc();
return FirebaseFirestore.instance
.collection('All_Data')
.where("private", isEqualTo: false)
.where("Area", isEqualTo: user_loc)
.snapshots();
}
late final userStreamFuture = getMyStream();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
FutureBuilder<Stream<QuerySnapshot>>(
future: userStreamFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
// you will get the steam here
}
return CircularProgressIndicator();
},
)
],
),
);
}
}

Collecting string from Firebase Firestore in flutter

I am creating like system and i want to get likeCount from firebase which i created.
It's collecting it but returns null,
here is my code:
String? getLikecount(tresc) {
String? likeCount;
FirebaseFirestore.instance
.collection('Posty')
.where('Tresc', isEqualTo: tresc)
.get()
.then((value) => value.docs.forEach((element) async {
var id = element.id;
final value = await FirebaseFirestore.instance.collection('Posty').doc(id).get();
likeCount = value.data()!['likeCount'].toString();
print(likeCount);
}));
print(likeCount);
return likeCount;
}
and here is console output:
Data is loaded from Firestore (and most modern cloud APIs) asynchronously, because it may needs to come from the network and we can't block your code (and your users) while waiting for it.
If we change the print statements a bit, and format the code, it'll be much easier to see what's going on:
String? getLikecount(tresc) {
String? likeCount;
FirebaseFirestore.instance
.collection('Posty')
.where('Tresc', isEqualTo: tresc)
.get()
.then((value) => value.docs.forEach((element) async {
var id = element.id;
final value = await FirebaseFirestore.instance
.collection('Posty')
.doc(id)
.get();
likeCount = value.data()!['likeCount'].toString();
print('In then: $likeCount');
}));
print('After then: $likeCount');
return likeCount;
}
If you run this, you'll see it outputs:
After then: null
In then: 0
This is probably not what you expected, but it explains perfectly why you don't get a result. By the time your return likeCount runs, the likeCount = value.data()!['likeCount'].toString() hasn't executed yet.
The solution is always the same: any code that needs the data from the database has to be inside the then handler, be called from there, or be otherwise synchronized.
In Flutter it is most common to use async and await for this. The key thing to realize is that you can't return something now that hasn't been loaded yet. With async/await you function becomes:
Future<String?> getLikecount(tresc) {
String? likeCount;
var value = await FirebaseFirestore.instance
.collection('Posty')
.where('Tresc', isEqualTo: tresc)
.get();
for (var doc in value.docs) {
var id = element.id;
final value = await FirebaseFirestore.instance
.collection('Posty')
.doc(id)
.get();
likeCount = value.data()!['likeCount'].toString();
print('In then: $likeCount');
}));
print('After then: $likeCount');
return likeCount;
}
Now your code returns a Future<String?> so a value that at some point will hold the string. When calling getLikecount you will now need to use then or await to handle the Future, and if you want to show the count in the UI you will have to store it in the State of a StatefulWidget.

How to read Firestore field value in flutter?

I want to get field value.
my code is..
void _checkNumner(String number) async {
final userRef = firestore.collection('users');
var documentSnapshot =
await userRef.where("number", isEqualTo: true).get().then((num) {
QuerySnapshot<Map<String, dynamic>> number = num;
print(number);
print("test");
});
print(documentSnapshot);
}
but my console is
how I get field number?
I want to load number values ​​in all docs.
I'm so beginer. T.T
Please reply from the masters
Thank you
Firebase firestore is a NoSQL, document-oriented database. User Data is stored in documents which are organized into collection , i.e collection contains list of document. In simpler words we can say QuerySnapshot contains/provide group of DocumentSnapshot. more about firestore data model
Collection --> QuerySnapshot --> Group of DocumentSnapshot
Document --> DocumentSnapshot
1) Fetch from collection - QuerySnapshot
Here we'll get list of DocumentSnapshots, we can filter by using where commad
Future<void> checkNumber(int number) async {
final QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('users')
.where("number", isEqualTo: number)
.get();
snapshot.docs.isEmpty
? {
//TODO: your code here
debugPrint("no data found")
}
: {
for (DocumentSnapshot element in snapshot.docs)
{
//TODO: your code here
debugPrint("number is: ${element['number']}"),
debugPrint("name is: ${element['name']}"),
}
};
}
1) Fetch from document - DocumentSnapshot
To fetch data from document we require documentId, and we get a single documentSnapshot instead of multiple like in above way.
Future<void> checkNumberWithDocId() async {
const String docId = 'aaaa';
final DocumentSnapshot snapshot = await FirebaseFirestore.instance.collection('users').doc(docId).get();
snapshot.exists
? {
//TODO: your code here
debugPrint("no data found")
}
: {
//TODO: your code here
debugPrint("number is: ${snapshot['number']}"),
debugPrint("name is: ${snapshot['name']}"),
};
}

How to get firestore data as stream?

I am making a collection group query, where upon matching a particular field, I am going a level backwards and then read the data.
I am able to do that in Future approach.
Here is my code returning future:
#override
Future<Either<JobPostFailure, List<JobPost>>> readAppliedJobPosts({
required String seamanId,
}) async {
final querySnapshot = await FirebaseFirestore.instance
.collectionGroup(ConstStrings.applications)
.where(
ConstStrings.seamanId,
isEqualTo: seamanId,
)
.get();
final List<JobPost> mList = [];
for (var docSnap in querySnapshot.docs) {
final jobPostDocSnap = await docSnap.reference.parent.parent?.get();
mList.add(JobPostDto.fromFirestore(jobPostDocSnap!).toDomain());
}
return right(mList);
}
Now I am struggling to do this in Stream approach, where my return type would be something like this : Stream<Either<JobPostFailure, List<JobPost>>>. What is the equivalent of my above code in Stream?
My try so far :
#override
Stream<Either<JobPostFailure, List<JobPost>>> watchAppliedJobPosts({
required String seamanId,
}) async* {
yield* _firestore
.collectionGroup(ConstStrings.applications)
.where(
ConstStrings.seamanId,
isEqualTo: seamanId,
)
.snapshots()
.map((event) {
return event.docs.map((e) {
return e.reference.parent.parent!.snapshots().map((event) {
return right(JobPostDto.fromFirestore(event).toDomain());
}).toList();
});
});
}
And its a big mess!
You can use method snapshots instead of get. Is will create a new stream that will fetch data for every change your document or collection has

Flutter firestore return Future<List<Data>> returns empty

I have a function that retrieve a List of data as a Future.
final CollectionReference _departmentHoursCollection =
FirebaseFirestore.instance.collection('departmentsHours');
Future<List<DepartmentHour>> isUnfinishedGK(List<String> sites) async {
sites.asMap().forEach((key, value) async {
QuerySnapshot departmentsHoursSnapshot = await _departmentHoursCollection
.where('siteName', isEqualTo: value)
.where('justification', isEqualTo: 'working')
.get();
if (departmentsHoursSnapshot.docs.isNotEmpty) {
departmentsHoursSnapshot.docs.asMap().forEach((key, value) async {
var document = await value.reference.get();
print(document.data());
_temporal.add(DepartmentHour.fromMap(document.data()));
});
}
});
print("size: ${_temporal.length}");
return _temporal;
}
however when i print size at the end, it keeps showing:
I/flutter (20592): size: 0
But the data is being fetched and added to the variable: _temporal:
I/flutter (20592): {globalTime: 24304, endHour: 06:00, startedTime:
Timestamp(seconds=1602329908, nanoseconds=453000000),
departmentHourDetails: [{globalTime: 24300, endJobTime:
Timestamp(seconds=1602354212, nanoseconds=456000000), department:
Grind Room, startJobTime: Timestamp(seconds=1602329911,
nanoseconds=766000000)}, {globalTime: 24299, endJobTime:
Timestamp(seconds=1602354212, nanoseconds=456000000), department: Main
Production, startJobTime: Timestamp(seconds=1602329913,
nanoseconds=94000000)}], startHour: 22:00, scheduleDate:
Timestamp(seconds=1602306000, nanoseconds=0), finishedTime:
Timestamp(seconds=1602329908, nanoseconds=453000000), siteName: SMD,
justification: working}
anyone knows how to return the List with the fetched data instead of returning a [].
To solve the problem you can use Future.forEach(), that waits for each Future to be completed before moving to the next element, as you can see here.
Example:
Future<List<String>> myFuture(List<String> names) async {
List<String> newNames = [];
await Future.forEach(names, (name) async {
await Future.delayed(Duration(seconds: 2), () {
newNames.add(name);
});
});
return newNames;
}