Flutter and Firebase Add Objects to List - firebase

this is my first question.
Right now my code is working when adding 'displayName' to List:
final usersRef = FirebaseFirestore.instance.collection('users');
List userList = [];
void getUsers() async {
List<String> newUserList = [];
await usersRef.get().then((QuerySnapshot snapshot) {
snapshot.docs.forEach((DocumentSnapshot documentSnapshot) {
Map<String, dynamic> data = documentSnapshot.data();
newUserList.add(data['displayName']);
});
});
setState(() {
userList = newUserList;
});
}
But I want to add 'userList[id]' AND 'userList[displayName]' to list the 'displayName' in DropDown BUT to send(push) the 'id':
DropdownSearch<String>(
...
items: userList,
...
onChanged: (String value) {
Navigator.push(context,MaterialPageRoute(builder: (context) => ProfilePage(profileId: value),
...
Because in DropDown-List Names could be duplicate like John Doe but a List of unique IDs to select makes no sense ... Any help?

The await in await usersRef.get()... is not doing anything, since you're not returning anything from within the callback.
I highly recommend using either async/await or then, but not both.
With then the code would be:
void getUsers() {
List<String> newUserList = [];
usersRef.get().then((QuerySnapshot snapshot) {
snapshot.docs.forEach((DocumentSnapshot documentSnapshot) {
Map<String, dynamic> data = documentSnapshot.data();
newUserList.add(data['displayName']);
});
setState(() {
userList = newUserList;
});
});
}
And with async/await it'd be:
void getUsers() async {
List<String> newUserList = [];
QuerySnapshot snapshot = await usersRef.get();
snapshot.docs.forEach((DocumentSnapshot documentSnapshot) {
Map<String, dynamic> data = documentSnapshot.data();
newUserList.add(data['displayName']);
});
setState(() {
userList = newUserList;
});
}

Related

flutter Why doesn't async and await work?

_getstockList
_getstockList( List<dynamic> nlist) async {
Map<String, dynamic> userdocdata;
var userdata = await firestore.collection('users').doc('NVPjZEAZneKblrubGZSW').get();
userdocdata = userdata.data() as Map<String, dynamic>;
nlist = userdocdata['favorite'];
}
Main Code
Widget build(BuildContext context) {
List<dynamic> list = [];
List<Map<String, dynamic>> stockcardlist = [];
_getstockList(list);
print(list); // output
_getstockInfo(list, stockcardlist);
~~~
}
_getstockInfo
_getstockInfo(List<dynamic> nlist, List<Map<String,dynamic>> stockcardlist){
print(nlist.length); // output
}
Desired result
print(list)
print(nlist.length)
valid value
BUT
result
print(list) = []
print(nlist.length) = 0
please help me i use Future, sync, unawait but i cant solve
It looks like _getStockList doesn't return anything. When you pass it, the original object remains unaffected. You could try to fix that:
Future<List<dynamic>> _getstockList() async {
Map<String, dynamic> userdocdata;
var userdata = await firestore.collection('users').doc('NVPjZEAZneKblrubGZSW').get();
userdocdata = userdata.data() as Map<String, dynamic>;
List<dynamic> nlist = userdocdata['favorite'];
return nlist;
}
Now you need to call this function to fill the list
Widget build(BuildContext context) {
List<Map<String, dynamic>> stockcardlist = [];
List<dynamic> list = await _getstockList(); // DOES NOT WORK!
print(list);
_getstockInfo(list, stockcardlist);
...
}
However, this does not work, since you are not allowed to use await in build, which is not async. To get around this, you can use a FutureBuilder:
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
Widget build(BuildContext context) {
return FutureBuilder<List<dynamic>>(
future: _getstockList(),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
List<dynamic> list = snapshot.data ?? [];
print(list);
return MyAwesomeScreenWithAList(list);
}
);
}
Now if you want to use the results of the first async function in a second one, the easiest will probably be, though not ideal, to use two nested FutureBuilders.

Flutter firebase firestore combine 2 query

In the example below, I want to combine 2 firestore queries, but I could not get it to work.
final List<Who> pagedData =
await _query.get().then((QuerySnapshot snapshot) async {
if (snapshot.docs.isNotEmpty) {
_lastDocument = snapshot.docs.last;
} else {
_lastDocument = null;
}
return snapshot.docs.map((QueryDocumentSnapshot e) async {
final data = e.data() as Map<String, dynamic>;
Whoes w = Whoes.fromMap(e.data());
User u = await _firestore
.collection("user")
.doc(data['s'])
.get()
.then((DocumentSnapshot documentSnapshot) => User.fromMap(
documentSnapshot.data()));
return Who(w, u);
}).toList();
});
When I put await in the user part, things get confused and I couldn't edit it.
What I want to output as a result is List<Who>
What am I missing?
It gives me this error:
The return type 'List<Future<Who>>' isn't a 'Future<List<Who>>', as required by the closure's context.
I solved the problem, I leave it here for anyone who encounters this
final List<Who> pagedData =
await _query.get().then((QuerySnapshot snapshot) async {
if (snapshot.docs.isNotEmpty) {
_lastDocument = snapshot.docs.last;
} else {
_lastDocument = null;
}
Iterable<Future<Who>> futureWho =
snapshot.docs.map((QueryDocumentSnapshot e) async {
final data = e.data() as Map<String, dynamic>;
Whoes w = Whoes.fromMap(e.data());
User u = await _firestore
.collection("user")
.doc(data['s'])
.get()
.then((DocumentSnapshot documentSnapshot) => User.fromMap(
documentSnapshot.data()));
return Who(w, u);
});
Future<List<Who>> listWho = Future.wait(futureWho);
return listWho;
});

How can I loop over an array in firebase?

Im trying to get data from firebase. But im a bit struggling with that heres how it looks now
getusers() async {
var firestore = FirebaseFirestore.instance;
List listOfIds = [];
QuerySnapshot qn= await firestore
.collection('videos')
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
setState(() {
});
});
});
if (!mounted) return;
_allResults =qn.docs;
What I want is get the hashtasg array field and then add it to the qn.doc data in _allresults . But how can I do that ?
Heres my firebase so you can see how it looks
And last step I wanna loop over the howle hashtag array
This is my widget
class Openalldocs extends StatefulWidget {
final TextEditingController searchinginput;
static const route = '/openalldocs';
const Openalldocs({Key key, this.searchinginput}) : super(key: key);
#override
_OpenalldocsState createState() => _OpenalldocsState();
}
class _OpenalldocsState extends State<Openalldocs> {
List _allResults = [];
List _resultsList = [];
Future resultsLoaded;
bool nosuerfound = false;
String searchresult;
#override
void initState() {
super.initState();
widget.searchinginput.addListener(_onsearchChanged);
setState(() {
nosuerfound = true;
});
}
#override
void dispose() {
widget.searchinginput.removeListener(_onsearchChanged());
super.dispose();
}
#override
void didChangeDependencies() {
widget.searchinginput.text;
resultsLoaded = getusers();
super.didChangeDependencies();
}
_onsearchChanged() {
setState(() {
nosuerfound = false;
});
searchResults();
}
searchResults() {
var showResults = [];
if (widget.searchinginput.text != "") {
for (var tripsnapshot in _allResults) {
var title = DatbaseService.instance
.videosfromsnapshot(tripsnapshot)
.hashtag1
.toLowerCase();
var title2 = DatbaseService.instance
.videosfromsnapshot(tripsnapshot)
.hashtag2
.toLowerCase();
var title3 = DatbaseService.instance
.videosfromsnapshot(tripsnapshot)
.hashtag3
.toLowerCase();
if (title.contains(widget.searchinginput.text.toLowerCase()) ||
title2.contains(widget.searchinginput.text.toLowerCase()) ||
title3.contains(widget.searchinginput.text.toLowerCase())) {
setState(() {
nosuerfound = true;
});
showResults.add(tripsnapshot);
}
}
} else {
setState(() {
nosuerfound = true;
});
showResults = List.from(_allResults);
}
setState(() {
_resultsList = showResults;
});
}
getusers() async {
var firestore = FirebaseFirestore.instance;
List listOfIds = [];
QuerySnapshot qn= await firestore
.collection('videos')
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
setState(() {
_allResults.add(doc.data()["hashtag1"]);
});
});
});
if (!mounted) return;
searchResults();
return "Complete";
}
#override
Widget build(BuildContext context) {
final user = Provider.of<Userforid>(context);
if (nosuerfound == true) {
return ListView.builder(
itemCount: _resultsList.length,
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// the AMOUNT is how many hashtags you want to show
for (var i = 0; i < _resultsList.length; i += 1) ...[
// the SizedBox will only exist between the elements in the list
// as before
if (i != 0) SizedBox(height: 6),
// create a builder to allow declaring a variable
Builder(
builder: (context) {
// declare the hashtag variable
final hashtag = 'hashtag${i + 1}';
return InkWell(
onTap: () {
// do something with the hashtag stored in the variable
// this will make it relative to the element in the list
},
child: Column(
children: <Widget>[
// why is there a Column inside another with only one child?
// I would recommend to remove it
Column(
children: [
HighlightedMatchesText(
searchString: widget.searchinginput.text,
// notice how I am using the hashtag variable here
// instead of a constant? ('hashtag1'), by the way
// the for loop will make the hashtag start at 0
// you can change it by increment in the declaration
// `final hashtag = 'hashtag${i+1}'`, if you want
// the existing behavior
content: _resultsList[index][hashtag],
),
],
),
// what is this? if it is to add more space between the items
// in the list, I recommend removing it from here, and add it
// to the first `SizedBox` in the for loop
// in case you do that, the Column that this widget belong
// would also only now contain one widget, so, there is no
// need to have it
SizedBox(height: 3),
],
You are using the Firestore methods correctly, the querySnapshot.docs is an array of all documents in that collection that you are looping through with forEach - You only require further logic on the doc.data().
in this case: push all "hashtag1" to the results
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
setState(() {
_allResults.add(doc.data()["hashtag1"]);
});
});
Update Suggested code block
Future<String> getusers() async {
var firestore = FirebaseFirestore.instance;
List listOfIds = [];
QuerySnapshot qn= await firestore
.collection('videos')
.get();
for (var doc in qn.docs) {
setState(() {
_allResults.add(doc.data()["hashtag1"]);
});
}
});
});
if (!mounted) return "Error loading";
searchResults();
return "Complete";
}
yea check out this. if any error, let me no because i am not on system
List<QueryDocumentSnapshot> _allResults =[]
QuerySnapshot qn = await firestore.collection('videos').get();
if (!mounted) return;
setState(() {
_allResults = qn.docs;
});
UPDATE
This line states that the _resultList is a List of documents, and you want to access all the hashtags from it, because you have the for-loop, which goes until it reaches the length of _resultList, therefore you are getting all the hashtags. If you only want to show the hashtag1, then change this:
content: _resultsList[index].data()[hashtag],
to this:
content: _resultsList[index].data()["hashtag1"],
If you want to have all the documents in this List, use this:
.then((QuerySnapshot querySnapshot) {
_allResults = querySnapshot.docs;
}

Comparing elements Flutter/FirebaseFirestore

i have a problem with getting users, whose emails are in the other user's array 'SeniorList'. It prints me empty array when i have a user with an email from
_seniorList
I'm new to a Firebase so every advice is important.
Here is Firestore DB structure:
https://imgur.com/yrtJ4RZ
https://imgur.com/z3gurUq
And Code i tried:
Future<List<String>> getSeniorList() async {
var _currentUser = FirebaseAuth.instance.currentUser;
List<String> list;
DocumentSnapshot data = await FirebaseFirestore.instance
.collection('users')
.doc(_currentUser!.uid)
.get();
list = List.from(data['SeniorList']);
return list;
}
Future<void> printSeniorNameList() async {
final List<String> _seniorList = await getSeniorList();
print(_seniorList);
final QuerySnapshot result = await FirebaseFirestore.instance
.collection('users')
.where('email', arrayContainsAny: _seniorList)
.get();
final List<DocumentSnapshot> documents = result.docs;
print(documents);
}
PS. If u can tell me how to paste Images in a right way i will be thanksfull!
Solved it this way:
Future<List<String>> getSeniorList() async {
var _currentUser = FirebaseAuth.instance.currentUser;
List<String> list;
DocumentSnapshot data = await FirebaseFirestore.instance
.collection('users')
.doc(_currentUser!.uid)
.get();
list = List.from(data['SeniorList']);
return list;
}
Future<bool> isSeniorAlreadyInTheList(String checkemail) async {
final List<String> _seniorList = await getSeniorList();
if (_seniorList.contains(checkemail)) {
return true;
} else {
print('Email not in a Senior List');
return false;
}
}
Future<void> printSeniorNameWhoseEmailInTheList(String checkemail) async {
bool exists = await isSeniorAlreadyInTheList(checkemail);
Map<String, dynamic>? seniorName;
if (exists) {
var result = await FirebaseFirestore.instance
.collection('users')
.where('email', isEqualTo: checkemail)
.limit(1)
.get();
seniorName = result.docs[0].data();
print(seniorName!['username']);
} else
print('That users email is not in a SeniorList!');
}
Already Works for me.

Firestore: Data are Returned Back without the Right Favorite Items Status True or False

I have a flutter app and I am trying to fetch favorites data from cloud firestore database depending on userId.
The problem is that data are returned back without the right favorite items status true or false.
when I print favoriteData in console, I receive empty map like this: {}
Here is my code and how I tried to achieve this:
Future<void> fetchProducts() async {
final List<Product> loadedProducts = [];
var userId = await getCurrentUser();
final response = await Firestore
.instance
.collection("products")
.getDocuments();
final favoriteResponse = await Firestore.instance
.collection("userFavorites")
.document(userId)
.collection("MyFavorites")
.getDocuments();
final favoriteData = favoriteResponse.documents.asMap();
try{
print(favoriteData.toString());
response.documents.forEach((element) {
loadedProducts.add(Product(
id: element.documentID,
title: element.data['title'],
price: element.data['price'],
isFavorite: favoriteData == null ? false : favoriteData[element.documentID] ?? false,
));
});
_items = loadedProducts;
notifyListeners();
}catch(error){
print(error);
}
}
Here where I call fetchProducts which includes favourited items:
class _ProductsScreenState extends State<ProductsScreen> {
var _showOnlyFavorites = false;
var _isInit = true;
var _isLoading =false;
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies() {
if(_isInit){
setState(() {
_isLoading = true;
});
Provider.of<Products>(context,listen: false).fetchProducts().then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
super.didChangeDependencies();
}
Here is a screenshot of documents I am trying to get their value:

Resources