From ed80b54bef9cf2e8e8d6f94cc0db4a5bae0e3e02 Mon Sep 17 00:00:00 2001 From: llsajjad Date: Wed, 3 Sep 2025 11:14:11 +0330 Subject: [PATCH 1/2] Fixed some w(),h() and persion text --- .../pages/liked_tenders_desktop_page.dart | 10 +++++----- .../liked_tenders/pages/liked_tenders_mobile_page.dart | 10 +++++----- .../liked_tenders/pages/liked_tenders_tablet_page.dart | 10 +++++----- lib/views/login/pages/login_desktop_page.dart | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart b/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart index 7fe511c..98f3ebd 100644 --- a/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart +++ b/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart @@ -94,7 +94,7 @@ class _LikedTendersDesktopPageState extends State { if (feedback.isEmpty) { return const Center( - child: Text('چیزی برای نمایش نیست'), + child: Text('Nothing to show'), ); } @@ -138,12 +138,12 @@ class _LikedTendersDesktopPageState extends State { ), ); } else { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 20), + return Padding( + padding: EdgeInsets.symmetric(vertical: 20.0.h()), child: Center( child: SizedBox( - width: 24, - height: 24, + width: 24.0.w(), + height: 24.0.h(), child: CircularProgressIndicator( color: AppColors.secondary50, strokeWidth: 2, diff --git a/lib/views/liked_tenders/pages/liked_tenders_mobile_page.dart b/lib/views/liked_tenders/pages/liked_tenders_mobile_page.dart index f072287..0d1b8e1 100644 --- a/lib/views/liked_tenders/pages/liked_tenders_mobile_page.dart +++ b/lib/views/liked_tenders/pages/liked_tenders_mobile_page.dart @@ -71,7 +71,7 @@ class _LikedTendersMobilePageState extends State { final feedback = viewModel.data?.data?.feedback ?? []; if (feedback.isEmpty) { - return const Center(child: Text('چیزی برای نمایش نیست')); + return const Center(child: Text('Nothing to show')); } final itemCount = feedback.length + (viewModel.isLoadingMore ? 1 : 0); @@ -109,12 +109,12 @@ class _LikedTendersMobilePageState extends State { ), ); } else { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 20), + return Padding( + padding: EdgeInsets.symmetric(vertical: 20.0.h()), child: Center( child: SizedBox( - width: 24, - height: 24, + width: 24.0.w(), + height: 24.0.h(), child: CircularProgressIndicator( color: AppColors.secondary50, strokeWidth: 2, diff --git a/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart b/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart index 37cf573..8061e86 100644 --- a/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart +++ b/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart @@ -96,7 +96,7 @@ class _LikedTendersTabletPageState extends State { final feedback = viewModel.data?.data?.feedback ?? []; if (feedback.isEmpty) { - return const Center(child: Text('چیزی برای نمایش نیست')); + return const Center(child: Text('Nothing to show')); } final itemCount = @@ -138,12 +138,12 @@ class _LikedTendersTabletPageState extends State { ), ); } else { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 20), + return Padding( + padding: EdgeInsets.symmetric(vertical: 20.0.h()), child: Center( child: SizedBox( - width: 24, - height: 24, + width: 24.0.w(), + height: 24.0.h(), child: CircularProgressIndicator( color: AppColors.secondary50, strokeWidth: 2, diff --git a/lib/views/login/pages/login_desktop_page.dart b/lib/views/login/pages/login_desktop_page.dart index 028c510..468407f 100644 --- a/lib/views/login/pages/login_desktop_page.dart +++ b/lib/views/login/pages/login_desktop_page.dart @@ -131,7 +131,7 @@ class _LoginDesktopPageState extends State { ? Padding( padding: EdgeInsets.symmetric(vertical: 20.0.h()), child: ClipRRect( - borderRadius: BorderRadiusGeometry.circular(32), + borderRadius: BorderRadius.circular(32), child: Image.asset( AssetsManager.webLoginImage, width: 796.0, From faab29cd99d26af53a614705f5f045f592eec2b3 Mon Sep 17 00:00:00 2001 From: llsajjad Date: Wed, 3 Sep 2025 13:16:57 +0330 Subject: [PATCH 2/2] changed pagination in liked tenders tanlet and web --- assets/icons/arrow_down_small.svg | 3 + lib/core/constants/assets.dart | 1 + lib/view_models/liked_tenders_view_model.dart | 125 +++++---- .../pages/liked_tenders_desktop_page.dart | 243 +++++++++++------- .../pages/liked_tenders_tablet_page.dart | 182 ++++++++----- 5 files changed, 344 insertions(+), 210 deletions(-) create mode 100644 assets/icons/arrow_down_small.svg diff --git a/assets/icons/arrow_down_small.svg b/assets/icons/arrow_down_small.svg new file mode 100644 index 0000000..c4af0cf --- /dev/null +++ b/assets/icons/arrow_down_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/core/constants/assets.dart b/lib/core/constants/assets.dart index 7dbc80d..980c0f0 100644 --- a/lib/core/constants/assets.dart +++ b/lib/core/constants/assets.dart @@ -62,5 +62,6 @@ class AssetsManager { //Liked tenders static const trash = 'assets/icons/trash.svg'; + static const arrowdownSmall = 'assets/icons/arrow_down_small.svg'; } diff --git a/lib/view_models/liked_tenders_view_model.dart b/lib/view_models/liked_tenders_view_model.dart index 931a1c0..28dcf9a 100644 --- a/lib/view_models/liked_tenders_view_model.dart +++ b/lib/view_models/liked_tenders_view_model.dart @@ -17,67 +17,75 @@ class LikedTendersViewModel with ChangeNotifier { LikedTendersResponse? data; int _offset = 0; - final int _limit = 20; + final int _limit = 7; - Future getLikedTenders({bool reset = false}) async { - if (!reset && (isLoadingMore || !hasMore)) { - return; - } + int currentPage = 1; - if (reset) { - _offset = 0; - hasMore = true; - } +Future getLikedTenders({bool reset = false}) async { + if (!reset && (isLoadingMore || !hasMore)) { + return; + } + if (reset) { + _offset = (currentPage - 1) * _limit; + hasMore = true; + + data = null; + isLoading = true; + notifyListeners(); + } else { if (_offset == 0) { isLoading = true; } else { isLoadingMore = true; } - errorMessage = null; - notifyListeners(); - - final result = await likedTendersRepository.getLikedTenders( - offset: _offset, - limit: _limit, - ); - - switch (result) { - case Ok(): - final incoming = result.value; - final incomingList = incoming.data?.feedback ?? []; - - if (reset || data == null) { - data = incoming; - } else { - final existingList = data!.data!.feedback ?? []; - final combined = List.of(existingList) - ..addAll(incomingList); - - final newMeta = incoming.data?.meta ?? data!.data!.meta; - - data = data!.copyWith( - data: data!.data!.copyWith(feedback: combined, meta: newMeta), - ); - } - - // چون offset بر اساس limit حرکت می‌کنه - hasMore = incomingList.length == _limit; - _offset += _limit; - - errorMessage = null; - break; - - case Error(): - errorMessage = result.error.toString(); - break; - } - - isLoading = false; - isLoadingMore = false; notifyListeners(); } + errorMessage = null; + + final result = await likedTendersRepository.getLikedTenders( + offset: _offset, + limit: _limit, + ); + + switch (result) { + case Ok(): + final incoming = result.value; + final incomingList = incoming.data?.feedback ?? []; + + if (reset) { + data = incoming; + } else { + final existingList = data?.data?.feedback ?? []; + final combined = List.of(existingList) + ..addAll(incomingList); + + data = data?.copyWith( + data: data?.data?.copyWith( + feedback: combined, + meta: incoming.data?.meta ?? data!.data!.meta, + ), + ); + } + + hasMore = incomingList.length == _limit; + _offset += _limit; + + errorMessage = null; + break; + + case Error(): + errorMessage = result.error.toString(); + break; + } + + isLoading = false; + isLoadingMore = false; + notifyListeners(); +} + + Future loadMoreIfNeeded() async { if (isLoadingMore || !hasMore) { return; @@ -85,6 +93,13 @@ class LikedTendersViewModel with ChangeNotifier { await getLikedTenders(reset: false); } + Future jumpToPage(int page) async { + currentPage = page; + _offset = (page - 1) * _limit; + hasMore = true; + await getLikedTenders(reset: true); + } + void removeTenderById(String id) { final current = data; final list = current?.data?.feedback; @@ -92,14 +107,16 @@ class LikedTendersViewModel with ChangeNotifier { return; } - final List updatedList = List.of( - list, - )..removeWhere((e) => e.id == id); + final updatedList = List.of(list) + ..removeWhere((e) => e.id == id); final updatedMeta = current.data?.meta?.copyWith(total: updatedList.length); data = current.copyWith( - data: current.data?.copyWith(feedback: updatedList, meta: updatedMeta), + data: current.data?.copyWith( + feedback: updatedList, + meta: updatedMeta, + ), ); notifyListeners(); diff --git a/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart b/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart index 98f3ebd..42a1584 100644 --- a/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart +++ b/lib/views/liked_tenders/pages/liked_tenders_desktop_page.dart @@ -19,39 +19,10 @@ class LikedTendersDesktopPage extends StatefulWidget { } class _LikedTendersDesktopPageState extends State { - late ScrollController _scrollController; - - @override - void initState() { - super.initState(); - _scrollController = ScrollController()..addListener(_onScroll); - } - - void _onScroll() { - if (!_scrollController.hasClients) { - return; - } - final max = _scrollController.position.maxScrollExtent; - final pos = _scrollController.position.pixels; - const threshold = 200.0; - if (max - pos <= threshold) { - final vm = Provider.of(context, listen: false); - vm.loadMoreIfNeeded(); - } - } - - @override - void dispose() { - _scrollController.removeListener(_onScroll); - _scrollController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.backgroundColor, - body: Column( children: [ DesktopNavigationWidget( @@ -72,93 +43,171 @@ class _LikedTendersDesktopPageState extends State { Expanded( child: SizedBox( width: 740, - child: Column( - children: [ - Expanded( - child: Consumer( - builder: (context, viewModel, child) { - if (viewModel.isLoading && - (viewModel.data?.data?.feedback ?? []).isEmpty) { - return const Center( - child: CircularProgressIndicator( - color: AppColors.secondary50, - ), - ); - } + child: Consumer( + builder: (context, viewModel, child) { + if (viewModel.isLoading && + (viewModel.data?.data?.feedback ?? []).isEmpty) { + return const Center( + child: CircularProgressIndicator( + color: AppColors.secondary50, + ), + ); + } - if (viewModel.errorMessage != null) { - return Center(child: Text(viewModel.errorMessage!)); - } + if (viewModel.errorMessage != null) { + return Center(child: Text(viewModel.errorMessage!)); + } - final feedback = viewModel.data?.data?.feedback ?? []; + final feedback = viewModel.data?.data?.feedback ?? []; - if (feedback.isEmpty) { - return const Center( - child: Text('Nothing to show'), - ); - } + if (feedback.isEmpty) { + return const Center(child: Text('Nothing to show')); + } - final itemCount = - feedback.length + (viewModel.isLoadingMore ? 1 : 0); + final currentPage = viewModel.currentPage; + final totalPages = viewModel.data?.data?.meta?.pages ?? 1; - return RefreshIndicator( + return Column( + children: [ + Expanded( + child: RefreshIndicator( onRefresh: () async { await viewModel.getLikedTenders(reset: true); }, child: ListView.builder( - controller: _scrollController, padding: EdgeInsets.symmetric( horizontal: 24.0.w(), vertical: 15.0.h(), ), - itemCount: itemCount, + itemCount: feedback.length, itemBuilder: (context, index) { - if (index < feedback.length) { - final item = feedback[index]; - return Padding( - padding: EdgeInsets.only(bottom: 20.0.h()), - child: Dismissible( - key: ValueKey(item.id ?? index), - direction: DismissDirection.endToStart, - background: Container( - alignment: Alignment.centerRight, - padding: EdgeInsets.symmetric( - horizontal: 20.0.w(), - ), - child: SvgPicture.asset( - AssetsManager.trash, - ), + final item = feedback[index]; + return Padding( + padding: EdgeInsets.only(bottom: 20.0.h()), + child: Dismissible( + key: ValueKey(item.id ?? index), + direction: DismissDirection.endToStart, + background: Container( + alignment: Alignment.centerRight, + padding: EdgeInsets.symmetric( + horizontal: 20.0.w(), ), - onDismissed: (_) { - if (item.id != null) { - viewModel.removeTenderById(item.id!); - } - }, - child: LikedListItem(tender: item.tender!), - ), - ); - } else { - return Padding( - padding: EdgeInsets.symmetric(vertical: 20.0.h()), - child: Center( - child: SizedBox( - width: 24.0.w(), - height: 24.0.h(), - child: CircularProgressIndicator( - color: AppColors.secondary50, - strokeWidth: 2, - ), + child: SvgPicture.asset( + AssetsManager.trash, ), ), - ); - } + onDismissed: (_) { + if (item.id != null) { + viewModel.removeTenderById(item.id!); + } + }, + child: LikedListItem(tender: item.tender!), + ), + ); }, ), - ); - }, - ), - ), - ], + ), + ), + + 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()), + + GestureDetector( + onTap: () async { + final selectedPage = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Select Page'), + content: SizedBox( + width: 200, + height: 300, + child: ListView.builder( + itemCount: 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( + '$currentPage', + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.grey70, + ), + ), + SizedBox(width: 5.0.w()), + SvgPicture.asset( + AssetsManager.arrowdownSmall, + ), + 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( + '$totalPages', + style: TextStyle( + fontSize: 13.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, + ), + ), + ], + ), + + SizedBox(height: 30.0.h()), + ], + ); + }, ), ), ), diff --git a/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart b/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart index 8061e86..8e59ffe 100644 --- a/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart +++ b/lib/views/liked_tenders/pages/liked_tenders_tablet_page.dart @@ -21,34 +21,6 @@ class LikedTendersTabletPage extends StatefulWidget { } class _LikedTendersTabletPageState extends State { - late final ScrollController _scrollController; - - @override - void initState() { - super.initState(); - _scrollController = ScrollController()..addListener(_onScroll); - } - - void _onScroll() { - if (!_scrollController.hasClients) { - return; - } - final max = _scrollController.position.maxScrollExtent; - final pos = _scrollController.position.pixels; - const threshold = 200.0; - if (max - pos <= threshold) { - final vm = Provider.of(context, listen: false); - vm.loadMoreIfNeeded(); - } - } - - @override - void dispose() { - _scrollController.removeListener(_onScroll); - _scrollController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { final GlobalKey key = GlobalKey(); @@ -99,21 +71,23 @@ class _LikedTendersTabletPageState extends State { return const Center(child: Text('Nothing to show')); } - final itemCount = - feedback.length + (viewModel.isLoadingMore ? 1 : 0); - return RefreshIndicator( - onRefresh: () async { - await viewModel.getLikedTenders(reset: true); - }, - child: ListView.builder( - controller: _scrollController, - padding: EdgeInsets.symmetric( - horizontal: 24.0.w(), - vertical: 15.0.h(), - ), - itemCount: itemCount, - itemBuilder: (context, index) { - if (index < feedback.length) { + final currentPage = viewModel.currentPage; + final totalPages = viewModel.data?.data?.meta?.pages ?? 1; + + return Column( + children: [ + Expanded( + child: RefreshIndicator( + onRefresh: () async { + await viewModel.getLikedTenders(reset: true); + }, + child: ListView.builder( + padding: EdgeInsets.symmetric( + horizontal: 24.0.w(), + vertical: 15.0.h(), + ), + itemCount: feedback.length, + itemBuilder: (context, index) { final item = feedback[index]; return Padding( padding: EdgeInsets.only(bottom: 20.0.h()), @@ -137,24 +111,114 @@ class _LikedTendersTabletPageState extends State { child: LikedListItem(tender: item.tender!), ), ); - } else { - return Padding( - padding: EdgeInsets.symmetric(vertical: 20.0.h()), - child: Center( - child: SizedBox( - width: 24.0.w(), - height: 24.0.h(), - child: CircularProgressIndicator( - color: AppColors.secondary50, - strokeWidth: 2, + }, + ), + ), + ), + + 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()), + + GestureDetector( + onTap: () async { + final selectedPage = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Select Page'), + content: SizedBox( + width: 200, + height: 300, + child: ListView.builder( + itemCount: 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( + '$currentPage', + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.grey70, ), ), - ), - ); - } - }, - ), - ); }, + SizedBox(width: 5.0.w()), + SvgPicture.asset( + AssetsManager.arrowdownSmall, + ), + 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( + '$totalPages', + style: TextStyle( + fontSize: 13.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey60, + ), + ), + SizedBox(width: 30.0.w()), + ], + ), + + SizedBox(height: 30.0.h()), + ], + ); + }, ), ), ],