From 7b6783a5da1710aa73b3eb4cebefc2b9450a19f3 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 <50428976+WaseemAbbasi22@users.noreply.github.com> Date: Tue, 15 Sep 2026 11:56:00 +0300 Subject: [PATCH] pagination added --- .../asset_tracking/asset_tracking_page.dart | 224 ++++++++++-------- .../asset_tracking_provider.dart | 35 ++- 2 files changed, 149 insertions(+), 110 deletions(-) diff --git a/lib/modules/asset_tracking/asset_tracking_page.dart b/lib/modules/asset_tracking/asset_tracking_page.dart index ce2213b7..fa1ef7be 100644 --- a/lib/modules/asset_tracking/asset_tracking_page.dart +++ b/lib/modules/asset_tracking/asset_tracking_page.dart @@ -1,23 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map_geojson2/flutter_map_geojson2.dart'; - -import 'package:latlong2/latlong.dart'; -import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; -import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/modules/asset_tracking/asset_tracking_provider.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; -import 'package:test_sa/views/widgets/item_views/info_header_widget.dart'; -import 'package:test_sa/views/widgets/item_views/info_text_widget.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; -import 'package:test_sa/views/widgets/requests/request_status.dart'; - import 'asset_tracking_item_view.dart'; import 'map_tracking_page.dart'; @@ -46,10 +36,16 @@ class _AssetTrackingPageState extends State with SingleTicker _animationController.forward(); WidgetsBinding.instance.addPostFrameCallback((_) { assetTrackingProvider.reset(); - assetTrackingProvider.getAssetsFromPenguin(); + getAssetsFromPenguin(); }); } + Future getAssetsFromPenguin({bool loadMore = false}) async { + await assetTrackingProvider.getAssetsFromPenguin( + loadMore: loadMore, + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -118,48 +114,60 @@ class _AssetTrackingPageState extends State with SingleTicker return Consumer(builder: (context, provider, _) { return (provider.assetsList.isEmpty && !provider.isLoading) ? NoDataFound(message: context.translation.noDataFound).center - : ListView.separated( - key: const ValueKey('asset-list_view'), - padding: const EdgeInsets.all(16), - itemCount: provider.isLoading ? 6 : provider.assetsList.length, - separatorBuilder: (listContext, itemIndex) => 12.height, - itemBuilder: (listContext, itemIndex) { - if (provider.isLoading) return const SizedBox().toAssetListShimmer(listContext, provider.isLoading); - - // Calculate staggered animation delay - final delay = itemIndex * 0.08; - final animation = Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation( - parent: _animationController, - curve: Interval( - delay.clamp(0.0, 0.7), - (delay + 0.4).clamp(0.0, 1.0), - curve: Curves.easeOut, + : NotificationListener( + onNotification: (scrollInfo) { + if (!provider.isNextPageLoading && provider.nextPage && scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) { + getAssetsFromPenguin(loadMore: true); + } + return false; + }, + child: ListView.separated( + key: const ValueKey('asset-list_view'), + padding: const EdgeInsets.all(16), + itemCount: provider.isLoading ? 6 : provider.assetsList.length + (provider.isNextPageLoading ? 1 : 0), + separatorBuilder: (listContext, itemIndex) => 12.height, + itemBuilder: (listContext, itemIndex) { + if (provider.isLoading) return const SizedBox().toAssetListShimmer(listContext, provider.isLoading); + if (itemIndex >= provider.assetsList.length) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: Center(child: CircularProgressIndicator()), + ); + } + final delay = itemIndex * 0.08; + final animation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.7), + (delay + 0.4).clamp(0.0, 1.0), + curve: Curves.easeOut, + ), ), - ), - ); - - final slideAnimation = Tween( - begin: const Offset(0.0, 0.15), - end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _animationController, - curve: Interval( - delay.clamp(0.0, 0.7), - (delay + 0.4).clamp(0.0, 1.0), - curve: Curves.easeOutCubic, + ); + + final slideAnimation = Tween( + begin: const Offset(0.0, 0.15), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.7), + (delay + 0.4).clamp(0.0, 1.0), + curve: Curves.easeOutCubic, + ), ), - ), - ); - - return FadeTransition( - opacity: animation, - child: SlideTransition(position: slideAnimation, child: AssetTrackingListItemView(provider.assetsList[itemIndex])).onPress(() { - Navigator.push(context, MaterialPageRoute(builder: (context) => MapTrackingPage(assetTrackingData: provider.assetsList[itemIndex]))); - }), - ); - }, + ); + + return FadeTransition( + opacity: animation, + child: SlideTransition(position: slideAnimation, child: AssetTrackingListItemView(provider.assetsList[itemIndex])).onPress(() { + Navigator.push(context, MaterialPageRoute(builder: (context) => MapTrackingPage(assetTrackingData: provider.assetsList[itemIndex]))); + }), + ); + }, + ), ); }); } @@ -173,56 +181,78 @@ class _AssetTrackingPageState extends State with SingleTicker // // padding: const EdgeInsets.all(16), - GridView.builder( - key: const ValueKey('asset-grid_view'), - padding: const EdgeInsets.all(16), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: .8, - crossAxisSpacing: 12, - mainAxisSpacing: 12, - ), + NotificationListener( + onNotification: (scrollInfo) { + if (!provider.isLoading && + !provider.isNextPageLoading && + provider.nextPage && + scrollInfo.metrics.pixels >= (scrollInfo.metrics.maxScrollExtent - 200)) { + getAssetsFromPenguin(loadMore: true); + } + return false; + }, + child: Column( + children: [ + Expanded( + child: GridView.builder( + key: const ValueKey('asset-grid_view'), + padding: const EdgeInsets.all(16), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: .8, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + ), + + itemCount: provider.isLoading ? 6 : provider.assetsList.length, + // separatorBuilder: (listContext, itemIndex) => 12.height, + + itemBuilder: (listContext, itemIndex) { + if (provider.isLoading) return const SizedBox().toAssetListShimmer(listContext, provider.isLoading); - itemCount: provider.isLoading ? 6 : provider.assetsList.length, - // separatorBuilder: (listContext, itemIndex) => 12.height, - - itemBuilder: (listContext, itemIndex) { - if (provider.isLoading) return const SizedBox().toAssetListShimmer(listContext, provider.isLoading); - - // Calculate staggered animation delay - final delay = itemIndex * 0.08; - final animation = Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation( - parent: _animationController, - curve: Interval( - delay.clamp(0.0, 0.7), - (delay + 0.4).clamp(0.0, 1.0), - curve: Curves.easeOut, + // Calculate staggered animation delay + final delay = itemIndex * 0.08; + final animation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.7), + (delay + 0.4).clamp(0.0, 1.0), + curve: Curves.easeOut, + ), + ), + ); + + final slideAnimation = Tween( + begin: const Offset(0.0, 0.15), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.7), + (delay + 0.4).clamp(0.0, 1.0), + curve: Curves.easeOutCubic, + ), + ), + ); + + return FadeTransition( + opacity: animation, + child: SlideTransition(position: slideAnimation, child: AssetTrackingListItemView(provider.assetsList[itemIndex])).onPress(() { + Navigator.push(context, MaterialPageRoute(builder: (context) => MapTrackingPage(assetTrackingData: provider.assetsList[itemIndex]))); + }), + ); + }, ), ), - ); - - final slideAnimation = Tween( - begin: const Offset(0.0, 0.15), - end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _animationController, - curve: Interval( - delay.clamp(0.0, 0.7), - (delay + 0.4).clamp(0.0, 1.0), - curve: Curves.easeOutCubic, + if (provider.isNextPageLoading) + const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: Center(child: CircularProgressIndicator()), ), - ), - ); - - return FadeTransition( - opacity: animation, - child: SlideTransition(position: slideAnimation, child: AssetTrackingListItemView(provider.assetsList[itemIndex])).onPress(() { - Navigator.push(context, MaterialPageRoute(builder: (context) => MapTrackingPage(assetTrackingData: provider.assetsList[itemIndex]))); - }), - ); - }, + ], + ), ); }); } diff --git a/lib/modules/asset_tracking/asset_tracking_provider.dart b/lib/modules/asset_tracking/asset_tracking_provider.dart index fe99bd86..ae21666c 100644 --- a/lib/modules/asset_tracking/asset_tracking_provider.dart +++ b/lib/modules/asset_tracking/asset_tracking_provider.dart @@ -13,7 +13,7 @@ class AssetTrackingProvider extends ChangeNotifier { bool isNextPageLoading = false; final pageItemNumber = 12; int pageNo = 1; - + bool nextPage = false; List assetsList = []; List assetsOnTheFloorList = []; @@ -30,6 +30,9 @@ class AssetTrackingProvider extends ChangeNotifier { reset() { isLoading = false; + isNextPageLoading = false; + pageNo = 1; + nextPage = false; selectedFloor = null; selectedBuilding = null; selectedSite = null; @@ -57,26 +60,31 @@ class AssetTrackingProvider extends ChangeNotifier { final response = await ApiManager.instance.post(URLs.getAssetsFromPenguin, body: payload); if (response.statusCode >= 200 && response.statusCode < 300) { final Map jsonData = json.decode(response.body); - List list = jsonData["data"]; - assetsList.addAll(list.map((item) => AssetTrackingModel.fromJson(item)).toList()); - if (jsonData["totalRows"] <= assetsList.length) { - // nextPage = true; - } - // if (loadMore) { - // assetInventoryResponse?.assetList?.addAll(newResponse.assetList ?? []); - // assetInventoryResponse?.totalRows = newResponse.totalRows; - // } else { - // assetInventoryResponse = newResponse; + List list = jsonData["data"] ?? []; + List responseAssetList = list.map((item) => AssetTrackingModel.fromJson(item)).toList(); + + // assetsList.addAll(list.map((item) => AssetTrackingModel.fromJson(item)).toList()); + // if (jsonData["totalRows"] <= assetsList.length) { + // nextPage = true; // } - // nextPage = (assetInventoryResponse?.assetList?.length ?? 0) < (assetInventoryResponse?.totalRows ?? 0); + if (loadMore) { + assetsList.addAll(responseAssetList); + // assetInventoryResponse?.totalRows = newResponse.totalRows; + } else { + assetsList = responseAssetList; + // assetInventoryResponse = newResponse; + } + final int totalRows = (jsonData["totalRows"] as int?) ?? assetsList.length; + nextPage = (assetsList.length) < totalRows; } else { - // nextPage = false; + if (loadMore) pageNo -= 1; } isLoading = false; isNextPageLoading = false; notifyListeners(); return response.statusCode; } catch (error) { + if (loadMore) pageNo -= 1; isLoading = false; isNextPageLoading = false; // stateCode = -1; @@ -86,6 +94,7 @@ class AssetTrackingProvider extends ChangeNotifier { } } + Future getAssetsFromPenguinByFloor() async { try { assetsOnTheFloorList.clear();