Flutter debounce api call in rxdart vanilla bloc

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)

Leave a Reply

Your email address will not be published. Required fields are marked *