I have some documents stored under players in firebase and all of them have an id field with num value (e.g. 1, 3 or 25). now I only have 30 players but I'm trying to avoid getting all 30 player when I only need some to avoid unnecessary reads. I have a a list that has all the id of players I want and I'm trying to use arrayContainsAny to fix my problem like:
FirebaseFirestore.instance.collection('players').where("id", arrayContainsAny: widget.selectedPlayer);
where widget selected player is list as I said, I also tried using [1, 2] in place of widget.selectedPlayer but all of them returns no documents.
PS: I tried using is equal to and it works fine:
FirebaseFirestore.instance.collection('players').where("id", isEqualTo: 27)
here is my section of code
players = FirebaseFirestore.instance.collection('players').where("id", arrayContainsAny: widget.selectedPlayer);
return StreamBuilder<QuerySnapshot>(
stream: players.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
var sortedPlayers = sortPlayersByPosition(snapshot.data.docs);
debugPrint("player docs: " + sortedPlayers.toString());
return ListView.builder(
padding: EdgeInsets.all(0.0),
itemCount: snapshot.data.docs.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
// return the header
return playerHeaderRow(header);
}
index -= 1;
if (widget.selectedPlayer.contains(sortedPlayers[index]["id"])) {
return buildPlayers(sortedPlayers[index]);
} else {
// need to return a empty widget here but return null stop the loop when we come to a false case
// this will create a empty widget : https://stackoverflow.com/a/55796929
return SizedBox.shrink();
}
},
);
},
);
ok so if you want to use this function "An in query returns documents where the given field matches any of the comparison values."
you are suppose to use wherein and not arraycontainsany, I wish they had a doc somewhere for this
Related
i have problems with my code. i receive only the data of my first query inside the streambuilder if statement. do i need to use a loop ???
With listenStream i receive for example 4 results. 2 of the results have the value in field 'kategorien' = 'Keine'. These 2 fields i need to complete to show in my programm with the results from the query inside the if statement of the streambuilder. i hope i explained a little to understand my targets.
The problem is, that i receive only the first 'kategorie' for the first entry in the database.
can someone help my please?
Here is my code:
StreamBuilder<QuerySnapshot>(
stream: listenStream,
builder: (context,snapshot){
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView.builder(
itemCount:snapshot.data?.docs.length,
itemBuilder: (context,index){
String? falscheKat = snapshot.data?.docs[index]['kategorie'].toString();
if(falscheKat=='Keine')
{
FirebaseFirestore.instance.collection('artikel').where('item', isEqualTo: snapshot.data?.docs[index]['item'].toString())
.get().then((value) {
neueKat = value.docs[0].get("item");
});
}
return ListTile(
title: Text(snapshot.data?.docs[index]['item']),
subtitle: Text(neueKat.toString()),
);
},
);
})
and here my Firestore query listenStream:
final Stream<QuerySnapshot> listenStream = FirebaseFirestore.instance
.collection('listen1')
.doc('zuhause')
.collection('artikel').snapshots();
Use another StreamBuilder for the second Firestore query.
i want to learn flutter step by step, I created UI, now i wanted to create fake firebase, later i will implement real firebase and i will use cubit, and then bloc.
I found this error, and i don't know how to remove it:
"AsyncSnapshot<Object?> snapshot
The argument type 'Object?' can't be assigned to the parameter type 'FirstListModel'."
this is my code before i whas trying to implement fake firebase, as im trying to do now:
itemBuilder: (context, index) {
final oneElement = firstList[index];
return buildPlate(index,await oneElement);
},
this itemBuilder is inside "ReorderableListView". Now im going step further and im trying to implement my fakeFirebase:
itemBuilder: (context, index) {
return FutureBuilder(
future: fakeFirebase.getElement(index.toString()),
builder: (context, snapshot) {
if (snapshot.hasData) {
return buildPlate(index, snapshot.data);
} else {
return const CircularProgressIndicator();
}
},
);
},
in the code above i have error in "snapshot.data"
in order to have better understanding, it is my function from fakeFirebase:
#override
Future<FirstListModel> getElement(String? id) async {
FirstListModel error = FirstListModel(text: "error", id: "404");
if (id != null) {
return firstList[int.parse(id)];
}
return error;
}
and this is my model:
class FirstListModel {
String text;
String id;
FirstListModel({
required this.text,
required this.id,
});
}
this is begining of my "buildPlate":
Widget buildPlate(int index, FirstListModel oneElement) => ListTile(
if i need to post more of my code, fell free to ask, it is my own aplication, just to lear flutter
"firstList" is a list of models, it containes objects that will be added by the user, simulating firebase
Define the type parameter for the future builder and cast the snapshot.data to non null by using the ! operator
return FutureBuilder<FirstListModel>(
future: fakeFirebase.getElement(index.toString()),
builder: (context, snapshot) {
if (snapshot.hasData) {
return buildPlate(index, snapshot.data!);
} else {
return const CircularProgressIndicator();
}
},
);
I am working on an Exercises app.
I am using FIREBASE with a StreamBuilder to retrieve an "exercise id" that has been favorited in my app. This is working fine and I get the correct Id.
I then pass it to a simple method to retrieve the actual "exercise" so that I can display it. This method passes through a List of exercises, checking if the exercise id, is found, and then I want it to return the exercise.
However I am always getting null returned. I can't figure out why this is.
I'd really appreciate some help, as very stuck with it right now.
Here below is my code:
This is the method:
Exercise getExerciseByID(String exerciseId) {
_exercises.forEach((exercise) {
print('COMPARE1: ${exercise.id} AND $exerciseId');
if (exercise.id == exerciseId) {
print('COMPARE2: ${exercise.id} AND ${exerciseId}');
return exercise;
}
});
}
Note that the two print statements both print and show matching id's
And here is the Build with StreamBuilder etc..
return Scaffold(
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('favorites')
.doc('${_firebaseAuth.currentUser.uid}')
.collection('userFavorites')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(),
);
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
//TODO: GET EXERCISE FOR DOC
String _exerciseId = snapshot.data.docs[index].id;
_exercise = getExerciseByID(_exerciseId);
//TODO: DISPLAY CARD
print("Retrieved Exercise: $_exercise");
return Center(child: Text(""));
},
);
},
// padding: EdgeInsets.only(left: sideMargin),
// },
),
);
Note: the print statement: print("Retrieved Exercise: $_exercise"); is where I am always finding the NULL.
Many thanks for any assistance.
change this
Exercise getExerciseByID(String exerciseId) {
_exercises.forEach((exercise) {
print('COMPARE1: ${exercise.id} AND $exerciseId');
if (exercise.id == exerciseId) {
print('COMPARE2: ${exercise.id} AND ${exerciseId}');
return exercise;
}
});
}
to this
Exercise getExerciseByID(String exerciseId) {
Exercise returningExercise;
_exercises.forEach((exercise) {
print('COMPARE1: ${exercise.id} AND $exerciseId');
if (exercise.id == exerciseId) {
print('COMPARE2: ${exercise.id} AND ${exerciseId}');
returningExercise = exercise;
}
});
return returningExercise;
}
You are returning inside ForEach Statement. This doesn't return to your function.
put return statement outside ForEach statement.
Note: This solution is not best. But you now know whats going wrong.
Bad state: field does not exist within the DocumentSnapshotPlatform
The relevant error-causing widget was
StreamBuilder<QuerySnapshot<Object?>>
StreamBuilder:file:///D:/EgoPro/Flutter%20Apps/task_app/lib/screens/task_screen.dart:189:13
this is the error
StreamBuilder<QuerySnapshot>(
// <2> Pass `Stream<QuerySnapshot>` to stream
stream:
FirebaseFirestore.instance.collection('tasks').snapshots(),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
// <3> Retrieve `List<DocumentSnapshot>` from snapshot
final List<DocumentSnapshot> documents = snapshot.data!.docs;
print(documents);
return ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: documents
.map(
(doc) => Meetingcard(
id: doc.get("id"),
title: doc.get("title"),
description: doc.get("description"),
time: TimeOfDay.now()),
)
.toList());
} else if (snapshot.hasError) {
return Text("'It's Error!'");
} else {
return CircularProgressIndicator();
}
},
)
Why am i getting this error ?
This is the image of my documents
enter image description here>
doc.get will return this error if the specified field does not exists in the document. So one of your fields: id, title, description (or more of these) can't be found in doc.
You can add a breakpoint or log and check the result of doc.data() inside your .map((doc)... to see what does it contain.
(One of the possible ways to handle optional fields is to define a model class, create converter where you handle missing values and assign empty string or other default value, so when you read data from your stream you can use this model, and you don't have to handle missing values there.)
EDIT:
Based on the error picture in comment the error seems to be somewhere else, where you assign value to documents. snapshot.data!.docs has the type List<QueryDocumentSnapshot<Object?>> and not `List. Try the following code:
StreamBuilder<QuerySnapshot>(
// <2> Pass `Stream<QuerySnapshot>` to stream
stream:
FirebaseFirestore.instance.collection('tasks').snapshots(),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
// <3> Retrieve `List<DocumentSnapshot>` from snapshot
return ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
children: snapshot.data!.docs
.map(
(DocumentSnapshot doc) => Meetingcard(
id: doc.data()!["id"],
title: doc.data()!["title"],
description: data()!["description"],
time: TimeOfDay.now()),
)
.toList());
} else if (snapshot.hasError) {
return Text("'It's Error!'");
} else {
return CircularProgressIndicator();
}
},
)
I have a table in my sqflite database containing the call history of the respective users. Now on my Call history page in flutter, I am showing the complete history data, fetched from sqflite up till now its working fine. But now I want to check whether the numbers are in my history list exist in contact. If yes, then I want to show their contact name and avatar in the list. Otherwise I just want to show the number. Here's my code:
List<Map<String, dynamic>> ok =
await DatabaseHelper.instance.getAllLogs(argv);
setState(() {
queryRows = ok;
});
var historyRecords = List<HistoryRecord>.from(queryRows.map((row) => HistoryRecord.fromJson(row)));
FutureBuilder<List<HistoryRecord>>(
future: _checkContact(historyRecords),
builder: (context, snapshot) {
return ListView.builder(
itemCount: historyRecords.length,
itemBuilder: (context, index) {
print(historyRecords[index]);
},
);
},
)
Future<List<HistoryRecord>> _checkContact(List<HistoryRecord> rec)async
{
for(int i=0;i<rec.length;i++) {
var conhere=await
ContactsService.getContactsForPhone(rec[i].callHistoryNumber);
//how should i map iterable contact list to Historyrecord
}
}
To call an asynchronous call in UI, you can use FutureBuilder. You can run a check for each and every items in the list like this:
FutureBuilder<bool>(
initialData: false, // You can set initial data or check snapshot.hasData in the builder
future: _checkRecordInContact(queryRow), // Run check for a single queryRow
builder: (context, snapshot) {
if (snapshot.data) { // snapshot.data is what being return from the above async function
// True: Return your UI element with Name and Avatar here for number in Contacts
} else {
// False: Return UI element withouut Name and Avatar
}
},
);
However I don't recommended this method since there would be too many async calls that will slow down the app. What I recommend is to run a check for all items in the queryRows first, then send it to UI.
First of all you should use an Object to represent your history records instead of Map<String, dynamic> to avoid bugs when handling data. Let's say we have a list of HistoryRecord objects, parse from queryRows. Let's call this list historyRecords
var historyRecords = List<HistoryRecord>.from(queryRows.map((row) => HistoryRecord.fromJson(row)));
Each object should have a Boolean property fromContact to check if it's in the Contacts or not. We can then do this:
Widget buildListView(historyRecords) {
return FutureBuilder<List<HistoryRecord>>(
future: _checkContact(historyRecords), // Here you run the check for all queryRows items and assign the fromContact property of each item
builder: (context, snapshot) {
ListView.builder(
itemCount: historyRecords.length,
itemBuilder: (context, index) {
if (historyRecords[index].fromContact) { // Check if the record is in Contacts
// True: Return your UI element with Name and Avatar here
} else {
// False: Return UI element without Name and Avatar
}
},
);
},
);
}
You can then check the contacts with the following property of HistoryRecord and function:
class HistoryRecord {
bool fromContact;
Uint8List avatar;
String name;
//... other properties
HistoryRecord({this.fromContact, this.avatar, this.name});
}
Future<List<HistoryRecord>> _checkContact(List<HistoryRecord> rec) async {
for (int i = 0; i < rec.length; i++) {
Iterable<Contact> conhere =
await ContactsService.getContactsForPhone(rec[i].callHistoryNumber);
if (conhere != null) {
rec[i]
..name = conhere.first.displayName
..avatar = conhere.first.avatar
..fromContact = true;
}
}
return rec;
}
You can use FutureBuilder to check each number like:
ListView.builder(
itemCount: history.length,
itemBuilder: (context, index) {
FutureBuilder(
future: checkContactExists(history[0]),
builder: (context, snap){
if(snap.hasData){
if(snap.data = true){
return PersonContact();
}else{
return JustNumber();
}
}
return Loading();
}
)
},
);