Sorting a Future List in Flutter

1,186

You can move your posts.sort((a, b) => a.id.compareTo(b.id)); inside your Future function, before returning posts. And change the setState, to change the state of a boolean, which sorts or not.

You can change like this:

//define a boolen
bool _isSorted =false;


Future<List<Post>> fetchPosts(bool sortORnot) async {
  List<Post> posts = [];
  final response = await http.get('https://jsonplaceholder.typicode.com/posts');

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    var postsJson = jsonDecode(response.body);
    for (int i = 0; i < postsJson.length; i++) {
      posts.add(Post.fromJson(jsonDecode(response.body)[i]));
    }
    if (sortORnot) {posts.sort((a, b) => a.id.compareTo(b.id));}// this will sort only if you wanted your list sorted.
    return posts;
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load posts');
  }
}

Change your FutureBuilder to this:

FutureBuilder<List<Post>>(
 future:_isSorted? fetchPosts(true):fetchPosts(false),
 builder: (context, snapshot) {

and setState to this:

 setState(() {
  _isSorted = !_isSorted; //this flips the value whenever you press it.
  });

Now, in your future builder, you should get the posts sorted, can you try this?

Share:
1,186
GrandMagus
Author by

GrandMagus

Updated on December 28, 2022

Comments

  • GrandMagus
    GrandMagus over 1 year

    I've been looking for a solution to sort a list (ascending and descending) On Button Press inside of a FutureBuilder, that is a Future<List>, but can't seem to understand how to define it as a List and then sort it on a button press. So I call the API, the API returns some dummy value, it's gets built in the Future Builder and in a ListView.builder, now I want sort the list by id (or by any type for that matter) but the method is not working because the list is null. The code:

    API Call for the dummy data:

    Future<List<Post>> fetchPosts() async {
      List<Post> posts = [];
      final response = await http.get('https://jsonplaceholder.typicode.com/posts');
    
      if (response.statusCode == 200) {
        // If the server did return a 200 OK response,
        // then parse the JSON.
        var postsJson = jsonDecode(response.body);
        for (int i = 0; i < postsJson.length; i++) {
          posts.add(Post.fromJson(jsonDecode(response.body)[i]));
        }
        return posts;
      } else {
        // If the server did not return a 200 OK response,
        // then throw an exception.
        throw Exception('Failed to load posts');
      }
    }
    
    

    The Future Builder:

     List<Post> posts = []; /// if a define it like this, the value is always null
     Future<List<Post>> futurePosts;
    
      @override
      void initState() {
        super.initState();
        futurePosts = fetchPosts();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.white,
          body: SafeArea(
            child: SingleChildScrollView(
              scrollDirection: Axis.vertical,
              child: Column(
                children: [
                  MaterialButton(color: Colors.grey, onPressed: (){
    // here I am setting set to compare the values of all IDs so it can be sorted ascending and descending by number of ID every time I press the button
                    setState(() {
                      posts.sort((a, b) => a.id.compareTo(b.id));
                    });
                  },),
                  Container(
                    height: 1000,
                    child: FutureBuilder<List<Post>>(
                      future: futurePosts,
                      builder: (context, snapshot) {
                        if (snapshot.hasData) {
                          return ListView.builder(
                            shrinkWrap: true,
                            itemCount: snapshot.data.length,
                            itemBuilder: (context, index) {
                              return Text('${snapshot.data[index].id}')
                            },
                          );
                        } else if (snapshot.hasError) {
                          return Text("${snapshot.error}");
                        }
                        return Container();
                      },
                    ),
    

    But it seems my understanding and code is not working for me at this point. Any help is appreciated, thanks in advance!

    • pskink
      pskink about 3 years
      seems you need a StreamBuilder instead of FutureBuilder as you show your data multiple times, in order to trigger the builder: property callback you most likely would need a StreamController.add method
    • GrandMagus
      GrandMagus about 3 years
      Ok, maybe you have a point, but does the sort method stays the same? That it is my question here, sorting, I can try with StreamBuilder...
    • GrandMagus
      GrandMagus about 3 years
      Thanks mate, but The method 'sort' isn't defined for the type 'Future'., since I'm calling an API, it s a Future<List<Post>> and not a List<Post>, what's the workaround that?
    • pskink
      pskink about 3 years
      where you call sort? you need to call it on posts class field
    • GrandMagus
      GrandMagus about 3 years
      When I call the API in initState(), since it is a Future<List<Post>> I have to define it like that Future<List<Post>> list; so in the button where I want to sort, I get The method 'sort' isn't defined for the type 'Future'.
    • GrandMagus
      GrandMagus about 3 years
      Future<List<Posts>> list; var ctrl = StreamController(); @override void initState() { super.initState(); list = fetchPosts(); Future.delayed(Duration(seconds: 3), () => list).then(ctrl.add); }
    • GrandMagus
      GrandMagus about 3 years
    • pskink
      pskink about 3 years
      as you can see fetchPosts is async method but its return value is not used at all
    • GrandMagus
      GrandMagus about 3 years
      Yeah, I see that mate, thanks a lot! Now the question is, how should I implement it on ONE button, should I use a boolean value or not? I actually was close to the solution, but you left the chat, but thanks a lot again!
    • pskink
      pskink about 3 years
      ONE button? i think you could use Switch class, the docs say: "A material design switch. Used to toggle the on/off state of a single setting." - you can also use Checkbox class
    • GrandMagus
      GrandMagus about 3 years
      Yeah, like a toggle button with the sort function, sort descending and ascending on single click. I will try with the Switch and let you know how it goes.
  • GrandMagus
    GrandMagus about 3 years
    I've tried it, but it's not working, when I print out the _isSorted it does change to true and false, but the list stays the same, it is not sorting by ID value.
  • GrandMagus
    GrandMagus about 3 years
    whenever I click the button, sometimes it calls in the API and sometimes it doesn't: I/flutter ( 5048): outside the builder: false I/flutter ( 5048): in the api: true I/flutter ( 5048): outside the builder: true I/flutter ( 5048): outside the builder: false I/flutter ( 5048): in the api: true
  • GrandMagus
    GrandMagus about 3 years
    The problem is that I have a FutureBuilder so if I set future: posts, it returns The argument type 'List<Post>' can't be assigned to the parameter type 'Future<List<Posts>>'., so I'm right at the beginning.. Thanks tho!
  • ChessMax
    ChessMax about 3 years
    Don't use FutureBuilder here.
  • GrandMagus
    GrandMagus about 3 years
    Even without the FutureBuilder it is not working..
  • GrandMagus
    GrandMagus about 3 years
    The posts is not empty but nothing is changing in the list...
  • ChessMax
    ChessMax about 3 years
    I've updated the post. Maybe that would work for you?