Search code examples
flutterinfinite-scrollbloc

how to use infinite_scroll_pagination for bloc pattern


I'm currently learning and converting my code to BLoc pattern. Before I'm using flutter_pagewise ^1.2.3 for my infinite scroll using Future<> but I don't know how to use it using bloc or is it compatible with it.

So now I'm trying infinite_scroll_pagination: ^2.3.0 since it says in its docs that it supports Bloc. But I don't understand the example code in the docs for bloc. Can you give me a simple example of how to use it with bloc? I'm currently using flutter_bloc: ^6.1.3.

Here are my bloc script:

class TimeslotViewBloc extends Bloc<TimeslotViewEvent, TimeslotViewState> {
  final GetTimeslotView gettimeslotView;

  TimeslotViewBloc({this.gettimeslotView}) : super(TimeslotViewInitialState());

  @override
  Stream<TimeslotViewState> mapEventToState(
    TimeslotViewEvent event,
  ) async* {
    if (event is GetTimeslotViewEvent) {
      yield TimeslotViewLoadingState();
      final failureOrSuccess = await gettimeslotView(Params(
        id: event.id,
        date: event.date,
      ));
      yield* _eitherLoadedOrErrorState(failureOrSuccess);
    }
  }

  Stream<TimeslotViewState> _eitherLoadedOrErrorState(
    Either<Failure, List<TimeslotViewEntity>> failureOrTrivia,
  ) async* {
    yield failureOrTrivia.fold(
      (failure) => TimeslotViewErrorState(
          message: _mapFailureToMessage(failure), failure: failure),
      (result) => TimeslotViewLoadedState(result),
    );
  }


//Bloc Events----------------------------------------
abstract class TimeslotViewEvent extends Equatable {
  const TimeslotViewEvent();
  @override
  List<Object> get props => [];
}

class GetTimeslotViewEvent extends TimeslotViewEvent {
  final String id;
  final String date;
  final int offset;
  final int limit;

  GetTimeslotViewEvent(
      {this.id,
      this.date,
      this.offset,
      this.limit});
}

//Bloc States----------------------------------------
abstract class TimeslotViewState extends Equatable {
  const TimeslotViewState();
  @override
  List<Object> get props => [];
}

class TimeslotViewLoadingState extends TimeslotViewState {}

class TimeslotViewLoadedState extends TimeslotViewState {
  final List<TimeslotViewEntity> records;

  TimeslotViewLoadedState(this.records);
  @override
  List<Object> get props => [records];
}

UPDATE: Here is the revised code from Davii that works for me

@override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => _timeLotBloc,
      child: BlocListener<TimeslotViewBloc, TimeslotViewState>(
        listener: (context, state) {
          if (state is TimeslotViewLoadedState) {

            //Save record count instead of records list
            totalRecordCount += state.records.length; 
            final _next = 1 + totalRecordCount;

            final isLastPage = state.records.length < PAGE_SIZE;
            if (isLastPage) {
              _pagingController.appendLastPage(state.records);
            } else {
              _pagingController.appendPage(state.records, _next);
            }
          }
          if (state is TimeslotViewErrorState) {
            _pagingController.error = state.error;
          }
          
        },
        //Removed pagedListview from bloc builder
        child: PagedListView<int, TimeslotViewEntity>(
            pagingController: _pagingController,
            builderDelegate: PagedChildBuilderDelegate<TimeslotViewEntity>(
            itemBuilder: (context, time, index) => TimeslotViewEntityListItem(
            character: time,
          ),
        ),
      ),),
      );
  }

Solution

  • class PaginatedList extends StatefulWidget {
      const PaginatedList({Key? key}) : super(key: key);
    
      @override
      _PaginatedListState createState() => _PaginatedListState();
    }
    
    class _PaginatedListState extends State<PaginatedList> {
      //*bloc assuming you use getIt and injectable
      late final _timeLotBloc = getIt<TimeslotViewBloc>();
    
      List<TimeslotViewEntity> records = [];
    
      //*initialize page controller
      final PagingController<int, TimeslotViewEntity> _pagingController =
          PagingController(firstPageKey: 0);
    
      @override
      void initState() {
        super.initState();
    
        //*so at event add list of records
        _pagingController.addPageRequestListener(
          (pageKey) => _timeLotBloc
              .add(GetTimeslotViewEvent(records: records, offset: pageKey,limit: 10)),
        );
      }
    
      @override
      void dispose() {
        super.dispose();
        _timeLotBloc.close();
        _pagingController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return BlocProvider(
          create: (context) => _timeLotBloc,
          child: BlocListener<TimeslotViewBloc, TimeslotViewState>(
            listener: (context, state) {
              if (state is TimeslotViewLoadedState) {
    
                records =state.records;
              
                //forget about existing record
                //about the last page, fetch last page number from 
                //backend
    
                int lastPage = state.lastPage
                final _next = 1 + records.length;
    
                if(_next>lastPage){
                  _pagingController.appendLastPage(records);
                  }
                else{
                   _pagingController.appendPage(records, _next);
                }
                
                
              }
              if (state is TimeslotViewErrorState) {
                _pagingController.error = state.error;
              }
              
            },child: BlocBuilder<TimeslotViewBloc,TimeslotViewState>(
              builder: (context,state)=> PagedListView<int, TimeslotViewEntity>(
                pagingController: _pagingController,
                builderDelegate: PagedChildBuilderDelegate<TimeslotViewEntity>(
                itemBuilder: (context, time, index) => TimeslotViewEntityListItem(
                character: time,
              ),
            ),
          ),),
          ),
        );
      }
    }
    

    now on the bloc event class

    class GetTimeslotViewEvent extends TimeslotViewEvent {
      final String id;
      final String date;
      final int offset;
      final int limit;
      //add this on event
      final List<TimeslotViewEntity> records;
    
      GetTimeslotViewEvent({
        this.id,
        this.date,
        this.offset,
        this.limit,
        required this.records,
      });
    }
    

    on state class

    class TimeslotViewLoadedState extends TimeslotViewState {
      final List<TimeslotViewEntity> records;
      final List<TimeslotViewEntity> existingRecords;
      TimeslotViewLoadedState(this.records, this.existingRecords);
      @override
      List<Object> get props => [records, existingRecords];
    }
    

    and on bloc now

     yield* _eitherLoadedOrErrorState(failureOrSuccess,event);
      Stream<TimeslotViewState> _eitherLoadedOrErrorState(
        Either<Failure, List<TimeslotViewEntity>> failureOrTrivia,
        GetTimeslotViewEvent event,
      ) async* {
        yield failureOrTrivia.fold(
          (failure) => TimeslotViewErrorState(
              message: _mapFailureToMessage(failure), failure: failure),
              //existing records from the event,
          (result) => TimeslotViewLoadedState(result,event.records),
        );
      }
    

    yap this method worked on me