Sort Firebase Data from AsyncSnapshot<QuerySnapshot<Object?>> with Dart .sort() Function

383

Solution 1

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!

Solution 2

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.

Share:
383
usafutb0l3r
Author by

usafutb0l3r

Updated on December 06, 2022

Comments

  • usafutb0l3r
    usafutb0l3r over 1 year

    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;
    }
    
  • usafutb0l3r
    usafutb0l3r over 2 years
    I was passing it around just to free up some space in my Widget tree. Also to help with filtering... essentially my order of operations is: 1) Make query with any added filters, 2) Sort the returned query. However, I did not realize it was immutable.... that is a bit concerning and maybe explains why nothing happened with my function. I'll try your recommendation of saving it as a variable and passing that back! It's just all a bit confusing since the snapshot has so much extra info that I don't really need. I have to dig down to the actual list object to get to the relevant fields.