Add filter dialog and bottom sheet for tenders

This commit is contained in:
llsajjad
2025-08-31 16:24:35 +03:30
parent bf6fbea9a9
commit 96540c3cb3
9 changed files with 314 additions and 57 deletions
@@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'tenders_filter_dialog.dart';
enum PlatformType { mobile, tabletDesktop }
class FilterButton extends StatelessWidget {
final PlatformType platformType;
const FilterButton({required this.platformType, super.key});
void _openFilter(BuildContext context) {
if (platformType == PlatformType.mobile) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (_) => const TendersFilterDialog(),
);
} else {
showDialog(
context: context,
builder:
(_) => Dialog(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: const TendersFilterDialog(),
),
);
}
}
@override
Widget build(BuildContext context) {
// Mobile style: container with icon and text
return InkWell(
onTap: () => _openFilter(context),
child: Container(
margin: const EdgeInsets.all(12),
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.filter_list, color: Colors.blue),
SizedBox(width: 8),
Text(
'Filter',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
);
}
}
@@ -0,0 +1,173 @@
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';
class TendersFilterDialog extends StatelessWidget {
const TendersFilterDialog({super.key});
Widget _dateRangePill(BuildContext context) {
final viewModel = context.watch<YourTendersViewModel>();
final start = viewModel.selectedDateRange?.start;
final end = viewModel.selectedDateRange?.end;
Future<void> pick() async {
final picked = await showDateRangePicker(
context: context,
firstDate: DateTime(2020),
lastDate: DateTime(2035),
initialDateRange: viewModel.selectedDateRange,
);
if (picked != null) {
viewModel.setDateRange(picked);
}
}
return 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 ? 'YYYY/MM/DD' : viewModel.startDateFormatted,
style: TextStyle(
fontSize: 16.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey70,
),
),
Text(
end == null ? 'YYYY/MM/DD' : viewModel.endDateFormatted,
style: TextStyle(
fontSize: 16.0.sp(),
fontWeight: FontWeight.w400,
color: AppColors.grey70,
),
),
],
),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Consumer<YourTendersViewModel>(
builder: (context, vm, child) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Text(
'Filter',
style: TextStyle(
fontSize: 20.0.sp(),
fontWeight: FontWeight.w700,
color: AppColors.grey70,
),
),
),
SizedBox(height: 16.0.h()),
Text(
'Select date range',
style: TextStyle(
fontSize: 16.0.sp(),
fontWeight: FontWeight.w600,
color: AppColors.grey80,
),
),
SizedBox(height: 20.0.h()),
_dateRangePill(context),
SizedBox(height: 16.0.h()),
Text(
'Status',
style: TextStyle(
fontSize: 16.0.sp(),
fontWeight: FontWeight.w600,
color: AppColors.grey70,
),
),
...[
'All',
'Like tenders',
'Dislike tenders',
'Approved tenders',
'Tenders submitted',
].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()),
SizedBox(
width: double.infinity,
child: BaseButton(
isEnabled: true,
onPressed: () {
context.pop();
},
text: 'Confirm',
textColor: AppColors.mainBlue,
backgroundColor: AppColors.primary30,
),
),
],
),
),
);
},
);
}
}