Reputation: 11
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
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