I am having trouble sorting data after a Firebase query. In my database, there is a collection of bikes
, and each bike has a condition: Excellent
, Great
, Good
, Fair
, Poor
, or Totaled
. I would like to sort by condition so the start of the list is Excellent
and the bottom is Totaled
. I am then putting this in a ListView
.
My Firebase structure: My Firebase structure
My query: FirebaseFirestore.instance.collection('bikes').snapshots();
I tried following this post's guidance, but to no avail. My very simple test below to sort by title length doesn't seem to sort anything (I'm passing the sorting to a function since sorting is only when the user clicks a button).
AsyncSnapshot<QuerySnapshot<Object?>> sortSnapshot(
AsyncSnapshot<QuerySnapshot<Object?>> snapshot) {
if (sortList == true) {
snapshot.data!.docs.sort((a, b) => a['Name'].length.compareTo(b['Name'].length));
}
return snapshot;
}
CodePudding user response:
I don't think the sorting part is your problem here. The following code sorts the items by length of Name
using the same approach you provided above, and then sorts by condition:
void main() {
final conditions = {
'Excellent': 0,
'Great': 1,
'Good': 2,
'Fair': 3,
'Poor': 4,
'Totaled': 5,
};
final docs = [
{'Name': 'Argon', 'Condition': 'Great'},
{'Name': 'Baum', 'Condition': 'Good'},
{'Name': 'Canyon', 'Condition': 'Excellent'},
{'Name': 'Davidson', 'Condition': 'Totaled'},
{'Name': 'Erickson', 'Condition': 'Fair'},
{'Name': 'Focus', 'Condition': 'Poor'},
];
docs.sort((a, b) => a['Name']!.length.compareTo(b['Name']!.length));
print(docs);
docs.sort((a, b) =>
conditions[a['Condition']]!.compareTo(conditions[b['Condition']]!));
print(docs);
}
I think a more likely explanation for what is happening, is you are not updating the UI by calling setState
or something along those lines. Or maybe your sortList
variable is assigned to false
. Or maybe you aren't correctly registering the function as a callback to the aforementioned button press. There really isn't enough context here to say for certain, but I really don't think your sorting code is the problem here.
Edit. After looking at it a bit more. Why are you passing around and trying to update an AsyncSnapshot
object? The documentation suggests that this class is immutable. I presume calling .data
on this object generates the data every time you call the method, rather than returning a mutable reference (otherwise the class could not possibly be immutable).
Essentially what I think you will need to do is save snapshot.data!.docs
to a variable, and sort and return that, rather than returning back the AsyncSnapshot
.
CodePudding user response:
SOLUTION:
For anyone stumbling across this in the future: mmcdon20's answer was correct. It turns out the AsyncSnapshot is indeed immutable. I didn't get any warnings or errors when trying to sort it (which is sort of annoying), but it makes sense. It couldn't be changed, so it wasn't changing.
Anyways, I ended up modifying my sorting function as follows:
My query: FirebaseFirestore.instance.collection('bikes').snapshots();
Extraction of mutable List to sort:
var snapList = snapshot.data!.docs.toList(); // <-- Mutable List object
var posts = sortSnapshot(snapList); // <-- Function that sorts
My sorting function:
List<QueryDocumentSnapshot<Object?>> sortSnapshot(
List<QueryDocumentSnapshot<Object?>> snapList) {
if (sortString == 'Sort by: Distance') {
// Grab lat/lon
// Do distance calculation via Haversine formula
// Sort by distance
} else if (sortString == 'Sort by: Condition') {
snapList.sort((a, b) => compareCondition(a['Condition'], b['Condition']));
}
return snapList;
}
I haven't implemented the sorting by Distance yet, but you get the idea. For sorting by condition, here's my comparator:
int compareCondition(String a, String b) {
// Order: Excellent, Great, Good, Fair, Poor, Totaled
if (a == b) return 0;
switch (a) {
case "Excellent":
return -1;
case "Great":
if (b == 'Excellent') return 1;
return -1;
case "Good":
if ((b == 'Excellent') || (b == 'Great')) return 1;
return -1;
case "Fair":
if ((b == 'Excellent') || (b == 'Great') || (b == 'Great')) return 1;
return -1;
case "Poor":
if ((b == 'Excellent') ||
(b == 'Great') ||
(b == 'Great') ||
(b == 'Fair')) return 1;
return -1;
case "Totaled":
if ((b == 'Excellent') ||
(b == 'Great') ||
(b == 'Great') ||
(b == 'Fair') ||
(b == 'Poor')) return 1;
return -1;
}
return -1;
}
This properly sorts the list, and I can then use my posts
List object to generate my ListTiles
in my ListView
.
Thanks mmcdon20
and hopes this helps someone out someday!