I am using my rest API to get data in the app I used HTTP I write some code for pagination. the pagination is working good but when my all data get returned it's not show a message to all data is returned else its starts duplicate data from the top again. it's creating the duplicate data again pagination not stopping its returning the same data again and agin
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_web_browser/flutter_web_browser.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:convert';
import "package:http/http.dart" as http;
import 'package:voterlist/Widgets/adhelper.dart';
import 'package:voterlist/Widgets/constant.dart';
import 'package:voterlist/Widgets/skeleton.dart';
import 'package:voterlist/Widgets/widget.dart';
class Jobs extends StatefulWidget {
const Jobs({Key? key}) : super(key: key);
@override
State<Jobs> createState() => _JobsState();
}
class _JobsState extends State<Jobs> {
final adManager = AdManager();
// We will fetch data from this Rest api
final _baseUrl = 'api';
// At the beginning, we fetch the first 20 posts
int _page = 0;
// you can change this value to fetch more or less posts per page (10, 15, 5, etc)
final int _limit = 15;
// There is next page or not
bool _hasNextPage = true;
// Used to display loading indicators when _firstLoad function is running
bool _isFirstLoadRunning = false;
// Used to display loading indicators when _loadMore function is running
bool _isLoadMoreRunning = false;
// This holds the posts fetched from the server
List _posts = [];
// This function will be called when the app launches (see the initState function)
void _firstLoad() async {
setState(() {
_isFirstLoadRunning = true;
});
try {
final res = await http.get(Uri.parse("$_baseUrl?$_page&$_limit"));
setState(() {
_posts = json.decode(res.body);
});
} catch (err) {
if (kDebugMode) {
print('Something went wrong');
}
}
setState(() {
_isFirstLoadRunning = false;
});
}
// This function will be triggered whenver the user scroll
// to near the bottom of the list view
void _loadMore() async {
if (_hasNextPage == true &&
_isFirstLoadRunning == false &&
_isLoadMoreRunning == false &&
_controller.position.extentAfter < 300) {
setState(() {
_isLoadMoreRunning = true; // Display a progress indicator at the bottom
});
_page = 1; // Increase _page by 1
try {
final res = await http.get(Uri.parse("$_baseUrl?$_page&$_limit"));
final List fetchedPosts = json.decode(res.body);
if (fetchedPosts.isNotEmpty) {
if (!fetchedPosts.contains(fetchedPosts)) {
setState(() {
_posts.addAll(fetchedPosts);
});
}
} else {
// This means there is no more data
// and therefore, we will not send another GET request
setState(() {
_hasNextPage = false;
});
}
} catch (err) {
if (kDebugMode) {
print('Something went wrong!');
}
}
setState(() {
_isLoadMoreRunning = false;
});
}
}
// The controller for the ListView
late ScrollController _controller;
@override
void initState() {
super.initState();
_firstLoad();
_controller = ScrollController()..addListener(_loadMore);
adManager.addAds(true, true, true);
adManager.showInterstitial();
}
@override
void dispose() {
_controller.removeListener(_loadMore);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: headerNav(title: "Sarkari Naukri"),
body: _isFirstLoadRunning
? ListView.separated(
itemCount: 5,
itemBuilder: (context, index) => const NewsCardSkelton(),
separatorBuilder: (context, index) =>
const SizedBox(height: defaultPadding),
)
: Column(
children: [
Expanded(
child: ListView.separated(
controller: _controller,
itemCount: _posts.length,
itemBuilder: (_, index) => JobCard(
onPress: () {
FlutterWebBrowser.openWebPage(
url: _posts[index]['moredetils'],
safariVCOptions: const SafariViewControllerOptions(
barCollapsingEnabled: true,
preferredBarTintColor: Colors.green,
preferredControlTintColor: Colors.amber,
dismissButtonStyle:
SafariViewControllerDismissButtonStyle.close,
modalPresentationCapturesStatusBarAppearance: true,
),
);
},
image: _posts[index]['imagelink'],
state: _posts[index]['state'],
title: _posts[index]['title'],
subtitle: _posts[index]['totalpost'].toString(),
),
separatorBuilder: (BuildContext context, int index) {
if ((index 1) % 4 == 0) {
return Container(
width: adManager.getBannerAd()?.size.width.toDouble(),
height:
adManager.getBannerAd()?.size.height.toDouble(),
child: AdWidget(ad: adManager.getBannerAd()!),
);
}
return const SizedBox();
},
),
),
// when the _loadMore function is running
if (_isLoadMoreRunning == true)
const Padding(
padding: EdgeInsets.only(top: 10, bottom: 40),
child: Center(
child: CircularProgressIndicator(),
),
),
// When nothing else to load
if (_hasNextPage == false)
Container(
padding: const EdgeInsets.only(top: 30, bottom: 40),
color: Colors.amber,
child: const Center(
child: Text('No more jobs'),
),
),
],
),
);
}
}
class NewsCardSkelton extends StatelessWidget {
const NewsCardSkelton({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
const Skeleton(height: 120, width: 120),
const SizedBox(width: defaultPadding),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Skeleton(width: 80),
const SizedBox(height: defaultPadding / 2),
const Skeleton(),
const SizedBox(height: defaultPadding / 2),
const Skeleton(),
const SizedBox(height: defaultPadding / 2),
Row(
children: const [
Expanded(
child: Skeleton(),
),
SizedBox(width: defaultPadding),
Expanded(
child: Skeleton(),
),
],
)
],
),
)
],
);
}
}
Thank you.
CodePudding user response:
This code looks good to me!!. Maybe you should clear _posts in loadMore(). For example:
// This function will be triggered whenver the user scroll
// to near the bottom of the list view
void _loadMore() async {
if (_hasNextPage == true &&
_isFirstLoadRunning == false &&
_isLoadMoreRunning == false &&
_controller.position.extentAfter < 300) {
setState(() {
_isLoadMoreRunning = true; // Display a progress indicator at the bottom
});
_page = 1; // Increase _page by 1
try {
final res = await http.get(Uri.parse("$_baseUrl?$_page&$_limit"));
final List fetchedPosts = json.decode(res.body);
if (fetchedPosts.isNotEmpty) {
if (!fetchedPosts.contains(fetchedPosts)) {
setState(() {
// added this line
_posts.clear();
_posts.addAll(fetchedPosts);
});
}
} else {
// This means there is no more data
// and therefore, we will not send another GET request
setState(() {
_hasNextPage = false;
});
}
} catch (err) {
if (kDebugMode) {
print('Something went wrong!');
}
}
setState(() {
_isLoadMoreRunning = false;
});
}
}
CodePudding user response:
I can see your URL pattern look like trying to use GET URL parameters like this $_baseUrl?$_page&$_limit
, I think it supposed to be something like $_baseUrl?page=$_page&limit=$_limit