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

Pagination with Bloc Pattern in Flutter

Pagination First Practical in Flutter

ExpansionPanel with ExpansionPanelList with Complete Collapse Operation in Flutter