Merge pull request 'changed pagination to selectPage in t d yourTenders' (#124) from pagination_scroll_to_select_page into main

Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/124
This commit is contained in:
Yashar Panahi
2025-09-14 08:09:51 +03:30
7 changed files with 321 additions and 85 deletions
+68 -38
View File
@@ -19,6 +19,7 @@ class YourTendersViewModel with ChangeNotifier {
String? errorMessage; String? errorMessage;
TenderApprovalsResponse? data; TenderApprovalsResponse? data;
LikedTendersResponse? likedTendersData; LikedTendersResponse? likedTendersData;
// Pagination state for feedback lists // Pagination state for feedback lists
bool isLoadingMore = false; bool isLoadingMore = false;
bool hasMore = true; bool hasMore = true;
@@ -31,6 +32,7 @@ class YourTendersViewModel with ChangeNotifier {
bool hasMoreTenders = true; bool hasMoreTenders = true;
int _tendersOffset = 0; int _tendersOffset = 0;
final int _tendersLimit = 10; final int _tendersLimit = 10;
// Filter States // Filter States
DateTimeRange? selectedDateRange; DateTimeRange? selectedDateRange;
String selectedStatus = 'All'; String selectedStatus = 'All';
@@ -118,11 +120,12 @@ class YourTendersViewModel with ChangeNotifier {
required String feedbackType, required String feedbackType,
bool reset = false, bool reset = false,
}) async { }) async {
if (reset) { if (reset) {
_offset = 0; _offset = (currentPage - 1) * _limit;
hasMore = true; hasMore = true;
_currentFeedbackType = feedbackType; _currentFeedbackType = feedbackType;
} }
if (!reset && (isLoadingMore || !hasMore)) { if (!reset && (isLoadingMore || !hasMore)) {
return; return;
@@ -258,39 +261,66 @@ class YourTendersViewModel with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<void> callWithFilter() async { Future<void> callWithFilter() async {
// Clear data before applying filter likedTendersData = null;
_clearAllData(); data = null;
isLoading = true;
notifyListeners();
// Make fresh request based on selected status if (selectedStatus == YourTendersStrings.all) {
if (selectedStatus == YourTendersStrings.all) { final requestModel = GetTendersRequestModel(
final requestModel = GetTendersRequestModel( status: '',
status: '', createdFrom: startDateUnix,
createdFrom: startDateUnix, createdTo: endDateUnix,
createdTo: endDateUnix, limit: _tendersLimit,
limit: _tendersLimit, offset: _tendersOffset,
offset: 0, );
); await getTenders(requestModel: requestModel, reset: true);
await getTenders(requestModel: requestModel, reset: true); } else if (selectedStatus == YourTendersStrings.tendersSubmitted) {
} else if (selectedStatus == YourTendersStrings.tendersSubmitted) { final requestModel = GetTendersRequestModel(
final requestModel = GetTendersRequestModel( status: TenderApprovalStatus.submitted.value,
status: TenderApprovalStatus.submitted.value, createdFrom: startDateUnix,
createdFrom: startDateUnix, createdTo: endDateUnix,
createdTo: endDateUnix, limit: _tendersLimit,
limit: _tendersLimit, offset: _tendersOffset,
offset: 0, );
); await getTenders(requestModel: requestModel, reset: true);
await getTenders(requestModel: requestModel, reset: true); } else if (selectedStatus == YourTendersStrings.likeTenders) {
} else if (selectedStatus == YourTendersStrings.likeTenders) { await getTendersFeedback(
await getTendersFeedback( feedbackType: TenderFeedback.liked.value,
feedbackType: TenderFeedback.liked.value, reset: true,
reset: true, );
); } else if (selectedStatus == YourTendersStrings.dislikeTenders) {
} else if (selectedStatus == YourTendersStrings.dislikeTenders) { await getTendersFeedback(
await getTendersFeedback( feedbackType: TenderFeedback.disliked.value,
feedbackType: TenderFeedback.disliked.value, reset: true,
reset: true, );
); }
} }
int currentPage = 1;
int get totalPages {
if (likedTendersData?.data?.meta?.pages != null) {
return likedTendersData!.data!.meta!.pages!;
}
if (data?.data?.metadata?.pages != null) {
return data!.data!.metadata!.pages!;
}
return 1;
}
Future<void> jumpToPage(int page) async {
currentPage = page;
_offset = (page - 1) * _limit;
_tendersOffset = (page - 1) * _tendersLimit;
likedTendersData = null;
data = null;
hasMore = true;
hasMoreTenders = true;
await callWithFilter();
} }
} }
+1 -2
View File
@@ -128,8 +128,7 @@ class TabletHomePage extends StatelessWidget {
width: double.infinity, width: double.infinity,
height: 148, height: 148,
onTap: () { onTap: () {
//YourTendersRouteData().push(context); YourTendersRouteData().push(context);
NotificationRouteData().push(context);
}, },
), ),
), ),
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tm_app/core/constants/tender_feedback.dart'; import 'package:tm_app/core/constants/tender_feedback.dart';
import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart'; import 'package:tm_app/view_models/your_tenders_view_model.dart';
import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart'; import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart';
import 'package:tm_app/views/your_tenders/widgets/filter_button.dart'; import 'package:tm_app/views/your_tenders/widgets/filter_button.dart';
@@ -38,26 +39,25 @@ class _YourTendersDesktopPageState extends State<YourTendersDesktopPage>
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.backgroundColor, backgroundColor: AppColors.backgroundColor,
body: Column( body: Column(
children: [ children: [
DesktopNavigationWidget( DesktopNavigationWidget(currentIndex: 1), // Tenders index
currentIndex: 1, // Tenders index
),
Expanded( Expanded(
child: SizedBox( child: SizedBox(
width: 740, width: 740,
child: Column( child: Column(
children: [ children: [
SizedBox(height: 64.0), SizedBox(height: 64.0.h()),
TabletDesktopAppbar(), const TabletDesktopAppbar(),
SizedBox(height: 20.0), SizedBox(height: 20.0.h()),
FilterButton(platformType: PlatformType.tabletDesktop), const FilterButton(platformType: PlatformType.tabletDesktop),
Expanded( Expanded(
child: Consumer<YourTendersViewModel>( child: Consumer<YourTendersViewModel>(
builder: (context, viewModel, child) { builder: (context, viewModel, child) {
if (viewModel.isLoading) { if (viewModel.isLoading &&
(viewModel.data == null &&
viewModel.likedTendersData == null)) {
return const Center( return const Center(
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: AppColors.secondary50, color: AppColors.secondary50,
@@ -69,32 +69,140 @@ class _YourTendersDesktopPageState extends State<YourTendersDesktopPage>
return Center(child: Text(viewModel.errorMessage!)); return Center(child: Text(viewModel.errorMessage!));
} }
Widget content;
if (viewModel.selectedStatus == if (viewModel.selectedStatus ==
YourTendersStrings.likeTenders && YourTendersStrings.likeTenders &&
viewModel.likedTendersData != null) { viewModel.likedTendersData != null) {
return LikedDislikedTendersList( content = LikedDislikedTendersList(
data: viewModel.likedTendersData!, data: viewModel.likedTendersData!,
status: TenderFeedback.liked.value, status: TenderFeedback.liked.value,
isDesctop: true, isDesktop: true,
); );
} else if (viewModel.selectedStatus == } else if (viewModel.selectedStatus ==
YourTendersStrings.dislikeTenders && YourTendersStrings.dislikeTenders &&
viewModel.likedTendersData != null) { viewModel.likedTendersData != null) {
return LikedDislikedTendersList( content = LikedDislikedTendersList(
data: viewModel.likedTendersData!, data: viewModel.likedTendersData!,
status: TenderFeedback.disliked.value, status: TenderFeedback.disliked.value,
isDesctop: true,
);
} else if (viewModel.data != null) {
return TendersSubmitted(
approvedTenders: viewModel.data!,
isDesktop: true, isDesktop: true,
); );
} else if (viewModel.data != null) {
content = TendersSubmitted(
approvedTenders: viewModel.data!,
isDesktop: true,
isMobile: false,
);
} else { } else {
return const Center( content = const Center(
child: Text(YourTendersStrings.noTenders), child: Text(YourTendersStrings.noTenders),
); );
} }
return Column(
children: [
Expanded(child: content),
SizedBox(height: 10.0.h()),
Row(
children: [
const Spacer(),
Text(
'Page',
style: TextStyle(
fontSize: 13.0.sp(),
fontWeight: FontWeight.w300,
color: AppColors.grey60,
),
),
SizedBox(width: 10.0.w()),
InkWell(
onTap: () async {
final selectedPage = await showDialog<int>(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor:
AppColors.backgroundColor,
title: const Text('Select Page'),
content: SizedBox(
width: 200.0.w(),
height: 300.0.h(),
child: ListView.builder(
itemCount: viewModel.totalPages,
itemBuilder: (context, index) {
final pageNumber = index + 1;
return ListTile(
title: Text(
'Page $pageNumber',
),
onTap:
() => Navigator.pop(
context,
pageNumber,
),
);
},
),
),
);
},
);
if (selectedPage != null) {
await viewModel.jumpToPage(selectedPage);
}
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: AppColors.grey30,
width: 1,
),
borderRadius: BorderRadius.circular(4),
),
child: Row(
children: [
SizedBox(width: 5.0.w()),
Text(
'${viewModel.currentPage}',
style: TextStyle(
fontSize: 14.0.sp(),
fontWeight: FontWeight.w500,
color: AppColors.grey70,
),
),
SizedBox(width: 5.0.w()),
const Icon(Icons.arrow_drop_down),
SizedBox(width: 5.0.w()),
],
),
),
),
SizedBox(width: 5.0.w()),
Text(
'of',
style: TextStyle(
fontSize: 14.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey60,
),
),
SizedBox(width: 5.0.w()),
Text(
'${viewModel.totalPages}',
style: TextStyle(
fontSize: 13.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey60,
),
),
],
),
SizedBox(height: 30.0.h()),
],
);
}, },
), ),
), ),
@@ -68,7 +68,7 @@ class _YourTendersMobilePageState extends State<YourTendersMobilePage> {
child: LikedDislikedTendersList( child: LikedDislikedTendersList(
data: viewModel.likedTendersData!, data: viewModel.likedTendersData!,
status: TenderFeedback.liked.value, status: TenderFeedback.liked.value,
isDesctop: false, isDesktop: false,
), ),
) )
else if (viewModel.selectedStatus == else if (viewModel.selectedStatus ==
@@ -78,7 +78,7 @@ class _YourTendersMobilePageState extends State<YourTendersMobilePage> {
child: LikedDislikedTendersList( child: LikedDislikedTendersList(
data: viewModel.likedTendersData!, data: viewModel.likedTendersData!,
status: TenderFeedback.disliked.value, status: TenderFeedback.disliked.value,
isDesctop: false, isDesktop: false,
), ),
) )
else if (viewModel.data != null) else if (viewModel.data != null)
@@ -86,6 +86,7 @@ class _YourTendersMobilePageState extends State<YourTendersMobilePage> {
child: TendersSubmitted( child: TendersSubmitted(
approvedTenders: viewModel.data!, approvedTenders: viewModel.data!,
isDesktop: false, isDesktop: false,
isMobile: true,
), ),
) )
else else
@@ -7,7 +7,6 @@ import 'package:tm_app/views/your_tenders/widgets/filter_button.dart';
import 'package:tm_app/views/your_tenders/widgets/liked_disliked_tenders_list.dart'; import 'package:tm_app/views/your_tenders/widgets/liked_disliked_tenders_list.dart';
import 'package:tm_app/views/your_tenders/widgets/tablet_desktop_appbar.dart'; import 'package:tm_app/views/your_tenders/widgets/tablet_desktop_appbar.dart';
import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart'; import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart';
import '../../../core/constants/tender_feedback.dart'; import '../../../core/constants/tender_feedback.dart';
import '../../shared/tablet_navigation_widget.dart'; import '../../shared/tablet_navigation_widget.dart';
import '../../shared/tender_app_bar.dart'; import '../../shared/tender_app_bar.dart';
@@ -43,9 +42,7 @@ class _YourTendersTabletPageState extends State<YourTendersTabletPage>
key: key, key: key,
backgroundColor: AppColors.backgroundColor, backgroundColor: AppColors.backgroundColor,
appBar: tabletAppBar(title: YourTendersStrings.yourTenders, key: key), appBar: tabletAppBar(title: YourTendersStrings.yourTenders, key: key),
drawer: TabletNavigationWidget( drawer: TabletNavigationWidget(currentIndex: 1),
currentIndex: 1, // Home is index 0
),
body: Center( body: Center(
child: SizedBox( child: SizedBox(
width: 720, width: 720,
@@ -57,19 +54,14 @@ class _YourTendersTabletPageState extends State<YourTendersTabletPage>
child: TabletDesktopAppbar(), child: TabletDesktopAppbar(),
), ),
SizedBox(height: 24.0.h()), SizedBox(height: 24.0.h()),
// MainTabBar(
// controller: controller,
// titles: [
// AppStrings.approvedTenders,
// AppStrings.tenderSubmitted,
// ],
// ),
FilterButton(platformType: PlatformType.tabletDesktop), FilterButton(platformType: PlatformType.tabletDesktop),
Expanded( Expanded(
child: Consumer<YourTendersViewModel>( child: Consumer<YourTendersViewModel>(
builder: (context, viewModel, child) { builder: (context, viewModel, child) {
if (viewModel.isLoading) { if (viewModel.isLoading &&
(viewModel.data == null &&
viewModel.likedTendersData == null)) {
return const Center( return const Center(
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: AppColors.secondary50, color: AppColors.secondary50,
@@ -81,32 +73,136 @@ class _YourTendersTabletPageState extends State<YourTendersTabletPage>
return Center(child: Text(viewModel.errorMessage!)); return Center(child: Text(viewModel.errorMessage!));
} }
Widget content;
if (viewModel.selectedStatus == if (viewModel.selectedStatus ==
YourTendersStrings.likeTenders && YourTendersStrings.likeTenders &&
viewModel.likedTendersData != null) { viewModel.likedTendersData != null) {
return LikedDislikedTendersList( content = LikedDislikedTendersList(
data: viewModel.likedTendersData!, data: viewModel.likedTendersData!,
status: TenderFeedback.liked.value, status: TenderFeedback.liked.value,
isDesctop: false, isDesktop: false,
); );
} else if (viewModel.selectedStatus == } else if (viewModel.selectedStatus ==
YourTendersStrings.dislikeTenders && YourTendersStrings.dislikeTenders &&
viewModel.likedTendersData != null) { viewModel.likedTendersData != null) {
return LikedDislikedTendersList( content = LikedDislikedTendersList(
data: viewModel.likedTendersData!, data: viewModel.likedTendersData!,
status: TenderFeedback.disliked.value, status: TenderFeedback.disliked.value,
isDesctop: false,
);
} else if (viewModel.data != null) {
return TendersSubmitted(
approvedTenders: viewModel.data!,
isDesktop: false, isDesktop: false,
); );
} else if (viewModel.data != null) {
content = TendersSubmitted(
approvedTenders: viewModel.data!,
isDesktop: false,
isMobile: false,
);
} else { } else {
return const Center( content = const Center(
child: Text(YourTendersStrings.noTenders), child: Text(YourTendersStrings.noTenders),
); );
} }
return Column(
children: [
Expanded(child: content),
SizedBox(height: 10.0.h()),
Row(
children: [
const Spacer(),
Text(
'Page',
style: TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w300,
color: AppColors.grey60,
),
),
SizedBox(width: 10.0.w()),
InkWell(
onTap: () async {
final selectedPage = await showDialog<int>(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: AppColors.backgroundColor,
title: const Text('Select Page'),
content: SizedBox(
width: 200.0.w(),
height: 300.0.h(),
child: ListView.builder(
itemCount: viewModel.totalPages,
itemBuilder: (context, index) {
final pageNumber = index + 1;
return ListTile(
title: Text('Page $pageNumber'),
onTap: () => Navigator.pop(
context,
pageNumber,
),
);
},
),
),
);
},
);
if (selectedPage != null) {
await viewModel.jumpToPage(selectedPage);
}
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: AppColors.grey30,
width: 1,
),
borderRadius: BorderRadius.circular(4),
),
child: Row(
children: [
SizedBox(width: 5.0.w()),
Text(
'${viewModel.currentPage}',
style: TextStyle(
fontSize: 14.0.sp(),
fontWeight: FontWeight.w500,
color: AppColors.grey70,
),
),
SizedBox(width: 5.0.w()),
const Icon(Icons.arrow_drop_down),
SizedBox(width: 5.0.w()),
],
),
),
),
SizedBox(width: 5.0),
Text(
'of',
style: TextStyle(
fontSize: 14.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey60,
),
),
SizedBox(width: 5.0.w()),
Text(
'${viewModel.totalPages}',
style: TextStyle(
fontSize: 13.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey60,
),
),
],
),
const SizedBox(height: 30.0),
],
);
}, },
), ),
), ),
@@ -12,12 +12,12 @@ import '../../../core/constants/tender_feedback.dart';
class LikedDislikedTendersList extends StatefulWidget { class LikedDislikedTendersList extends StatefulWidget {
final LikedTendersResponse data; final LikedTendersResponse data;
final String status; final String status;
final bool isDesctop; final bool isDesktop;
const LikedDislikedTendersList({ const LikedDislikedTendersList({
required this.data, required this.data,
required this.status, required this.status,
required this.isDesctop, required this.isDesktop,
super.key, super.key,
}); });
@@ -86,7 +86,7 @@ class _LikedDislikedTendersListState extends State<LikedDislikedTendersList> {
padding: EdgeInsets.only(bottom: 16.0.h()), padding: EdgeInsets.only(bottom: 16.0.h()),
child: TenderCard( child: TenderCard(
tender: feedback.tender!, tender: feedback.tender!,
isDesktop: widget.isDesctop, isDesktop: widget.isDesktop,
status: widget.status, status: widget.status,
submissionMode: '', submissionMode: '',
), ),
@@ -11,11 +11,13 @@ class TendersSubmitted extends StatefulWidget {
const TendersSubmitted({ const TendersSubmitted({
required this.approvedTenders, required this.approvedTenders,
required this.isDesktop, required this.isDesktop,
required this.isMobile,
super.key, super.key,
}); });
final TenderApprovalsResponse approvedTenders; final TenderApprovalsResponse approvedTenders;
final bool isDesktop; final bool isDesktop;
final bool isMobile;
@override @override
State<TendersSubmitted> createState() => _TendersSubmittedState(); State<TendersSubmitted> createState() => _TendersSubmittedState();
@@ -67,7 +69,7 @@ class _TendersSubmittedState extends State<TendersSubmitted> {
tenders.length + (viewModel.isLoadingMoreTenders ? 1 : 0); tenders.length + (viewModel.isLoadingMoreTenders ? 1 : 0);
return ListView.builder( return ListView.builder(
controller: _scrollController, controller: widget.isMobile ? _scrollController : null,
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
// horizontal: 24.0.w(), // horizontal: 24.0.w(),
vertical: 15.0.h(), vertical: 15.0.h(),