i want to create a page in my app where i view history of previously entered data from firestore according to date.I have a page where i try to fetch data specific to date entered, but it seems to keep returning duplicate data as shown in the image below
I only want to be able to show a date particular date once in this page but i cant seem to do that. here is the code
StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(user?.uid)
.snapshots(),
builder: (context, AsyncSnapshot snapshot) {
print(snapshot.data.docs);
if (snapshot.hasError) {
Get.snackbar(
'About Task',
'User message',
backgroundColor: Colors.white,
snackPosition: SnackPosition.BOTTOM,
titleText: const Text(
'Failed Adding Task',
style: TextStyle(color: Colors.red),
),
messageText: const Text(
'Something went wrong... Try again',
style: TextStyle(color: Colors.red),
),
);
}
if (snapshot.data == null) {
const Center(
child: Text('Add a task/Transaction'),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
final List storeDocs = [];
snapshot.data!.docs.map((DocumentSnapshot document) {
Map a = document.data() as Map<String, dynamic>;
storeDocs.add(a);
a['id'] = document.id;
}).toList();
Calculations.getTotalBalance(storeDocs.asMap());
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
TaskModel task =
TaskModel.fromJson(snapshot.data.docs[index]);
print(Expenses.multipleDates);
return Container(
decoration: BoxDecoration(),
child: Column(
children: [
SizedBox(
height: 25,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 53,
height: 80,
child: Text(
task.date,
style: TextStyle(fontSize: 10),
),
),
],
),
Text(
task.amount,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.green),
),
Column(
children: [
Row(
children: [
Text(
task.amount,
style: const TextStyle(
fontSize: 15,
fontWeight:
FontWeight.w600,
color: Colors.red),
),
],
),
SizedBox(
height: 22,
),
Row(
children: [
GestureDetector(
onTap: () {
_showBottomSheet(
context, task);
},
child: GestureDetector(
onTap: () {
Navigator.pushNamed(
context,
SpreadSheetPage.id,
arguments: Tasks(
firestoreDocID:
task.date,
));
},
child: Text(
'View',
style: TextStyle(
color: Colors.blue),
),
),
),
],
)
],
),
],
),
const Padding(
padding:
EdgeInsets.only(left: 65, top: 8),
child: Divider(
thickness: 0.8,
),
)
],
),
);
});
} else {
return Container();
}
}),
here is what my database looks like
CodePudding user response:
You just need to add a filter on the collection:
.collection('tasks')
.where('date', isEqualTo: selectedDate)
.snapshots()
CodePudding user response:
Based on your feedback, here's what I'd do.
Note how you can't filter the query for duplicate values directly in your query.
There is no way to tell Firestore "get all of the results from this collection, but filter out elements that have a field repeated (or duplicated) in the collection itself". This makes sense since the Firestore SDK performs simple and fast queries through the where
clause. Read the docs for more info.
This means that the filtering is at your expenses, i.e. you have to implement it yourself and you will be charged for all of the reads you do. Note: any workaround to reduce the number of reads (e.g. use the not-in
clause) is hacky, hard to read and maintain and it just doesn't work for a large dataset (not-in
would limit your duplicates to 10, in your case), so embrace it.
Considering these premises one could implement the following brute-force solution that reads all the documents from your firestore collection and filters out the duplicates by exploiting a simple Set
:
Future<List> getAndFilter() async {
final f = FirebaseFirestore.instance;
final data = await f.collection('myCollection').get();
final unfilteredData = [for (final d in data.docs) d.data()];
final seenValues = Set();
final desiredData = unfilteredData.where((currentDoc) {
if (seenValues.contains(currentDoc)) return false;
seenValues.add(currentDoc);
return true;
});
return desiredData.toList();
}