Issue
I have a vanilla bloc where I am implementing infinite listview. So far I have done this. Here’s my bloc implementation where I am loading data into infinite listview like this.
class MyProductsBloc {
final MyProductsRepo _repository = MyProductsRepo();
final BehaviorSubject<MyProducts> _subject = BehaviorSubject<MyProducts>();
int _page;
getMyProducts() async {
_page = 1;
MyProducts response = await _repository.getProducts(page: 1);
_subject.sink.add(response);
}
getMoreProducts() async {
_page++;
MyProducts response =
await _repository.getProducts(page: _page);
if (response.success) {
_subject.value.maxPage = response.maxPage;
_subject.value.pageNum = response.pageNum;
_subject.value.data.addAll(response.data);
_subject.sink.add(_subject.value);
} else {
_page--;
}
}
dispose() {
_subject?.close();
}
BehaviorSubject<MyProducts> get subject => _subject;
}
And on the UI part I am using streambuilder which has a listview with notificationlistner to listen scrollnotification.
StreamBuilder<MyProducts>(
stream: myProductsBloc.subject.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.error != null &&
snapshot.data.error.length > 0) {
return ShowError(
error: snapshot.data.error,
onTap: () => myProductsBloc.getMyProducts(),
);
} else if (snapshot.data.data.length == 0) {
return EmptyWidget();
}
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent) {
if (snapshot.data.isEnd) {
return false;
} else {
myProductsBloc.getMoreProducts();
}
}
return false;
},
child: ListView(
children: [
ListView.separated(
primary: false,
shrinkWrap: true,
separatorBuilder: (context, i) =>
const SizedBox(height: 10),
padding: EdgeInsets.all(10),
itemCount: snapshot.data.data.length,
itemBuilder: (context, i) {
ProductData productData = snapshot.data.data[i];
return ProductCard(product: productData);
},
),
LoadMoreIndicator(isEnd: snapshot.data.isEnd),
],
),
);
} else if (snapshot.hasError) {
return Center(
child: Text('${snapshot.data.error}'),
);
} else {
return Center(
child: LoadingWidget(text: "Fetching products.."));
}
}),
But when scrolled, api is being called multiple times. Is there any way I can debounce calling api multiple time in this pattern?
Solution
class MyProductsBloc {
var _isLoading = false; <---- add this variable
final MyProductsRepo _repository = MyProductsRepo();
final BehaviorSubject<MyProducts> _subject = BehaviorSubject<MyProducts>();
int _page;
getMyProducts() async {
_page = 1;
MyProducts response = await _repository.getProducts(page: 1);
_subject.sink.add(response);
}
getMoreProducts() async {
if (_isLoading) return;
_isLoading = true;
try {
_page++;
MyProducts response = await _repository.getProducts(page: _page);
if (response.success) {
_subject.value.maxPage = response.maxPage;
_subject.value.pageNum = response.pageNum;
_subject.value.data.addAll(response.data);
_subject.sink.add(_subject.value);
} else {
_page--;
}
} finally { _isLoading = false; }
}
dispose() {
_subject?.close();
}
BehaviorSubject<MyProducts> get subject => _subject;
}
Answered By – Petrus Nguyễn Thái Học
Answer Checked By – Jay B. (FlutterFixes Admin)