I don't understand why the result of my code is an infinite loop.
class _SearchUserState extends State<SearchUser> {
List usernames = [];
#override
Widget build(BuildContext context) {
var invitedBy = widget.snapshot.data()["invitedBy"];
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
invitedBy.forEach((userId) async {
try {
var snap = await _firestore.collection('users').doc(userId).get();
var username = snap.data()!["username"];
print(username);
setState(() {
usernames.add(username);
});
} catch (err) {
print(err);
}
});
Text("invitedBy length ${invitedBy.length}"),
Text("usernames length ${usernames.length}")
For the two last lines of code i get :"invitedBy length 1" , "usernames length + infinite number"
You're calling setState inside your build method. That triggers another build, and setState is called again, and so on...
Related
I am trying to query a User from firebase within another query but for some reason but I can't get the code to work
The function the wont run is await usersRef.doc(uid).get(); and can be found here:
static getUserData(String uid) async {
return await usersRef.doc(uid).get();
}
static DirectMessageListModel getDocData(QueryDocumentSnapshot qdoc, String uid) {
Userdata postUser = Userdata.fromDoc(getUserData(uid));
return DirectMessageListModel.fromDoc(qdoc, postUser);
}
static DirectMessageListModel fromDoc(QueryDocumentSnapshot doc, Userdata altUser) {
return DirectMessageListModel(
doc['chatId'],
doc['lastMsgContent'],
doc['lastMsgType'],
altUser
);
}
parent function:
Stream<List<DirectMessageListModel>> getMeassageList(){
var snaps = FirebaseFirestore.instance.collection('directMessages').where('users', arrayContains: userdata!.uid).snapshots();
List<String> usersListElement = [];
return snaps.map((event) { return event.docs.map((e) {
usersListElement = [e.get('users')[0], e.get('users')[1]];
usersListElement.remove(userdata!.uid);
return DirectMessageListModel.getDocData(e, usersListElement.first);
}).toList();
});
}
You forgot to wait for the future getUserData(uid) to complete.
Try this:
static Future<DocumentSnapshot<Object>> getUserData(String uid) async {
return await usersRef.doc(uid).get();
}
static DirectMessageListModel getDocData(
QueryDocumentSnapshot qdoc,
String uid,
) async {
Userdata postUser = Userdata.fromDoc(await getUserData(uid)); // await here
return DirectMessageListModel.fromDoc(qdoc, postUser);
}
..
// parent function.
// Also wait for the future in the parent function.
// UPDATE BELOW! Define the parent function like this:
Stream<List<Future<DirectMessageListModel>>> getMeassageList() {
var snaps = FirebaseFirestore.instance
.collection('directMessages')
.where('users', arrayContains: userdata!.uid)
.snapshots();
List<String> usersListElement = [];
return snaps.map((event) {
return event.docs.map((e) async {
usersListElement = [e.get('users')[0], e.get('users')[1]];
usersListElement.remove(userdata!.uid);
return await DirectMessageListModel.getDocData(e, usersListElement.first);
}).toList();
});
}
NB: You are fetching user data (either sender/receiver) for each message in directMessages collection. It might be better to store just sender/receiver name in directMessages collection and simply display that. Then if the user clicks on a message, you can then fetch the full sender/receiver data.
I want to get Products added to cart by user.So i am trying to get documents based on uid(Documents named as uid)
But i am getting error
Only static members can be accessed in initializer
Code:
class CartDataBase{
FirebaseUser user;
final FirebaseAuth _auth=FirebaseAuth.instance;
void getUid()async
{
user=await _auth.currentUser();
}
final CollectionReference cart1 = Firestore.instance.collection('users').document(user.uid)//Only static members can be accessed in initializer
.collection('CartProducts');
List<Checkout> _checkout(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Checkout(
original: doc.data['original'] ?? 999,
image: doc.data['image'] ?? 'Error',
name: doc.data['name'] ?? 'Error',
quantity: doc.data['Quantity'] ?? 'N/A',
identifier: doc.data['identifier'] ?? 'qwerty',
price: doc.data['price'] ?? 999,
iPP: doc.data['IPP'] ?? 999,
uPQ: doc.data['UPQ'] ?? 999,
);
}).toList();
}
Stream<List<Checkout>> get CartProducts {
return cart1.snapshots().map(_checkout);
}
}
Screenshot for reference
The problem in your code can be found here:
final CollectionReference cart1 = Firestore.instance.collection('users').document(user.uid).collection('CartProducts');
There is no way you can assign that to the variable without waiting for it to finish fetching the document. You have to call await method first before you access it. We can solve this with the await keyword
final CollectionReference cart1 = await Firestore.instance.collection('users').document(user.uid).collection('CartProducts');
Since you cannot call async in your class, you have to convert it into a function as well as call that function in initstate() which gets called before your ui builds.
getDetails() async {
final CollectionReference cart1 = await Firestore.instance.collection('users').document(user.uid).collection('CartProducts');
//other things here
}
//place this below your widget build
#override
void initState() {
getDetails();
super.initState();
}
I'm uploading multiple images to Cloud Storage, I've read I can tap into the stream to display an upload progress bar however I can't see a way to do this.
Future<dynamic> postImage(Asset imageFile) async {
String fileName = DateTime.now().toString();
StorageReference reference = FirebaseStorage.instance.ref().child(fileName);
StorageUploadTask _uploadTask =
reference.putData((await imageFile.getByteData()).buffer.asUint8List());
StorageTaskSnapshot storageTaskSnapshot = await _uploadTask.onComplete;
return storageTaskSnapshot.ref.getDownloadURL();
}
There's an example of how to use the firebase-storage plugin in the FlutterFire repo, that is pretty handy.
That example pretty much performs calls on the StorageUploadTask task that you have, to determine the upload state and progress.
String get status {
String result;
if (task.isComplete) {
if (task.isSuccessful) {
result = 'Complete';
} else if (task.isCanceled) {
result = 'Canceled';
} else {
result = 'Failed ERROR: ${task.lastSnapshot.error}';
}
} else if (task.isInProgress) {
result = 'Uploading';
} else if (task.isPaused) {
result = 'Paused';
}
return result;
}
For determining progress, it uses:
String _bytesTransferred(StorageTaskSnapshot snapshot) {
return '${snapshot.bytesTransferred}/${snapshot.totalByteCount}';
}
Which is then used in the build() method like this:
#override
Widget build(BuildContext context) {
return StreamBuilder<StorageTaskEvent>(
stream: task.events,
builder: (BuildContext context,
AsyncSnapshot<StorageTaskEvent> asyncSnapshot) {
Widget subtitle;
if (asyncSnapshot.hasData) {
final StorageTaskEvent event = asyncSnapshot.data;
final StorageTaskSnapshot snapshot = event.snapshot;
subtitle = Text('$status: ${_bytesTransferred(snapshot)} bytes sent');
} else {
subtitle = const Text('Starting...');
}
So this takes the events stream of the task and gets the progress information from there.
I have two collections in my Firestore database.
From first collection('schools'), I need to get current user school. I did it and it is Ok. But I also need to use this school and get array of his school's available courses.
This array is situated in collection('students'). And as document I use schoolNameString which I took in previous collection. So in document(schoolNameString).data['available'], available is array of swings and I need
setState(){
myarrayvar = document(schoolNameString).data['available']
}
I can not do that and always see
error /flutter (25070): Tried calling: []("available")
This is my code:
class _Coirses_PageState extends State<Coirses_Page> with TickerProviderStateMixin
{
final _firesore = Firestore.instance;
String appBarName;
String studentSchoolString = 'mal';
String aseetRoadforCorcs;
List availableCorsces = [];
#override
void initState() {
FirebaseUser user;
super.initState();
getCurrentUser();
getCouses();
try {
var studentSchool = _firesore.collection('schools');
var docSchool = studentSchool.document(loggedInUser.uid);
docSchool.get().then((documents) {
setState(() {
studentSchoolString = documents.data['schoolName'];
print(studentSchoolString);
});
print(studentSchoolString);
});
} catch (e) {
print(e);
}
tabs = [
Tab(text: 'Barlyǵy',),
Tab(text: 'Oqyp jatyrmyn',),
Tab(text: 'Aıaqtadym',)
];
pages = [Progress_Bar()];
_controller = TabController(length: tabs.length, vsync: this);
}
Future<List<dynamic>> getCouses() async {
var document = Firestore.instance.collection("students").document(studentSchoolString). get();
return await document.then((doc) {
setState(() {
availableCorsces = doc.data['available'];
});
print('dsasdasdjnasndklasndasdkasld' + doc.data['available']);
return availableCorsces;
});
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
}
I expected that I can use it in my UI like Text: ('available[0]' ......)
But I can't.
Im trying to return the length of a list of documents with this function:
Future totalLikes(postID) async {
var respectsQuery = Firestore.instance
.collection('respects')
.where('postID', isEqualTo: postID);
respectsQuery.getDocuments().then((data) {
var totalEquals = data.documents.length;
return totalEquals;
});
}
I'm initialize this in the void init state (with another function call:
void initState() {
totalLikes(postID).then((result) {
setState(() {
_totalRespects = result;
});
});
}
However, when this runs, it initially returns a null value since it doesn't have time to to fully complete. I have tried to out an "await" before the Firestore call within the Future function but get the compile error of "Await only futures."
Can anyone help me understand how I can wait for this function to fully return a non-null value before setting the state of "_totalRespsects"?
Thanks!
I think you're looking for this:
Future totalLikes(postID) async {
var respectsQuery = Firestore.instance
.collection('respects')
.where('postID', isEqualTo: postID);
var querySnapshot = await respectsQuery.getDocuments();
var totalEquals = querySnapshot.documents.length;
return totalEquals;
}
Note that this loads all documents, just to determine the number of documents, which is incredibly wasteful (especially as you get more documents). Consider keeping a document where you maintain the count as a field, so that you only have to read a single document to get the count. See aggregation queries and distributed counters in the Firestore documentation.
Perfect code for your problem:
int? total;
getLength() async {
var getDocuments = await DatabaseHelper.registerUserCollection
.where("register", isEqualTo: "yes")
.get();
setState(() {
total = getDocuments.docs.length;
});
}
#override
void initState() {
super.initState();
getLength();
if (kDebugMode) {
print(total);
}
}