From 987f4998335f56d521c9a5faa393a7d1f313a75b Mon Sep 17 00:00:00 2001 From: llsajjad Date: Mon, 22 Sep 2025 17:18:41 +0330 Subject: [PATCH] added Filter and Sort dialog design in tenders page --- lib/views/tenders/pages/d_tenders_page.dart | 36 +++- .../tenders/strings/tenders_strings.dart | 16 +- .../widgets/tenders_filter_dialog.dart | 202 ++++++++++++++++++ .../tenders/widgets/tenders_sort_dialog.dart | 101 +++++++++ .../your_tenders/widgets/filter_button.dart | 6 +- ...g.dart => your_tenders_filter_dialog.dart} | 4 +- 6 files changed, 357 insertions(+), 8 deletions(-) create mode 100644 lib/views/tenders/widgets/tenders_filter_dialog.dart create mode 100644 lib/views/tenders/widgets/tenders_sort_dialog.dart rename lib/views/your_tenders/widgets/{tenders_filter_dialog.dart => your_tenders_filter_dialog.dart} (98%) diff --git a/lib/views/tenders/pages/d_tenders_page.dart b/lib/views/tenders/pages/d_tenders_page.dart index 3222305..683333d 100644 --- a/lib/views/tenders/pages/d_tenders_page.dart +++ b/lib/views/tenders/pages/d_tenders_page.dart @@ -12,6 +12,8 @@ import 'package:tm_app/views/shared/desktop_navigation_widget.dart'; import 'package:tm_app/views/shared/page_selection_dialog.dart'; import 'package:tm_app/views/tenders/strings/tenders_strings.dart'; import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart'; +import 'package:tm_app/views/tenders/widgets/tenders_filter_dialog.dart'; +import 'package:tm_app/views/tenders/widgets/tenders_sort_dialog.dart'; import '../../../core/constants/common_strings.dart'; import '../../../core/utils/app_toast.dart'; @@ -190,7 +192,22 @@ class _ActionButtons extends StatelessWidget { BaseButton( width: 178.0.w(), isEnabled: true, - onPressed: () {}, + onPressed: () { + showDialog( + context: context, + builder: + (_) => Dialog( + backgroundColor: AppColors.backgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: const SizedBox( + width: 560, + child: TendersFilterDialog(), + ), + ), + ); + }, text: 'Filter', icon: AssetsManager.filter, iconColor: AppColors.textBlue, @@ -201,7 +218,22 @@ class _ActionButtons extends StatelessWidget { BaseButton( width: 178.0.w(), isEnabled: true, - onPressed: () {}, + onPressed: () { + showDialog( + context: context, + builder: + (_) => Dialog( + backgroundColor: AppColors.backgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: const SizedBox( + width: 560, + child: TendersSortDialog(), + ), + ), + ); + }, text: 'Sort', icon: AssetsManager.sort, iconColor: AppColors.textBlue, diff --git a/lib/views/tenders/strings/tenders_strings.dart b/lib/views/tenders/strings/tenders_strings.dart index f0c5034..5745dba 100644 --- a/lib/views/tenders/strings/tenders_strings.dart +++ b/lib/views/tenders/strings/tenders_strings.dart @@ -12,5 +12,19 @@ class TendersStrings { static const String notifications = 'Notifications'; static const String of = 'Of'; static const String page = 'Page'; - static const String search = 'Search tenders...'; + static const String all = 'All'; + static const String read = 'Read'; + static const String selectDataRange = 'Select date range'; + static const String unread = 'Unread'; + static const String search = 'Search'; + static const String status = 'Status'; + static const String confirm = 'Confirm'; + static const String filter = 'Filter'; + static const String endDate = 'end Date'; + static const String startDate = 'Start Date'; + static const String sort = 'Sort'; + static const String mostOfTheTime = 'Most of the time'; + static const String minimumTime = 'Minimum time'; + static const String lessBudget = 'Less budget'; + static const String moreBudget = 'More budget'; } diff --git a/lib/views/tenders/widgets/tenders_filter_dialog.dart b/lib/views/tenders/widgets/tenders_filter_dialog.dart new file mode 100644 index 0000000..33a623d --- /dev/null +++ b/lib/views/tenders/widgets/tenders_filter_dialog.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/core/constants/assets.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/views/shared/base_button.dart'; +import 'package:tm_app/views/tenders/strings/tenders_strings.dart'; + +class TendersFilterDialog extends StatelessWidget { + const TendersFilterDialog({super.key}); + + Widget _dateRangePill(BuildContext context) { + final viewModel = context.watch(); + final start = viewModel.selectedDateRange?.start; + final end = viewModel.selectedDateRange?.end; + + Future pick() async { + final picked = await showDateRangePicker( + context: context, + firstDate: DateTime(2020), + lastDate: DateTime(2035), + initialDateRange: viewModel.selectedDateRange, + ); + if (picked != null) { + viewModel.setDateRange(picked); + } + } + + return SizedBox( + width: 560, + child: InkWell( + borderRadius: BorderRadius.circular(12), + onTap: pick, + child: Container( + height: 56.0.h(), + padding: const EdgeInsets.symmetric(horizontal: 20), + decoration: BoxDecoration( + color: AppColors.grey0, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.grey40), + ), + child: Center( + child: Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.center, + child: Container( + width: 1, + margin: const EdgeInsets.symmetric(vertical: 8), + color: AppColors.grey40, + ), + ), + Align( + alignment: Alignment.center, + child: SizedBox( + width: 26.0.w(), + height: 26.0.h(), + child: SvgPicture.asset(AssetsManager.trailingIcon), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + start == null + ? TendersStrings.startDate + : viewModel.startDateFormatted, + style: TextStyle( + fontSize: 16.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey70, + ), + ), + Text( + end == null + ? TendersStrings.endDate + : viewModel.endDateFormatted, + style: TextStyle( + fontSize: 16.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey70, + ), + ), + ], + ), + ], + ), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, vm, child) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + TendersStrings.filter, + style: TextStyle( + fontSize: 20.0.sp(), + fontWeight: FontWeight.w700, + color: AppColors.grey70, + ), + ), + const Spacer(), + InkWell( + onTap: () => Navigator.pop(context), + child: SvgPicture.asset( + AssetsManager.closeCircle, + colorFilter: ColorFilter.mode( + AppColors.grey80, + BlendMode.srcIn, + ), + width: 24.0.w(), + height: 24.0.h(), + ), + ), + ], + ), + SizedBox(height: 16.0.h()), + Divider(color: AppColors.grey20), + SizedBox(height: 20.0.h()), + Text( + TendersStrings.selectDataRange, + style: TextStyle( + fontSize: 16.0.sp(), + fontWeight: FontWeight.w600, + color: AppColors.grey80, + ), + ), + SizedBox(height: 10.0.h()), + _dateRangePill(context), + SizedBox(height: 16.0.h()), + Text( + TendersStrings.status, + style: TextStyle( + fontSize: 16.0.sp(), + fontWeight: FontWeight.w600, + color: AppColors.grey70, + ), + ), + ...[ + TendersStrings.all, + TendersStrings.read, + TendersStrings.unread, + ].map((status) { + return RadioListTile( + value: status, + groupValue: vm.selectedStatus, + activeColor: AppColors.mainBlue, + onChanged: (value) => vm.setStatus(value!), + title: Text( + status, + style: TextStyle( + fontSize: 16.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey70, + ), + ), + ); + }), + SizedBox(height: 16.0.h()), + Row( + children: [ + const Spacer(), + SizedBox( + width: 156.0.w(), + child: BaseButton( + isEnabled: true, + onPressed: () { + context.pop(); + vm.callWithFilter(); + }, + text: TendersStrings.confirm, + textColor: AppColors.textBlue, + backgroundColor: AppColors.primary2, + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/views/tenders/widgets/tenders_sort_dialog.dart b/lib/views/tenders/widgets/tenders_sort_dialog.dart new file mode 100644 index 0000000..7cf9a51 --- /dev/null +++ b/lib/views/tenders/widgets/tenders_sort_dialog.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; +import 'package:tm_app/core/constants/assets.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/views/shared/base_button.dart'; +import 'package:tm_app/views/tenders/strings/tenders_strings.dart'; + +class TendersSortDialog extends StatelessWidget { + const TendersSortDialog({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, vm, child) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + TendersStrings.sort, + style: TextStyle( + fontSize: 20.0.sp(), + fontWeight: FontWeight.w700, + color: AppColors.grey70, + ), + ), + const Spacer(), + InkWell( + onTap: () => Navigator.pop(context), + child: SvgPicture.asset( + AssetsManager.closeCircle, + colorFilter: ColorFilter.mode( + AppColors.grey80, + BlendMode.srcIn, + ), + width: 24.0.w(), + height: 24.0.h(), + ), + ), + ], + ), + SizedBox(height: 16.0.h()), + Divider(color: AppColors.grey20), + SizedBox(height: 20.0.h()), + ...[ + TendersStrings.minimumTime, + TendersStrings.mostOfTheTime, + TendersStrings.lessBudget, + TendersStrings.moreBudget, + ].map((status) { + return RadioListTile( + value: status, + groupValue: vm.selectedStatus, + activeColor: AppColors.mainBlue, + onChanged: (value) => vm.setStatus(value!), + title: Text( + status, + style: TextStyle( + fontSize: 16.0.sp(), + fontWeight: FontWeight.w400, + color: AppColors.grey70, + ), + ), + ); + }), + SizedBox(height: 16.0.h()), + Row( + children: [ + const Spacer(), + SizedBox( + width: 156.0.w(), + child: BaseButton( + isEnabled: true, + onPressed: () { + context.pop(); + vm.callWithFilter(); + }, + text: TendersStrings.confirm, + textColor: AppColors.textBlue, + backgroundColor: AppColors.primary2, + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/views/your_tenders/widgets/filter_button.dart b/lib/views/your_tenders/widgets/filter_button.dart index d10eb6f..f7aaa9b 100644 --- a/lib/views/your_tenders/widgets/filter_button.dart +++ b/lib/views/your_tenders/widgets/filter_button.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart'; -import 'tenders_filter_dialog.dart'; +import 'your_tenders_filter_dialog.dart'; enum PlatformType { mobile, tabletDesktop } @@ -19,7 +19,7 @@ class FilterButton extends StatelessWidget { shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), - builder: (_) => const TendersFilterDialog(), + builder: (_) => const YourTendersFilterDialog(), ); } else { showDialog( @@ -30,7 +30,7 @@ class FilterButton extends StatelessWidget { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), - child: const TendersFilterDialog(), + child: const YourTendersFilterDialog(), ), ); } diff --git a/lib/views/your_tenders/widgets/tenders_filter_dialog.dart b/lib/views/your_tenders/widgets/your_tenders_filter_dialog.dart similarity index 98% rename from lib/views/your_tenders/widgets/tenders_filter_dialog.dart rename to lib/views/your_tenders/widgets/your_tenders_filter_dialog.dart index 56ac63a..e790398 100644 --- a/lib/views/your_tenders/widgets/tenders_filter_dialog.dart +++ b/lib/views/your_tenders/widgets/your_tenders_filter_dialog.dart @@ -9,8 +9,8 @@ import 'package:tm_app/view_models/your_tenders_view_model.dart'; import 'package:tm_app/views/shared/base_button.dart'; import 'package:tm_app/views/your_tenders/strings/your_tenders_strings.dart'; -class TendersFilterDialog extends StatelessWidget { - const TendersFilterDialog({super.key}); +class YourTendersFilterDialog extends StatelessWidget { + const YourTendersFilterDialog({super.key}); Widget _dateRangePill(BuildContext context) { final viewModel = context.watch();