MhmD Yo
MhmD Yo

Reputation: 11

How to implement Pagination in flutter using BLoC cubit?

I'm trying to use Pagination in my Flutter app.

I have A List called movies and I'm storing the data List coming from Api with the current page specified and expose it in a ListView.

And I have a ScrollController and I'm checking if I reached the end of the screen I call again the function with the new data by changing the page number and I add the new List to the old list movies.

But when I reach the end of the screen the new List show up and the old List disappears, Have I miss something? How can I solve this problem

Here's the code

 import 'package:flutter/cupertino.dart';
   import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:movie_app/res/constants/colors.dart';
 import'package:movie_app/ui/pages/home_page/home_cubit/
 home_cubit/top_rated_bloc/top_rated_cubit.dart';

  import '../../../models/movies.dart';
  import '../../bloc_cubit/movies_status.dart';

  class TopRatedMovies extends StatefulWidget {
   const TopRatedMovies({Key? key}) : super(key: key);

  @override
    State<TopRatedMovies> createState() => _TopRatedMoviesState();
   }

  class _TopRatedMoviesState extends State<TopRatedMovies> {
  ScrollController scrollController = ScrollController();
  List<Movies> movies = [];

  @override
  void initState() {
   BlocProvider.of<TopRatedCubit>(context).getTopRatedMovies();

  super.initState();
  }
 @override
 Widget build(BuildContext context) {
return Scaffold(
  backgroundColor: MyColor.primaryColor,
  appBar: AppBar(
    backgroundColor: MyColor.primaryColor,

    title: const Text(
      'TOP RATED MOVIES'
    ),
  ),
  body: BlocConsumer<TopRatedCubit,TopRatedState>(
    listener: (context, state){},
    builder: (context, state){
      var cubit = BlocProvider.of<TopRatedCubit>(context);

      movies = state.topRated;
      
      switch(state.status){
        case MoviesStatus.loading:
          return const Center(child: CircularProgressIndicator());
        case MoviesStatus.initial:
        case MoviesStatus.success:
        case MoviesStatus.failure:
          return ListView.separated(
            controller: scrollController..addListener(() {
              if(scrollController.position.pixels == 
    scrollController.position.maxScrollExtent){
                cubit.currentPage++;
                BlocProvider.of<TopRatedCubit>(context).getTopRatedMovies();
                movies = [...movies, ...state.topRated];
              }
            }),
            itemCount: movies.length,
              itemBuilder: (context, index){
              return Text(
                '${movies[index].title}',style: const TextStyle(color: Colors.white),
              );
              },
              separatorBuilder: (context, index){
              return const SizedBox(height: 40,);
          }
          );
      }
    },

  ),
  );
  }
}





 import 'package:bloc/bloc.dart';
 import 'package:equatable/equatable.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:meta/meta.dart';

 import '../../../../../../models/movies.dart';
 import '../../../../../../repositories/movies_repo.dart';
 import '../../../../../bloc_cubit/movies_status.dart';



 part 'top_rated_state.dart';

  class TopRatedCubit extends Cubit<TopRatedState> {
  TopRatedCubit({required this.moviesRepo}) : super(const TopRatedState());

  final MoviesRepo moviesRepo;
   int currentPage = 1;


  Future<List<Movies>> getTopRatedMovies() async{

emit(
    state.copyWith(
      status: MoviesStatus.loading,
    )
    );

    moviesRepo.getTopRatedMovies(page: currentPage).then((topRatedMovies) {
    emit(
    state.copyWith(
        status: MoviesStatus.success,
      topRated: topRatedMovies
    ),
  );


      }).catchError((error){
  emit(
      state.copyWith(
          status: MoviesStatus.failure,
          exception: error
      )
  );
   }

   );
return [];

  }


   }

Upvotes: 1

Views: 3125

Answers (1)

Eng Mghase
Eng Mghase

Reputation: 834

Flutter using cubit for infinite scroll pagination -Move States

   abstract class MoviesState {}

   class MoviesInitial extends MoviesState {}

   class MoviesLoaded extends MoviesState {
      final List<Movies> movies;
      PostsLoaded(this.movies);
    }

   class MoviesLoading extends MoviesState {
      final List<Movies> oldMovies;
      final bool isFirstFetch;
      MoviesLoading(this.oldMovies, {this.isFirstFetch=false});
    }

--CUBIT

 class MoviesCubit extends Cubit<MoviesState> {
  MoviesCubit(this.moviesRepo) : super(MoviesInitial());

  int curentPage = 1;
   final MoviesRepo moviesRepo;

  void getTopRatedMovies() {
    if (state is MoviesLoading) return;

    final currentState = state;

    var oldMovies = <Movies>[];
    if (currentState is MoviesLoaded) {
      oldMovies = currentState.movies;
    }

    emit(MoviesLoading(oldMovies, isFirstFetch: page == 1));

    moviesRepo.getTopRatedMovies(page:curentPage).then((topRatedMovies) {
      currentPage++;

      final movies = (state as MoviesLoading).oldMovies;
      movies.addAll(topRatedMovies);    

      emit(MoviesLoaded(movies));
    });
  }

}

---FRONTEND

 class TopRatedMovies extends StatelessWidget {

  final scrollController = ScrollController();

  void setupScrollController(context) {
    scrollController.addListener(() {
      if (scrollController.position.atEdge) {
        if (scrollController.position.pixels != 0) {
          BlocProvider.of<MoviesCubit>(context).getTopRatedMovies();
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    setupScrollController(context);
    BlocProvider.of<MoviesCubit>(context).getTopRatedMovies();

    return Scaffold(
      appBar: AppBar(
        title: Text("TOP RATED MOVIES"),
      ),
      body: _moviesList(),
    );
  }

  Widget _moviesList() {
    return BlocBuilder<MoviesCubit, MoviesState>(builder: (context, state) {
      if (state is MoviesLoading && state.isFirstFetch) {
        return _loadingIndicator();
      }

      List<Movies> movies = [];
      bool isLoading = false;

      if (state is MoviesLoading) {
        movies = state.oldMovies;
        isLoading = true;
      } else if (state is MoviesLoaded) {
        movies = state.movies;
      }

      return ListView.separated(
        controller: scrollController,
        itemBuilder: (context, index) {
          if (index < movies.length)
            return _movie(movies[index], context);
          else {
            Timer(Duration(milliseconds: 30), () {
              scrollController
                  .jumpTo(scrollController.position.maxScrollExtent);
            });

            return _loadingIndicator();
          }
        },
        separatorBuilder: (context, index) {
          return Divider(
            color: Colors.grey[400],
          );
        },
        itemCount: movies.length + (isLoading ? 1 : 0),
      );
    });
  }

  Widget _loadingIndicator() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Center(child: CircularProgressIndicator()),
    );
  }

  Widget _movies(Movies movie, BuildContext context) {
    return Container(
      width: MediaQuery.of(context).size.width,
      margin: const EdgeInsets.all(10.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            "${movie.id}. ${movie.title}",
            style: TextStyle(
                fontSize: 18.0,
                color: Colors.black,
                fontWeight: FontWeight.bold),
          ),
          SizedBox(height: 10.0),
          Text(movies.body)
        ],
      ),
    );
  }
}

Upvotes: 1

Related Questions