I have this code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class Scroool extends StatefulWidget {
const Scroool({Key? key}) : super(key: key);
@override
State<Scroool> createState() => _ScrooolState();
}
class _ScrooolState extends State<Scroool> {
List posts = [];
int limit = 20;
bool isLoadingMore = false;
ScrollController controller = ScrollController();
@override
void initState() {
fetchPosts();
controller.addListener(scrolListener);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade300,
body: ListView.builder(
controller: controller,
padding: EdgeInsets.all(20),
itemCount: isLoadingMore ? posts.length 1 : posts.length,
itemBuilder: (context, index) {
if(index < posts.length) {
final post = posts[index];
return Card(
child: ListTile(
leading: CircleAvatar(child: Text("${index 1}"),),
title: Text(post['title'].toString()),
subtitle: Text(post['description']),
),
);
} else {
return Center(child: CircularProgressIndicator(),);
}
},
),
);
}
fetchPosts() async {
final url = Uri.parse("https://dummyjson.com/products?limit=$limit");
final response = await http.get(url);
if(response.statusCode == 200) {
final json = jsonDecode(response.body)['products'] as List;
setState(() {
posts = posts json;
isLoadingMore = false;
});
} else {
print("ERROR");
}
}
scrolListener() async{
//don' call the api if user scroll whil calling the api not finished yet
if(isLoadingMore) return;
if(controller.position.pixels ==
controller.position.maxScrollExtent) {
setState(() {
isLoadingMore = true;
});
limit =5;
await fetchPosts();
setState(() {
isLoadingMore = false;
});
} else {
print("not called");
}
}
}
The problem is when I access the end of the scrolling, add more products, but not only 5 like I have said in the code above, but more? why? and how to fix it?
CodePudding user response:
https://dummyjson.com/products?limit=$limit
retrieves limit
elements each time you call it. You increase that limit by 5 every time you hit the bottom. Then you do posts = posts json
Ie, you are adding those 25 elements you receive from the api to the 20 already existing (giving you 45 elements). Next time you are adding 30 new elements to the 45 already existing and so on and so forth.
Either
- completely replace your posts with the retrieved data
- only append the last 5 elements from your new list to the existing posts
- correctly use the
limit
andskip
parameters docs
I'd suggest to go with the third option. To achieve this there are several options. For instance creating a separate limit
and skip
variable.
class _ScrooolState extends State<Scroool> {
int limit = 20;
int skip = 0;
fetchPosts() async {
final url = Uri.parse("https://dummyjson.com/products?limit=$limit&skip=$skip");
...
}
scrolListener() async{
if(isLoadingMore) return;
if(controller.position.pixels == controller.position.maxScrollExtent) {
setState(() {
isLoadingMore = true;
});
//if the limit is still the initial 20 it will set the skip to 20
//later it will increase the skip by 5
skip = limit;
//for all subsequent requests set the limit to 5 to only retrieve
//5 more elements
limit = 5;
await fetchPosts();
...
}
}
That you are talking about posts
but actually are retrieving products
suggests, in your real code, you are using a different API. Of course chosing an option also depends on the capabilities of the API you actually plan to use. But I can only answer you based on what I see in your question. Thus, if your API supports pagination via skip
and limit
(or similar) use that. Otherwise, use one of the other options I mentioned.