diff --git a/assets/icons/search_normal.svg b/assets/icons/search_normal.svg
new file mode 100644
index 0000000..ff9b6e2
--- /dev/null
+++ b/assets/icons/search_normal.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/sort.svg b/assets/icons/sort.svg
new file mode 100644
index 0000000..bf462ba
--- /dev/null
+++ b/assets/icons/sort.svg
@@ -0,0 +1,5 @@
+
diff --git a/lib/core/constants/assets.dart b/lib/core/constants/assets.dart
index 33252ae..8c821a0 100644
--- a/lib/core/constants/assets.dart
+++ b/lib/core/constants/assets.dart
@@ -40,6 +40,8 @@ class AssetsManager {
static const arrowLeftSmall = 'assets/icons/arrow-left-small.svg';
static const arrowRightSmall = 'assets/icons/arrow-right-small.svg';
static const tick = 'assets/icons/tick.svg';
+ static const normalSearch = 'assets/icons/search_normal.svg';
+ static const sort = 'assets/icons/sort.svg';
//home page
static const arrows = 'assets/icons/arrows.svg';
diff --git a/lib/main.dart b/lib/main.dart
index 52e7bb3..bdc3c9e 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -37,7 +37,7 @@ void main() async {
final prefs = await SharedPreferences.getInstance();
List providers = [
- Provider(create: (context) => prefs),
+ Provider(create: (context) => prefs),
...providersRemote,
];
// Configure web URL strategy if running on web
diff --git a/lib/views/shared/base_button.dart b/lib/views/shared/base_button.dart
index 6eee16d..09c471d 100644
--- a/lib/views/shared/base_button.dart
+++ b/lib/views/shared/base_button.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
import '../../core/theme/colors.dart';
import '../../core/utils/size_config.dart';
@@ -16,6 +17,8 @@ class BaseButton extends StatelessWidget {
final double? elevation;
final double? width;
final bool isLoading;
+ final String? icon;
+ final Color? iconColor;
const BaseButton({
required this.isEnabled,
@@ -29,6 +32,8 @@ class BaseButton extends StatelessWidget {
this.elevation,
this.width,
this.isLoading = false,
+ this.icon,
+ this.iconColor,
super.key,
});
@@ -72,14 +77,38 @@ class BaseButton extends StatelessWidget {
),
),
),
- child: Text(
- text ?? LoginStrings.loginButton,
- style: TextStyle(
- fontSize: 16.0.sp(),
- fontWeight: FontWeight.bold,
- color: textColor ?? AppColors.white,
- ),
- ),
+ child:
+ icon != null
+ ? Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset(
+ icon!,
+ colorFilter: ColorFilter.mode(
+ iconColor ?? AppColors.white,
+ BlendMode.srcIn,
+ ),
+ ),
+ SizedBox(width: 7.0.w()),
+ Text(
+ text ?? LoginStrings.loginButton,
+ style: TextStyle(
+ fontSize: 16.0.sp(),
+ fontWeight: FontWeight.bold,
+ color: textColor ?? AppColors.white,
+ ),
+ ),
+ ],
+ )
+ : Text(
+ text ?? LoginStrings.loginButton,
+ style: TextStyle(
+ fontSize: 16.0.sp(),
+ fontWeight: FontWeight.bold,
+ color: textColor ?? AppColors.white,
+ ),
+ ),
),
);
}
diff --git a/lib/views/tenders/pages/d_tenders_page.dart b/lib/views/tenders/pages/d_tenders_page.dart
index 5ec9d4e..6ce2c02 100644
--- a/lib/views/tenders/pages/d_tenders_page.dart
+++ b/lib/views/tenders/pages/d_tenders_page.dart
@@ -1,8 +1,11 @@
import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.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/tenders_view_model.dart';
+import 'package:tm_app/views/shared/base_button.dart';
import 'package:tm_app/views/shared/desktop_navigation_widget.dart';
import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
@@ -49,6 +52,86 @@ class _DesktopTendersPageState extends State {
const DesktopNavigationWidget(
currentIndex: 1, // Tenders index
),
+
+ SizedBox(height: 48.0.h()),
+ //search box
+ SizedBox(
+ height: 48.0.h(),
+ width: 680,
+ child: TextField(
+ style: TextStyle(fontSize: 16.0.sp(), color: AppColors.grey0),
+ decoration: InputDecoration(
+ hintText: 'Search',
+ hintStyle: TextStyle(
+ fontSize: 16.0.sp(),
+ color: AppColors.grey60,
+ ),
+ prefixIcon: Padding(
+ padding: EdgeInsets.all(12.0.w()),
+ child: SvgPicture.asset(
+ AssetsManager.normalSearch,
+ width: 24.0.w(),
+ height: 24.0.h(),
+ ),
+ ),
+ filled: true,
+ fillColor: AppColors.grey0,
+ contentPadding: EdgeInsets.symmetric(
+ horizontal: 12.0.w(),
+ vertical: 12.0.h(),
+ ),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(12.0.w()),
+ borderSide: const BorderSide(
+ color: AppColors.mainBlue,
+ width: 1,
+ ),
+ ),
+ enabledBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(12.0.w()),
+ borderSide: BorderSide(color: AppColors.grey40, width: 1),
+ ),
+ focusedBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(12.0.w()),
+ borderSide: const BorderSide(
+ color: AppColors.grey,
+ width: 1.5,
+ ),
+ ),
+ ),
+ ),
+ ),
+ Container(
+ margin: EdgeInsets.only(top: 25.0.h()),
+ height: 40.0.h(),
+ width: 680,
+ child: Row(
+ children: [
+ BaseButton(
+ width: 178.0.w(),
+ isEnabled: true,
+ onPressed: () {},
+ text: 'Filter',
+ icon: AssetsManager.filter,
+ iconColor: AppColors.textBlue,
+ backgroundColor: AppColors.primary2,
+ textColor: AppColors.textBlue,
+ ),
+ SizedBox(width: 10.0.w()),
+ BaseButton(
+ width: 178.0.w(),
+ isEnabled: true,
+ onPressed: () {},
+ text: 'Sort',
+ icon: AssetsManager.sort,
+ iconColor: AppColors.textBlue,
+ backgroundColor: AppColors.primary2,
+ textColor: AppColors.textBlue,
+ ),
+ ],
+ ),
+ ),
+
Expanded(
child: Consumer(
builder: (context, viewModel, child) {
@@ -74,7 +157,7 @@ class _DesktopTendersPageState extends State {
width: 740,
height: 800,
child: Padding(
- padding: EdgeInsets.symmetric(vertical: 24.0.h()),
+ padding: EdgeInsets.symmetric(vertical: 4.0.h()),
child: MainTendersSlider(
tenders: viewModel.tendersResponse?.data?.tenders ?? [],
isDesktop: true,
diff --git a/lib/views/tenders/widgets/main_tenders_slider.dart b/lib/views/tenders/widgets/main_tenders_slider.dart
index 1ff7aae..8170668 100644
--- a/lib/views/tenders/widgets/main_tenders_slider.dart
+++ b/lib/views/tenders/widgets/main_tenders_slider.dart
@@ -15,6 +15,7 @@ class MainTendersSlider extends StatefulWidget {
super.key,
this.isDesktop = false,
});
+
final bool isDesktop;
final List tenders;
@@ -24,7 +25,6 @@ class MainTendersSlider extends StatefulWidget {
class _MainTendersSliderState extends State {
final PageController pageController = PageController(initialPage: 0);
-
int currentPage = 1;
void _goToPreviousPage() {
@@ -32,17 +32,22 @@ class _MainTendersSliderState extends State {
return;
}
+ final targetIndex = currentPage - 2;
+ if (targetIndex < 0) {
+ return;
+ }
+
pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutCubic,
);
+
setState(() {
currentPage = currentPage - 1;
});
- // Fetch feedback and approvals for the previous tender if not already loaded
final viewModel = context.read();
- final previousTenderId = widget.tenders[currentPage - 2].id!;
+ final previousTenderId = widget.tenders[targetIndex].id!;
if (!viewModel.hasFeedbackForTender(previousTenderId)) {
viewModel.getTenderFeedback(previousTenderId);
}
@@ -56,17 +61,22 @@ class _MainTendersSliderState extends State {
return;
}
+ final targetIndex = currentPage;
+ if (targetIndex >= widget.tenders.length) {
+ return;
+ }
+
pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOutCubic,
);
+
setState(() {
currentPage = currentPage + 1;
});
- // Fetch feedback and approvals for the next tender if not already loaded
final viewModel = context.read();
- final nextTenderId = widget.tenders[currentPage].id!;
+ final nextTenderId = widget.tenders[targetIndex].id!;
if (!viewModel.hasFeedbackForTender(nextTenderId)) {
viewModel.getTenderFeedback(nextTenderId);
}
@@ -83,11 +93,27 @@ class _MainTendersSliderState extends State {
@override
Widget build(BuildContext context) {
- bool isLtr = Directionality.of(context) == TextDirection.ltr;
- return SingleChildScrollView(
- child: Stack(
- children: [_tendersPageView(), _nextPreviousArrows(isLtr: isLtr)],
- ),
+ final bool isLtr = Directionality.of(context) == TextDirection.ltr;
+
+ return widget.isDesktop ? _tendersListView() : _tendersSlider(isLtr);
+ }
+
+ Widget _tendersListView() {
+ return ListView.separated(
+ padding: EdgeInsets.all(16.0.h()),
+ itemCount: widget.tenders.length,
+ separatorBuilder:
+ (_, __) => SizedBox(height: widget.isDesktop ? 0 : 16.0.h()),
+ itemBuilder: (context, index) {
+ final tender = widget.tenders[index];
+ return TenderCard(tender: tender, isDesktop: widget.isDesktop);
+ },
+ );
+ }
+
+ Widget _tendersSlider(bool isLtr) {
+ return Stack(
+ children: [_tendersPageView(), _nextPreviousArrows(isLtr: isLtr)],
);
}
@@ -97,25 +123,21 @@ class _MainTendersSliderState extends State {
height: 722.0.h(),
child: PageView.builder(
controller: pageController,
- scrollDirection: Axis.horizontal,
itemCount: widget.tenders.length,
onPageChanged: (index) {
setState(() {
currentPage = index + 1;
});
- // Fetch feedback and approvals for the new tender if not already loaded
final viewModel = context.read();
- if (!viewModel.hasFeedbackForTender(widget.tenders[index].id!)) {
- viewModel.getTenderFeedback(widget.tenders[index].id!);
+ final id = widget.tenders[index].id!;
+ if (!viewModel.hasFeedbackForTender(id)) {
+ viewModel.getTenderFeedback(id);
}
- if (!viewModel.hasTenderApprovalForTender(
- widget.tenders[index].id!,
- )) {
- viewModel.getTenderApprovals(widget.tenders[index].id!);
+ if (!viewModel.hasTenderApprovalForTender(id)) {
+ viewModel.getTenderApprovals(id);
}
- // Check if we need to load more tenders (when user reaches near the end)
if (index >= widget.tenders.length - 1 &&
viewModel.hasMoreData &&
!viewModel.isLoadingMore) {
diff --git a/lib/views/tenders/widgets/tender_action_buttons_row.dart b/lib/views/tenders/widgets/tender_action_buttons_row.dart
index 02f56b9..f7ee40f 100644
--- a/lib/views/tenders/widgets/tender_action_buttons_row.dart
+++ b/lib/views/tenders/widgets/tender_action_buttons_row.dart
@@ -35,13 +35,13 @@ class TenderActionButtonsRow extends StatelessWidget {
// Dislike button
_dislikeButton(viewModel),
- SizedBox(width: 32.0.w()),
+ isDesktop ? Container() : SizedBox(width: 32.0.w()),
//reject button
- _rejectButton(viewModel),
- SizedBox(width: 32.0.w()),
+ isDesktop ? Container() : _rejectButton(viewModel),
+ isDesktop ? Container() : SizedBox(width: 32.0.w()),
//submit button
- _submitButton(viewModel, context),
- SizedBox(width: 32.0.w()),
+ isDesktop ? Container() : _submitButton(viewModel, context),
+ SizedBox(width: isDesktop ? 16.0.h() : 32.0.w()),
// Like button
_likeButton(viewModel),
],
diff --git a/lib/views/tenders/widgets/tender_card.dart b/lib/views/tenders/widgets/tender_card.dart
index 5c76bc1..b08424a 100644
--- a/lib/views/tenders/widgets/tender_card.dart
+++ b/lib/views/tenders/widgets/tender_card.dart
@@ -26,7 +26,7 @@ class TenderCard extends StatelessWidget {
Container(
width: double.infinity,
margin: EdgeInsets.symmetric(horizontal: 16.0.w()),
- height: 587.0.h(),
+ height: isDesktop ? 290.0.h() : 587.0.h(),
decoration: BoxDecoration(
color: AppColors.grey0,
borderRadius: BorderRadius.circular(12),
@@ -56,38 +56,41 @@ class TenderCard extends StatelessWidget {
// Deadline badge
_deadlineBadge(),
- SizedBox(height: 12.0.h()),
+ isDesktop ? Container() : SizedBox(height: 12.0.h()),
// Approval status badge
// _approvalStatusBadge(),
- SizedBox(height: 12.0.h()),
+ SizedBox(height: isDesktop ? 6.0.h() : 12.0.h()),
// Title
_tenderTitle(),
- SizedBox(height: 8.0.h()),
+ SizedBox(height: isDesktop ? 4.0.h() : 8.0.h()),
// Description
_tenderDescription(),
- SizedBox(height: 12.0.h()),
+ isDesktop ? Container() : SizedBox(height: 12.0.h()),
// Tender ID
- _idText(),
+ isDesktop ? Container() : _idText(),
],
),
),
const Spacer(),
// Bottom section with progress and actions
Padding(
- padding: EdgeInsets.all(16.0.w()),
+ padding: EdgeInsets.symmetric(
+ vertical: isDesktop ? 8.0.w() : 16.0.w(),
+ horizontal: 16.0.w(),
+ ),
child: Column(
children: [
// Match percentage
_matchPercentage(),
- SizedBox(height: 8.0.h()),
+ SizedBox(height: isDesktop ? 4.0.h() : 8.0.h()),
// Progress bar
_progressBar(),
- SizedBox(height: 16.0.h()),
+ SizedBox(height: isDesktop ? 8.0.h() : 16.0.h()),
// Location and apply button
Row(
@@ -105,7 +108,7 @@ class TenderCard extends StatelessWidget {
],
),
),
- SizedBox(height: 72.0.h()),
+ SizedBox(height: isDesktop ? 20.0.h() : 72.0.h()),
if (!isDesktop) TenderActionButtonsRow(tender: tender),
],
);
@@ -158,10 +161,11 @@ class TenderCard extends StatelessWidget {
Widget _tenderTitle() {
return Text(
tender.title ?? '',
+ maxLines: isDesktop ? 2 : 4,
style: TextStyle(
color: AppColors.grey80,
fontSize: 16.0.sp(),
- fontWeight: FontWeight.w600,
+ fontWeight: isDesktop ? FontWeight.w500 : FontWeight.w600,
),
);
}
@@ -169,11 +173,11 @@ class TenderCard extends StatelessWidget {
Widget _tenderDescription() {
return Text(
tender.description ?? '',
- maxLines: 4,
+ maxLines: isDesktop ? 2 : 4,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: AppColors.grey70,
- fontSize: 16.0.sp(),
+ fontSize: isDesktop ? 14.0.sp() : 16.0.sp(),
fontWeight: FontWeight.w400,
height: 1.5,
),
@@ -199,15 +203,15 @@ class TenderCard extends StatelessWidget {
TenderDetailsStrings.tenderMatchProfile,
style: TextStyle(
color: AppColors.grey80,
- fontSize: 14.0.sp(),
- fontWeight: FontWeight.w500,
+ fontSize: isDesktop ? 12.0.sp() : 14.0.sp(),
+ fontWeight: isDesktop ? FontWeight.w400 : FontWeight.w500,
),
),
Text(
'${45}%',
style: TextStyle(
color: AppColors.secondaryTextColor,
- fontSize: 16.0.sp(),
+ fontSize: isDesktop ? 12.0.sp() : 16.0.sp(),
fontWeight: FontWeight.w600,
),
),