Reputation: 742
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!
Upvotes: 2
Views: 2754
Reputation: 2265
Something like this, I think, should work:
List<Post> posts;
@override
void initState() {
super.initState();
fetchPosts().then((items) {
setState(() {
posts = items;
});
});
}
@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: () {
setState(() {
if (posts != null) {
posts = posts.toList();
posts.sort((a, b) => a.id.compareTo(b.id));
}
});
},
),
Container(
height: 1000,
child: (posts != null)
? ListView.builder(
shrinkWrap: true,
itemCount: posts.length,
itemBuilder: (context, index) {
return Text('${posts[index].id}');
},
)
: Container(),
)
]),
),
),
);
}
Your posts
field is always empty because you never assign data to that field. And this is the main problem. Try it out.
Upvotes: 0
Reputation: 12353
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?
Upvotes: 1