Pagination (Second Practical) with Cubit in Flutter

 import "package:flutter/material.dart";

import "package:flutter_bloc/flutter_bloc.dart";
import "package:pagination_practice/Pagination_with_Cubit/cubit/posts_cubit.dart";
import "package:pagination_practice/Pagination_with_Cubit/data/repositories/posts_repository.dart";
import "package:pagination_practice/Pagination_with_Cubit/data/services/posts_service.dart";
import "package:pagination_practice/Pagination_with_Cubit/presentation/posts_screen.dart";

void main() {
runApp(
MyApp(
repository: PostsRepository(PostsService()),
),
);
}

class MyApp extends StatelessWidget {
final PostsRepository repository;

const MyApp({super.key, required this.repository});

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => PostsCubit(repository),
child: MaterialApp(
title: "Pagination",
debugShowCheckedModeBanner: false,
theme: ThemeData(
appBarTheme: const AppBarTheme(
backgroundColor: Colors.grey,
centerTitle: true,
),
),
home: PostView(),
),
);
}
}
import "dart:async";
import "package:flutter/material.dart";
import "package:flutter_bloc/flutter_bloc.dart";
import "package:pagination_practice/Pagination_with_Cubit/cubit/posts_cubit.dart";
import "package:pagination_practice/Pagination_with_Cubit/data/models/post.dart";

class PostView extends StatelessWidget {
PostView({super.key});

final scrollController = ScrollController();

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

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

return Scaffold(
appBar: AppBar(
title: const Text(
"Posts",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
body: _postList(),
);
}

Widget _postList() {
return BlocBuilder<PostsCubit, PostsState>(
builder: (context, state) {
if (state is PostsLoading && state.isFirstFetch) {
return _loadingIndicator();
}

List<Post> posts = [];
bool isLoading = false;

if (state is PostsLoading) {
posts = state.oldPosts;
isLoading = true;
} else if (state is PostsLoaded) {
posts = state.posts;
}

return ListView.separated(
controller: scrollController,
itemBuilder: (context, index) {
if (index < posts.length) {
return _post(posts[index], context);
} else {
Timer(
const Duration(milliseconds: 30),
() {
scrollController
.jumpTo(scrollController.position.maxScrollExtent);
},
);
return _loadingIndicator();
}
},
separatorBuilder: (context, index) {
return Divider(
color: Colors.grey[400],
);
},
itemCount: posts.length + (isLoading ? 1 : 0),
);
},
);
}

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

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

part of 'posts_cubit.dart';

@immutable
sealed class PostsState {}

final class PostsInitial extends PostsState {}

class PostsLoaded extends PostsState {
final List<Post> posts;

PostsLoaded(this.posts);
}

class PostsLoading extends PostsState {
final List<Post> oldPosts;
final bool isFirstFetch;

PostsLoading(this.oldPosts, {required this.isFirstFetch});
}

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
import 'package:pagination_practice/Pagination_with_Cubit/data/models/post.dart';
import 'package:pagination_practice/Pagination_with_Cubit/data/repositories/posts_repository.dart';

part 'posts_state.dart';

class PostsCubit extends Cubit<PostsState> {
PostsCubit(this.repository) : super(PostsInitial());

int page = 1;
final PostsRepository repository;

void loadPosts() {
if (state is PostsLoading) return;

final currentState = state;

var oldPosts = <Post>[];
if (currentState is PostsLoaded) {
oldPosts = currentState.posts;
}

emit(PostsLoading(oldPosts, isFirstFetch: page == 1));

repository.fetchPosts(page).then((newPosts) {
page++;

final posts = (state as PostsLoading).oldPosts;
posts.addAll(newPosts);

emit(PostsLoaded(posts));
});
}
}


class Post {
final String title;
final String body;
final int id;

Post.fromJson(Map json)
: title = json["title"],
body = json["body"],
id = json["id"];
}

import 'package:pagination_practice/Pagination_with_Cubit/data/models/post.dart';
import 'package:pagination_practice/Pagination_with_Cubit/data/services/posts_service.dart';

class PostsRepository {
final PostsService service;

PostsRepository(this.service);

Future<List<Post>> fetchPosts(int page) async {
final posts = await service.fetchPosts(page);
return posts.map(
(e) {
return Post.fromJson(e);
},
).toList();
}
}


import 'dart:convert';

import 'package:http/http.dart';

class PostsService {
static const FETCH_LIMIT = 15;
final baseUrl = "https://jsonplaceholder.typicode.com/posts";

Future<List<dynamic>> fetchPosts(int page) async {
try {
final response =
await get(Uri.parse(baseUrl + "?_limit=$FETCH_LIMIT&_page=$page"));
return jsonDecode(response.body) as List<dynamic>;
} catch (error) {
return [];
}
}
}













 


Comments

Popular posts from this blog

Second GET API Calling with Bloc simple Example in Flutter

Stack Container Scrollable Card widget UI with Custom Widget

Pagination with Bloc Pattern in Flutter