diff --git a/assets/icons/calendar.svg b/assets/icons/calendar.svg
new file mode 100644
index 0000000..542224b
--- /dev/null
+++ b/assets/icons/calendar.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/icons/menu.svg b/assets/icons/menu.svg
new file mode 100644
index 0000000..58fa920
--- /dev/null
+++ b/assets/icons/menu.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/notification.svg b/assets/icons/notification.svg
new file mode 100644
index 0000000..c02502d
--- /dev/null
+++ b/assets/icons/notification.svg
@@ -0,0 +1,5 @@
+
diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart
index d377d11..de9236d 100644
--- a/lib/core/config/dependencies.dart
+++ b/lib/core/config/dependencies.dart
@@ -6,6 +6,7 @@ import 'package:tm_app/data/repositories/tender_detail_repository.dart';
import 'package:tm_app/data/services/home_service.dart';
import 'package:tm_app/data/services/tender_detail_service.dart';
import 'package:tm_app/view_models/tender_detail_view_model.dart';
+import 'package:tm_app/view_models/tenders_view_model.dart';
import '../../data/repositories/auth_repository.dart';
import '../../data/services/auth_service.dart';
@@ -59,6 +60,7 @@ List get viewModels {
(context) =>
TenderDetailViewModel(tenderDetailRepository: context.read()),
),
+ ChangeNotifierProvider(create: (context) => TendersViewModel()),
];
}
diff --git a/lib/core/constants/assets.dart b/lib/core/constants/assets.dart
index 148a221..2d74ad8 100644
--- a/lib/core/constants/assets.dart
+++ b/lib/core/constants/assets.dart
@@ -3,6 +3,11 @@ class AssetsManager {
//bottom navigation
static const homeActive = 'assets/icons/home_active.svg';
+ static const home = 'assets/icons/home.svg';
+ static const tenders = 'assets/icons/task_square.svg';
+ static const tendersActive = 'assets/icons/task-square_active.svg';
+ static const profile = 'assets/icons/profile-circle.svg';
+ static const profileActive = 'assets/icons/profile-circle_active.svg';
// login page
static const logo = 'assets/pngs/logo.png';
@@ -30,6 +35,9 @@ class AssetsManager {
static const shield = 'assets/icons/shield.svg';
static const edit = 'assets/icons/edit.svg';
static const arrowRight = 'assets/icons/arrow-right.svg';
+ static const notification = 'assets/icons/notification.svg';
+ static const calendar = 'assets/icons/calendar.svg';
+ static const menu = 'assets/icons/menu.svg';
//your tenders page
static const tickCircle = 'assets/icons/tick-circle.svg';
diff --git a/lib/core/constants/strings.dart b/lib/core/constants/strings.dart
index 54089d9..7de2758 100644
--- a/lib/core/constants/strings.dart
+++ b/lib/core/constants/strings.dart
@@ -32,6 +32,7 @@ class AppStrings {
static const String tenderValue = 'Tender Value';
static const String thunderStatus = 'Thunder status';
static const String yourTenders = 'Your tenders';
+ static const String welcome = 'Welcome';
//Tenders
static const String tendersTitle = 'Tenders';
diff --git a/lib/core/routes/app_routes.dart b/lib/core/routes/app_routes.dart
index eb55985..a7883a6 100644
--- a/lib/core/routes/app_routes.dart
+++ b/lib/core/routes/app_routes.dart
@@ -4,10 +4,10 @@ import 'package:tm_app/views/detail/pages/detail_screen.dart';
import 'package:tm_app/views/home/pages/home_screen.dart';
import 'package:tm_app/views/login/pages/login_screen.dart';
-import '../../views/profile/profile_screen.dart';
-import '../../views/tenders/tenders_screen.dart';
+import '../../views/profile/pages/profile_screen.dart';
+import '../../views/tenders/pages/tenders_screen.dart';
import '../../views/your_tenders/your_tenders_screen.dart';
-import 'app_shell_screen.dart';
+import 'app_shell/app_shell_screen.dart';
part 'app_routes.g.dart';
diff --git a/lib/core/routes/app_shell/app_shell_screen.dart b/lib/core/routes/app_shell/app_shell_screen.dart
new file mode 100644
index 0000000..15f9a75
--- /dev/null
+++ b/lib/core/routes/app_shell/app_shell_screen.dart
@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+import 'package:go_router/go_router.dart';
+import 'package:tm_app/core/routes/app_shell/desktop_shell_page.dart';
+import 'package:tm_app/core/routes/app_shell/mobile_shell_page.dart';
+import 'package:tm_app/core/routes/app_shell/tablet_shell_page.dart';
+import 'package:tm_app/views/shared/responsive_builder.dart';
+
+class AppShellScreen extends StatelessWidget {
+ const AppShellScreen({required this.navigationShell, super.key});
+
+ final StatefulNavigationShell navigationShell;
+
+ void _gotoBranch(int index) {
+ navigationShell.goBranch(
+ index,
+ initialLocation: index == navigationShell.currentIndex,
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return ResponsiveBuilder(
+ mobile: MobileShellPage(
+ navigationShell: navigationShell,
+ onTap: _gotoBranch,
+ ),
+ tablet: TabletShellPage(
+ navigationShell: navigationShell,
+ onTap: _gotoBranch,
+ ),
+ desktop: DesktopShellPage(
+ navigationShell: navigationShell,
+ onTap: _gotoBranch,
+ ),
+ );
+ }
+}
diff --git a/lib/core/routes/app_shell/desktop_shell_page.dart b/lib/core/routes/app_shell/desktop_shell_page.dart
new file mode 100644
index 0000000..f234f1d
--- /dev/null
+++ b/lib/core/routes/app_shell/desktop_shell_page.dart
@@ -0,0 +1,109 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:go_router/go_router.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';
+
+class DesktopShellPage extends StatelessWidget {
+ const DesktopShellPage({
+ required this.navigationShell,
+ required this.onTap,
+ super.key,
+ });
+
+ final StatefulNavigationShell navigationShell;
+ final ValueChanged onTap;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Column(
+ children: [_navigationBar(context), Expanded(child: navigationShell)],
+ ),
+ );
+ }
+
+ Container _navigationBar(BuildContext context) {
+ return Container(
+ height: 64,
+ decoration: BoxDecoration(
+ border: Border(bottom: BorderSide(color: AppColors.grey30)),
+ ),
+
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ SizedBox(width: 40),
+ Image.asset(AssetsManager.logo, width: 59, height: 28),
+ Spacer(),
+ _bottomNavigationItem(
+ context: context,
+ text: 'Home',
+ isActive: navigationShell.currentIndex == 0,
+ onTap: () => onTap(0),
+ iconPath: 'assets/icons/home.svg',
+ activeIconPath: AssetsManager.homeActive,
+ ),
+ SizedBox(width: 24),
+ _bottomNavigationItem(
+ context: context,
+ text: 'Tenders',
+ isActive: navigationShell.currentIndex == 1,
+ onTap: () => onTap(1),
+ iconPath: 'assets/icons/task_square.svg',
+ activeIconPath: 'assets/icons/task-square_active.svg',
+ ),
+ SizedBox(width: 24),
+ _bottomNavigationItem(
+ context: context,
+ text: 'Profile',
+ isActive: navigationShell.currentIndex == 2,
+ onTap: () => onTap(2),
+ iconPath: 'assets/icons/profile-circle.svg',
+ activeIconPath: 'assets/icons/profile-circle_active.svg',
+ ),
+ Spacer(),
+ SizedBox(width: 59),
+ ],
+ ),
+ );
+ }
+
+ Widget _bottomNavigationItem({
+ required BuildContext context,
+ required String text,
+ required bool isActive,
+ required VoidCallback onTap,
+ required String iconPath,
+ required String activeIconPath,
+ }) {
+ return InkWell(
+ onTap: onTap,
+ child: SizedBox(
+ width: 200.0,
+ height: 64,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ SvgPicture.asset(isActive ? activeIconPath : iconPath),
+ SizedBox(width: 8),
+ Text(
+ text,
+ style: TextStyle(
+ fontSize: 12.0.sp(),
+ fontWeight: FontWeight.w400,
+ color:
+ isActive
+ ? const Color(0xFF0164FF)
+ : const Color(0xFF777777),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/core/routes/app_shell_screen.dart b/lib/core/routes/app_shell/mobile_shell_page.dart
similarity index 81%
rename from lib/core/routes/app_shell_screen.dart
rename to lib/core/routes/app_shell/mobile_shell_page.dart
index 62f6b60..ba1ca45 100644
--- a/lib/core/routes/app_shell_screen.dart
+++ b/lib/core/routes/app_shell/mobile_shell_page.dart
@@ -1,27 +1,22 @@
-// Shell screen with bottom navigation
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.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 '../constants/assets.dart';
-import '../theme/colors.dart';
-import '../utils/size_config.dart';
-
-class AppShellScreen extends StatelessWidget {
- const AppShellScreen({required this.navigationShell, super.key});
+class MobileShellPage extends StatelessWidget {
+ const MobileShellPage({
+ required this.navigationShell,
+ required this.onTap,
+ super.key,
+ });
final StatefulNavigationShell navigationShell;
-
- void _gotoBranch(int index) {
- navigationShell.goBranch(
- index,
- initialLocation: index == navigationShell.currentIndex,
- );
- }
+ final ValueChanged onTap;
@override
Widget build(BuildContext context) {
- SizeConfig.init(context);
return Scaffold(
body: navigationShell,
bottomNavigationBar: Container(
@@ -37,7 +32,7 @@ class AppShellScreen extends StatelessWidget {
context: context,
text: 'Home',
isActive: navigationShell.currentIndex == 0,
- onTap: () => _gotoBranch(0),
+ onTap: () => onTap(0),
iconPath: 'assets/icons/home.svg',
activeIconPath: AssetsManager.homeActive,
),
@@ -45,7 +40,7 @@ class AppShellScreen extends StatelessWidget {
context: context,
text: 'Tenders',
isActive: navigationShell.currentIndex == 1,
- onTap: () => _gotoBranch(1),
+ onTap: () => onTap(1),
iconPath: 'assets/icons/task_square.svg',
activeIconPath: 'assets/icons/task-square_active.svg',
),
@@ -53,7 +48,7 @@ class AppShellScreen extends StatelessWidget {
context: context,
text: 'Profile',
isActive: navigationShell.currentIndex == 2,
- onTap: () => _gotoBranch(2),
+ onTap: () => onTap(2),
iconPath: 'assets/icons/profile-circle.svg',
activeIconPath: 'assets/icons/profile-circle_active.svg',
),
diff --git a/lib/core/routes/app_shell/tablet_shell_page.dart b/lib/core/routes/app_shell/tablet_shell_page.dart
new file mode 100644
index 0000000..a4c346e
--- /dev/null
+++ b/lib/core/routes/app_shell/tablet_shell_page.dart
@@ -0,0 +1,126 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:go_router/go_router.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';
+
+class TabletShellPage extends StatelessWidget {
+ TabletShellPage({
+ required this.navigationShell,
+ required this.onTap,
+ super.key,
+ });
+
+ final StatefulNavigationShell navigationShell;
+ final ValueChanged onTap;
+
+ final GlobalKey _key = GlobalKey();
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ key: _key,
+ body: Stack(
+ children: [
+ navigationShell,
+ Container(
+ width: double.infinity,
+ height: 64.0.h(),
+ padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(color: AppColors.grey30, width: 1),
+ ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Image.asset(
+ AssetsManager.logo,
+ width: 59.0.w(),
+ height: 28.0.h(),
+ ),
+ InkWell(
+ onTap: () => _key.currentState!.openDrawer(),
+ child: SvgPicture.asset(AssetsManager.menu),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ drawer: Container(
+ width: 200.0.w(),
+ height: double.infinity,
+ color: AppColors.backgroundColor,
+ child: Column(
+ children: [
+ SizedBox(height: 48.0.h()),
+ _drawerItem(
+ context: context,
+ text: 'Home',
+ isActive: navigationShell.currentIndex == 0,
+ onTap: () => onTap(0),
+ iconPath: AssetsManager.home,
+ activeIconPath: AssetsManager.homeActive,
+ ),
+ _drawerItem(
+ context: context,
+ text: 'Tenders',
+ isActive: navigationShell.currentIndex == 1,
+ onTap: () => onTap(1),
+ iconPath: AssetsManager.tenders,
+ activeIconPath: AssetsManager.tendersActive,
+ ),
+ _drawerItem(
+ context: context,
+ text: 'Profile',
+ isActive: navigationShell.currentIndex == 2,
+ onTap: () => onTap(2),
+ iconPath: AssetsManager.profile,
+ activeIconPath: AssetsManager.profileActive,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _drawerItem({
+ required BuildContext context,
+ required String text,
+ required bool isActive,
+ required VoidCallback onTap,
+ required String iconPath,
+ required String activeIconPath,
+ }) {
+ return InkWell(
+ onTap: onTap,
+ child: SizedBox(
+ width: 200.0.w(),
+ height: 64.0.h(),
+ child: Padding(
+ padding: EdgeInsetsDirectional.only(start: 24.0.w()),
+ child: Row(
+ children: [
+ SvgPicture.asset(isActive ? activeIconPath : iconPath),
+ SizedBox(width: 8.0.w()),
+ Text(
+ text,
+ style: TextStyle(
+ fontSize: 12.0.sp(),
+ fontWeight: FontWeight.w400,
+ color:
+ isActive
+ ? const Color(0xFF0164FF)
+ : const Color(0xFF777777),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/core/theme/theme_provider.dart b/lib/core/theme/theme_provider.dart
index eae8662..3c1a944 100644
--- a/lib/core/theme/theme_provider.dart
+++ b/lib/core/theme/theme_provider.dart
@@ -3,7 +3,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'colors.dart';
-class ThemeProvider extends ChangeNotifier {
+class ThemeProvider with ChangeNotifier {
static const String _themeKey = 'theme_mode';
static BuildContext? _context;
@@ -40,6 +40,7 @@ class ThemeProvider extends ChangeNotifier {
// Notify listeners after loading
notifyListeners();
+ _rebuildAllChildren();
}
Future toggleTheme() async {
diff --git a/lib/data/services/auth_service.dart b/lib/data/services/auth_service.dart
index e8c4f39..7a03bbf 100644
--- a/lib/data/services/auth_service.dart
+++ b/lib/data/services/auth_service.dart
@@ -10,7 +10,7 @@ class AuthService {
// شبیهسازی API Call
await Future.delayed(const Duration(seconds: 2));
- if (email == 'test@example.com' && password == 'password123') {
+ if (email == 'a' && password == 'a') {
return {'token': 'xyz123', 'name': 'John Doe'};
} else {
throw Exception('Invalid credentials');
diff --git a/lib/main.dart b/lib/main.dart
index 77c1e0f..8ed1ab3 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -43,6 +43,7 @@ class _MyAppState extends State {
return MaterialApp.router(
title: 'Flutter Demo',
+ debugShowCheckedModeBanner: false,
scrollBehavior: MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
diff --git a/lib/view_models/tenders_view_model.dart b/lib/view_models/tenders_view_model.dart
new file mode 100644
index 0000000..c2d2827
--- /dev/null
+++ b/lib/view_models/tenders_view_model.dart
@@ -0,0 +1,85 @@
+import 'package:flutter/material.dart';
+import 'package:tm_app/views/tenders/models/tender_model.dart';
+
+class TendersViewModel with ChangeNotifier {
+ List likedTenders = [];
+ List dislikedTenders = [];
+ int currentPage = 1;
+ List rejectedTenders = [];
+ List submittedTenders = [];
+
+ void onDislike(Tender tender) {
+ if (likedTenders.contains(tender)) {
+ likedTenders.remove(tender);
+ if (submittedTenders.contains(tender)) {
+ submittedTenders.remove(tender);
+ }
+ }
+
+ if (!dislikedTenders.contains(tender)) {
+ dislikedTenders.add(tender);
+ if (submittedTenders.contains(tender)) {
+ submittedTenders.remove(tender);
+ }
+ } else {
+ dislikedTenders.remove(tender);
+ }
+ notifyListeners();
+ }
+
+ void onLike(Tender tender) {
+ if (dislikedTenders.contains(tender)) {
+ dislikedTenders.remove(tender);
+ if (rejectedTenders.contains(tender)) {
+ rejectedTenders.remove(tender);
+ }
+ }
+
+ if (!likedTenders.contains(tender)) {
+ likedTenders.add(tender);
+ if (rejectedTenders.contains(tender)) {
+ rejectedTenders.remove(tender);
+ }
+ } else {
+ likedTenders.remove(tender);
+ }
+ notifyListeners();
+ }
+
+ void onReject(Tender tender) {
+ if (rejectedTenders.contains(tender)) {
+ rejectedTenders.remove(tender);
+ } else if (submittedTenders.contains(tender)) {
+ submittedTenders.remove(tender);
+ rejectedTenders.add(tender);
+ if (likedTenders.contains(tender)) {
+ likedTenders.remove(tender);
+ }
+ } else {
+ rejectedTenders.add(tender);
+ if (likedTenders.contains(tender)) {
+ likedTenders.remove(tender);
+ }
+ }
+ notifyListeners();
+ }
+
+ void onSubmit(Tender tender) {
+ if (submittedTenders.contains(tender)) {
+ submittedTenders.remove(tender);
+ } else if (rejectedTenders.contains(tender)) {
+ rejectedTenders.remove(tender);
+ submittedTenders.add(tender);
+ if (dislikedTenders.contains(tender)) {
+ dislikedTenders.remove(tender);
+ }
+ } else {
+ submittedTenders.add(tender);
+ if (dislikedTenders.contains(tender)) {
+ dislikedTenders.remove(tender);
+ }
+ }
+
+ notifyListeners();
+ }
+}
diff --git a/lib/views/detail/pages/detail_screen.dart b/lib/views/detail/pages/detail_screen.dart
index 93464a1..2a5053b 100644
--- a/lib/views/detail/pages/detail_screen.dart
+++ b/lib/views/detail/pages/detail_screen.dart
@@ -12,9 +12,9 @@ class TenderDetailScreen extends StatelessWidget {
Widget build(BuildContext context) {
SizeConfig.init(context);
return ResponsiveBuilder(
- mobile: TenderDetailMobilePage(tenderId: 'JNDFKMDV-100JF',),
- tablet: TenderDetailTabletPage(tenderId: 'JNDFKMDV-100JF',),
- desktop: TenderDetailDesktopPage(tenderId: 'JNDFKMDV-100JF',),
+ mobile: TenderDetailMobilePage(tenderId: 'JNDFKMDV-100JF'),
+ tablet: TenderDetailTabletPage(tenderId: 'JNDFKMDV-100JF'),
+ desktop: TenderDetailDesktopPage(tenderId: 'JNDFKMDV-100JF'),
);
}
}
diff --git a/lib/views/home/pages/d_home_page.dart b/lib/views/home/pages/d_home_page.dart
index 258f0fd..eca0167 100644
--- a/lib/views/home/pages/d_home_page.dart
+++ b/lib/views/home/pages/d_home_page.dart
@@ -6,6 +6,7 @@ import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
import 'package:tm_app/view_models/home_view_model.dart';
+import 'package:tm_app/views/home/widgets/notification_card.dart';
import '../widgets.dart';
@@ -32,6 +33,8 @@ class DesktopHomePage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height: 55),
+ SizedBox(width: 780, child: NotificationCard()),
+ SizedBox(height: 40.0),
_progressBarsRow(homeViewModel.homeResponse!),
SizedBox(height: 32.0.h()),
_firstTenderCardsRow(context, homeViewModel.homeResponse!),
diff --git a/lib/views/home/pages/m_home_page.dart b/lib/views/home/pages/m_home_page.dart
index 6b8fdeb..37f3878 100644
--- a/lib/views/home/pages/m_home_page.dart
+++ b/lib/views/home/pages/m_home_page.dart
@@ -7,6 +7,7 @@ import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
import 'package:tm_app/view_models/home_view_model.dart';
+import 'package:tm_app/views/home/widgets/notification_card.dart';
import 'package:tm_app/views/shared/tender_app_bar.dart';
import '../widgets.dart';
@@ -63,6 +64,8 @@ class MobileHomePage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 18.0.h()),
+ NotificationCard(),
+ SizedBox(height: 32.0.h()),
_progressBarsRow(homeViewModel.homeResponse!),
SizedBox(height: 32.0.h()),
_firstTenderCardsRow(context, homeViewModel.homeResponse!),
diff --git a/lib/views/home/pages/t_home_page.dart b/lib/views/home/pages/t_home_page.dart
index c8747ed..cfc13b6 100644
--- a/lib/views/home/pages/t_home_page.dart
+++ b/lib/views/home/pages/t_home_page.dart
@@ -6,6 +6,7 @@ import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
import 'package:tm_app/view_models/home_view_model.dart';
+import 'package:tm_app/views/home/widgets/notification_card.dart';
import '../widgets.dart';
@@ -38,6 +39,8 @@ class TabletHomePage extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
+ NotificationCard(),
+ SizedBox(height: 40.0.h()),
_progressBarsRow(homeViewModel.homeResponse!),
SizedBox(height: 32.0.h()),
_firstTenderCardsRow(context, homeViewModel.homeResponse!),
diff --git a/lib/views/home/widgets/notification_card.dart b/lib/views/home/widgets/notification_card.dart
new file mode 100644
index 0000000..c042fa2
--- /dev/null
+++ b/lib/views/home/widgets/notification_card.dart
@@ -0,0 +1,70 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:tm_app/core/constants/assets.dart';
+import 'package:tm_app/core/constants/strings.dart';
+import 'package:tm_app/core/theme/colors.dart';
+import 'package:tm_app/core/utils/size_config.dart';
+
+class NotificationCard extends StatelessWidget {
+ const NotificationCard({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: double.infinity,
+ margin: EdgeInsets.symmetric(horizontal: 24.0.w()),
+ padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 16.0.h()),
+ decoration: BoxDecoration(
+ color: AppColors.mainBlue.withValues(alpha: 0.03),
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(color: AppColors.mainBlue.withValues(alpha: 0.15)),
+ ),
+ child: Column(
+ children: [
+ Row(
+ children: [
+ SvgPicture.asset(AssetsManager.notification),
+ SizedBox(width: 8.0.w()),
+ Text(
+ AppStrings.welcome,
+ style: TextStyle(
+ fontSize: 16.0.sp(),
+ fontWeight: FontWeight.w500,
+ color: AppColors.grey80,
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 8.0.h()),
+ Text(
+ 'You’ve been invited Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis',
+ maxLines: 3,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 14.0.sp(),
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey60,
+ ),
+ ),
+ SizedBox(height: 8.0.h()),
+ Padding(
+ padding: EdgeInsetsDirectional.only(start: 28.0),
+ child: Row(
+ children: [
+ SvgPicture.asset(AssetsManager.calendar),
+ SizedBox(width: 4.0.w()),
+ Text(
+ '5 Min',
+ style: TextStyle(
+ fontSize: 14.0.sp(),
+ color: AppColors.grey60,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/views/login/pages/login_mobile_page.dart b/lib/views/login/pages/login_mobile_page.dart
index 9912c6d..1d62480 100644
--- a/lib/views/login/pages/login_mobile_page.dart
+++ b/lib/views/login/pages/login_mobile_page.dart
@@ -14,8 +14,6 @@ class LoginMobilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
- SizeConfig.init(context);
-
return Scaffold(
backgroundColor: AppColors.backgroundColor,
body: Center(
diff --git a/lib/views/profile/pages/d_profile_page.dart b/lib/views/profile/pages/d_profile_page.dart
new file mode 100644
index 0000000..161472c
--- /dev/null
+++ b/lib/views/profile/pages/d_profile_page.dart
@@ -0,0 +1,61 @@
+import 'package:flutter/material.dart';
+
+import '../../../core/theme/colors.dart';
+import '../../../core/utils/size_config.dart';
+import '../widgets/theme_toggle.dart';
+import '../widgets/title_description.dart';
+
+class DesktopProfilePage extends StatelessWidget {
+ const DesktopProfilePage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ child: Scaffold(
+ backgroundColor: AppColors.backgroundColor,
+ body: Center(
+ child: SizedBox(
+ width: 740,
+ height: 507,
+ child: Column(
+ children: [
+ SizedBox(height: 36.0.h()),
+ TitleDescription(
+ title: 'Company Name',
+ description: 'Telecom Solutions GmbH',
+ ),
+ TitleDescription(
+ title: 'National ID',
+ description: '12345678901',
+ ),
+ TitleDescription(
+ title: 'Registration No.',
+ description: 'DE-55667788',
+ ),
+ TitleDescription(
+ title: 'Business Type',
+ description: 'Telecommunications',
+ ),
+ TitleDescription(title: 'Founded', description: '2010'),
+ SizedBox(height: 24.0.h()),
+ // Theme Toggle
+ Container(
+ padding: EdgeInsets.symmetric(
+ horizontal: 16.0.w(),
+ vertical: 12.0.h(),
+ ),
+ decoration: BoxDecoration(
+ color: AppColors.surfaceColor,
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: AppColors.borderColor),
+ ),
+ child: const ThemeToggle(),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/profile/profile_screen.dart b/lib/views/profile/pages/m_profile_page.dart
similarity index 51%
rename from lib/views/profile/profile_screen.dart
rename to lib/views/profile/pages/m_profile_page.dart
index b5cbfb1..eaf59d1 100644
--- a/lib/views/profile/profile_screen.dart
+++ b/lib/views/profile/pages/m_profile_page.dart
@@ -1,13 +1,14 @@
import 'package:flutter/material.dart';
-import 'package:tm_app/core/constants/strings.dart';
-import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/views/shared/tender_app_bar.dart';
-import '../../core/theme/colors.dart';
-import '../../widgets/theme_toggle.dart';
+import '../../../core/constants/strings.dart';
+import '../../../core/theme/colors.dart';
+import '../../../core/utils/size_config.dart';
+import '../widgets/theme_toggle.dart';
+import '../widgets/title_description.dart';
-class ProfileScreen extends StatelessWidget {
- const ProfileScreen({super.key});
+class MobileProfilePage extends StatelessWidget {
+ const MobileProfilePage({super.key});
@override
Widget build(BuildContext context) {
@@ -61,57 +62,3 @@ class ProfileScreen extends StatelessWidget {
);
}
}
-
-class TitleDescription extends StatelessWidget {
- const TitleDescription({
- required this.title,
- required this.description,
- super.key,
- });
-
- final String title;
- final String description;
-
- @override
- Widget build(BuildContext context) {
- return Padding(
- padding: EdgeInsets.only(bottom: 24.0.h()),
-
- child: SizedBox(
- width: double.infinity,
- height: 43.0.h(),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- title,
- style: TextStyle(
- fontSize: 16.0.sp(),
- fontWeight: FontWeight.w400,
- color: AppColors.grey60,
- ),
- ),
- Text(
- description,
- style: TextStyle(
- fontSize: 16.0.sp(),
- fontWeight: FontWeight.w400,
- color: AppColors.grey80,
- ),
- ),
- ],
- ),
- Container(
- width: double.infinity,
- height: 1.0.h(),
- color: AppColors.dividerColor,
- ),
- ],
- ),
- ),
- );
- }
-}
diff --git a/lib/views/profile/pages/profile_screen.dart b/lib/views/profile/pages/profile_screen.dart
new file mode 100644
index 0000000..299c06f
--- /dev/null
+++ b/lib/views/profile/pages/profile_screen.dart
@@ -0,0 +1,19 @@
+import 'package:flutter/material.dart';
+import 'package:tm_app/views/shared/responsive_builder.dart';
+
+import '../pages/d_profile_page.dart';
+import '../pages/m_profile_page.dart';
+import '../pages/t_profile_page.dart';
+
+class ProfileScreen extends StatelessWidget {
+ const ProfileScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return ResponsiveBuilder(
+ mobile: MobileProfilePage(),
+ tablet: TabletProfilePage(),
+ desktop: DesktopProfilePage(),
+ );
+ }
+}
diff --git a/lib/views/profile/pages/t_profile_page.dart b/lib/views/profile/pages/t_profile_page.dart
new file mode 100644
index 0000000..6f38764
--- /dev/null
+++ b/lib/views/profile/pages/t_profile_page.dart
@@ -0,0 +1,61 @@
+import 'package:flutter/material.dart';
+
+import '../../../core/theme/colors.dart';
+import '../../../core/utils/size_config.dart';
+import '../widgets/theme_toggle.dart';
+import '../widgets/title_description.dart';
+
+class TabletProfilePage extends StatelessWidget {
+ const TabletProfilePage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ child: Scaffold(
+ backgroundColor: AppColors.backgroundColor,
+ body: Padding(
+ padding: EdgeInsets.symmetric(
+ horizontal: 24.0.w(),
+ vertical: 16.0.h(),
+ ),
+ child: Column(
+ children: [
+ SizedBox(height: 128.0.h()),
+ TitleDescription(
+ title: 'Company Name',
+ description: 'Telecom Solutions GmbH',
+ ),
+ TitleDescription(
+ title: 'National ID',
+ description: '12345678901',
+ ),
+ TitleDescription(
+ title: 'Registration No.',
+ description: 'DE-55667788',
+ ),
+ TitleDescription(
+ title: 'Business Type',
+ description: 'Telecommunications',
+ ),
+ TitleDescription(title: 'Founded', description: '2010'),
+ SizedBox(height: 24.0.h()),
+ // Theme Toggle
+ Container(
+ padding: EdgeInsets.symmetric(
+ horizontal: 16.0.w(),
+ vertical: 12.0.h(),
+ ),
+ decoration: BoxDecoration(
+ color: AppColors.surfaceColor,
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: AppColors.borderColor),
+ ),
+ child: const ThemeToggle(),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/theme_toggle.dart b/lib/views/profile/widgets/theme_toggle.dart
similarity index 94%
rename from lib/widgets/theme_toggle.dart
rename to lib/views/profile/widgets/theme_toggle.dart
index 53fdabb..522b84a 100644
--- a/lib/widgets/theme_toggle.dart
+++ b/lib/views/profile/widgets/theme_toggle.dart
@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
-import '../core/theme/colors.dart';
-import '../core/theme/theme_provider.dart';
+import '../../../core/theme/colors.dart';
+import '../../../core/theme/theme_provider.dart';
class ThemeToggle extends StatelessWidget {
const ThemeToggle({super.key});
diff --git a/lib/views/profile/widgets/title_description.dart b/lib/views/profile/widgets/title_description.dart
new file mode 100644
index 0000000..11495f8
--- /dev/null
+++ b/lib/views/profile/widgets/title_description.dart
@@ -0,0 +1,57 @@
+import 'package:flutter/material.dart';
+import 'package:tm_app/core/theme/colors.dart';
+import 'package:tm_app/core/utils/size_config.dart';
+
+class TitleDescription extends StatelessWidget {
+ const TitleDescription({
+ required this.title,
+ required this.description,
+ super.key,
+ });
+
+ final String title;
+ final String description;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: EdgeInsets.only(bottom: 24.0.h()),
+
+ child: SizedBox(
+ width: double.infinity,
+ height: 43.0.h(),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ title,
+ style: TextStyle(
+ fontSize: 16.0.sp(),
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey60,
+ ),
+ ),
+ Text(
+ description,
+ style: TextStyle(
+ fontSize: 16.0.sp(),
+ fontWeight: FontWeight.w400,
+ color: AppColors.grey80,
+ ),
+ ),
+ ],
+ ),
+ Container(
+ width: double.infinity,
+ height: 1.0.h(),
+ color: AppColors.dividerColor,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/shared/tender_app_bar.dart b/lib/views/shared/tender_app_bar.dart
index 0a7fbfa..9b03bce 100644
--- a/lib/views/shared/tender_app_bar.dart
+++ b/lib/views/shared/tender_app_bar.dart
@@ -1,72 +1,103 @@
import 'package:flutter/material.dart';
-import 'package:flutter_svg/svg.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';
-PreferredSize tenderMobileAppBar({required String title}) {
+// PreferredSize tenderMobileAppBar({required String title}) {
+// return PreferredSize(
+// preferredSize: Size.fromHeight(kToolbarHeight.h()),
+// child: Container(
+// color: AppColors.backgroundColor,
+// padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
+// height: 56.0.h(),
+// width: double.infinity,
+// child: Row(
+// mainAxisAlignment: MainAxisAlignment.spaceBetween,
+// children: [
+// Text(
+// title,
+// style: TextStyle(
+// fontSize: 20,
+// fontWeight: FontWeight.bold,
+// color: AppColors.grey70,
+// ),
+// ),
+// Image.asset(
+// 'assets/icons/tenderLogo.png',
+// width: 59.0.w(),
+// height: 28.0.h(),
+// fit: BoxFit.cover,
+// ),
+// ],
+// ),
+// ),
+// );
+// }
+
+PreferredSizeWidget tenderMobileAppBar({required String title}) {
return PreferredSize(
- preferredSize: Size.fromHeight(kToolbarHeight.h()),
- child: Container(
- color: AppColors.backgroundColor,
- padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
- height: 56.0.h(),
- width: double.infinity,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- title,
- style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.bold,
- color: AppColors.grey70,
- ),
+ preferredSize: const Size.fromHeight(56),
+ child: AppBar(
+ backgroundColor: AppColors.backgroundColor,
+ elevation: 0,
+ titleSpacing: 0,
+ automaticallyImplyLeading: false,
+ actions: [
+ Image.asset(
+ AssetsManager.tenderLogo,
+ width: 59.0.w(),
+ height: 28.0.h(),
+ fit: BoxFit.cover,
+ ),
+ SizedBox(width: 24.0.w()),
+ ],
+ title: Padding(
+ padding: EdgeInsetsDirectional.only(start: 24.0.w()),
+ child: Text(
+ title,
+ style: TextStyle(
+ color: AppColors.grey70,
+ fontWeight: FontWeight.w600,
+ fontSize: 20.0.sp(),
),
- Image.asset(
- 'assets/icons/tenderLogo.png',
- width: 59.0.w(),
- height: 28.0.h(),
- fit: BoxFit.cover,
- ),
- ],
+ ),
),
),
);
}
-PreferredSize appBar({required String title, required BuildContext context}) {
- return PreferredSize(
- preferredSize: Size.fromHeight(kToolbarHeight.h()),
- child: Container(
- color: AppColors.backgroundColor,
- padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
- height: 56.0.h(),
- width: double.infinity,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- GestureDetector(
- onTap: () {
- Navigator.pop(context);
- },
- child: SvgPicture.asset(
- AssetsManager.arrowLeft,
- width: 24.0.w(),
- height: 24.0.h(),
- ),
- ),
- SizedBox(width: 2.0.w()),
- Text(
- title,
- style: TextStyle(
- fontSize: 20,
- fontWeight: FontWeight.bold,
- color: AppColors.grey70,
- ),
- ),
- ],
- ),
- ),
- );
-}
+// PreferredSize appBar({required String title, required BuildContext context}) {
+// return PreferredSize(
+// preferredSize: Size.fromHeight(kToolbarHeight.h()),
+// child: Container(
+// color: AppColors.backgroundColor,
+// padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
+// height: 56.0.h(),
+// width: double.infinity,
+// child: Row(
+// mainAxisAlignment: MainAxisAlignment.spaceBetween,
+// children: [
+// GestureDetector(
+// onTap: () {
+// Navigator.pop(context);
+// },
+// child: SvgPicture.asset(
+// AssetsManager.arrowLeft,
+// width: 24.0.w(),
+// height: 24.0.h(),
+// ),
+// ),
+// SizedBox(width: 2.0.w()),
+// Text(
+// title,
+// style: TextStyle(
+// fontSize: 20,
+// fontWeight: FontWeight.bold,
+// color: AppColors.grey70,
+// ),
+// ),
+// ],
+// ),
+// ),
+// );
+// }
diff --git a/lib/views/tenders/pages/d_tenders_page.dart b/lib/views/tenders/pages/d_tenders_page.dart
new file mode 100644
index 0000000..b8c2ae4
--- /dev/null
+++ b/lib/views/tenders/pages/d_tenders_page.dart
@@ -0,0 +1,29 @@
+import 'package:flutter/material.dart';
+import 'package:tm_app/core/theme/colors.dart';
+import 'package:tm_app/core/utils/size_config.dart';
+import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
+
+class DesktopTendersPage extends StatelessWidget {
+ const DesktopTendersPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ child: Scaffold(
+ backgroundColor: AppColors.backgroundColor,
+ body: Center(
+ child: SingleChildScrollView(
+ child: SizedBox(
+ width: 740,
+ height: 800,
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 24.0.h()),
+ child: MainTendersSlider(isDesktop: true),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/tenders/tenders_screen.dart b/lib/views/tenders/pages/m_tenders_page.dart
similarity index 57%
rename from lib/views/tenders/tenders_screen.dart
rename to lib/views/tenders/pages/m_tenders_page.dart
index 7a60de9..cd552f0 100644
--- a/lib/views/tenders/tenders_screen.dart
+++ b/lib/views/tenders/pages/m_tenders_page.dart
@@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
-import 'package:tm_app/core/constants/strings.dart';
-import 'package:tm_app/views/shared/tender_app_bar.dart';
+import 'package:tm_app/core/utils/size_config.dart';
+import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
-import '../../core/theme/colors.dart';
-import '../../core/utils/size_config.dart';
-import 'widgets/main_tenders_slider.dart';
+import '../../../core/constants/strings.dart';
+import '../../../core/theme/colors.dart';
+import '../../shared/tender_app_bar.dart';
-class TendersScreen extends StatelessWidget {
- const TendersScreen({super.key});
+class MobileTendersPage extends StatelessWidget {
+ const MobileTendersPage({super.key});
@override
Widget build(BuildContext context) {
diff --git a/lib/views/tenders/pages/t_tenders_page.dart b/lib/views/tenders/pages/t_tenders_page.dart
new file mode 100644
index 0000000..b56cdf0
--- /dev/null
+++ b/lib/views/tenders/pages/t_tenders_page.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+import 'package:tm_app/core/theme/colors.dart';
+import 'package:tm_app/core/utils/size_config.dart';
+import 'package:tm_app/views/tenders/widgets/main_tenders_slider.dart';
+
+class TabletTendersPage extends StatelessWidget {
+ const TabletTendersPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ child: Scaffold(
+ backgroundColor: AppColors.backgroundColor,
+
+ body: SingleChildScrollView(
+ child: Padding(
+ padding: EdgeInsets.symmetric(vertical: 24.0.h()),
+ child: MainTendersSlider(),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/tenders/pages/tenders_screen.dart b/lib/views/tenders/pages/tenders_screen.dart
new file mode 100644
index 0000000..b8bae90
--- /dev/null
+++ b/lib/views/tenders/pages/tenders_screen.dart
@@ -0,0 +1,19 @@
+import 'package:flutter/material.dart';
+import 'package:tm_app/views/shared/responsive_builder.dart';
+
+import 'd_tenders_page.dart';
+import 'm_tenders_page.dart';
+import 't_tenders_page.dart';
+
+class TendersScreen extends StatelessWidget {
+ const TendersScreen({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return ResponsiveBuilder(
+ mobile: MobileTendersPage(),
+ tablet: TabletTendersPage(),
+ desktop: DesktopTendersPage(),
+ );
+ }
+}
diff --git a/lib/views/tenders/widgets/main_tenders_slider.dart b/lib/views/tenders/widgets/main_tenders_slider.dart
index 2ee327b..4d27e20 100644
--- a/lib/views/tenders/widgets/main_tenders_slider.dart
+++ b/lib/views/tenders/widgets/main_tenders_slider.dart
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
-import 'package:tm_app/core/routes/app_routes.dart';
import 'package:tm_app/core/theme/colors.dart';
import '../../../core/constants/assets.dart';
@@ -9,7 +8,8 @@ import '../models/tender_model.dart';
import 'tender_card.dart';
class MainTendersSlider extends StatefulWidget {
- const MainTendersSlider({super.key});
+ const MainTendersSlider({super.key, this.isDesktop = false});
+ final bool isDesktop;
@override
State createState() => _MainTendersSliderState();
@@ -18,37 +18,7 @@ class MainTendersSlider extends StatefulWidget {
class _MainTendersSliderState extends State {
final PageController pageController = PageController(initialPage: 0);
- List likedTenders = [];
- List dislikedTenders = [];
int currentPage = 1;
- List rejectedTenders = [];
- List submittedTenders = [];
-
- void _onDislike(Tender tender) {
- if (likedTenders.contains(tender)) {
- likedTenders.remove(tender);
- }
-
- if (!dislikedTenders.contains(tender)) {
- dislikedTenders.add(tender);
- } else {
- dislikedTenders.remove(tender);
- }
- setState(() {});
- }
-
- void _onLike(Tender tender) {
- if (dislikedTenders.contains(tender)) {
- dislikedTenders.remove(tender);
- }
-
- if (!likedTenders.contains(tender)) {
- likedTenders.add(tender);
- } else {
- likedTenders.remove(tender);
- }
- setState(() {});
- }
void _goToPreviousPage() {
pageController.previousPage(
@@ -76,25 +46,6 @@ class _MainTendersSliderState extends State {
});
}
- void _onReject(Tender tender) {
- if (rejectedTenders.contains(tender)) {
- rejectedTenders.remove(tender);
- } else {
- rejectedTenders.add(tender);
- }
- setState(() {});
- }
-
- void _onSubmit(Tender tender) {
- if (submittedTenders.contains(tender)) {
- submittedTenders.remove(tender);
- } else {
- submittedTenders.add(tender);
- }
-
- setState(() {});
- }
-
@override
void dispose() {
pageController.dispose();
@@ -119,25 +70,7 @@ class _MainTendersSliderState extends State {
},
itemBuilder: (context, index) {
final tender = sampleTenders[index];
- return TenderCard(
- date: tender.date,
- deadline: tender.deadline,
- title: tender.title,
- description: tender.description,
- tenderId: tender.tenderId,
- location: tender.location,
- country: tender.country,
- matchPercentage: tender.matchPercentage,
- isLiked: likedTenders.contains(tender),
- isDisliked: dislikedTenders.contains(tender),
- onDislike: () => _onDislike(tender),
- onLike: () => _onLike(tender),
- onApply: () => TenderDetailRouteData().push(context),
- onReject: () => _onReject(tender),
- onSubmit: () => _onSubmit(tender),
- rejected: rejectedTenders.contains(tender),
- submitted: submittedTenders.contains(tender),
- );
+ return TenderCard(tender: tender, isDesktop: widget.isDesktop);
},
),
),
diff --git a/lib/views/tenders/widgets/tender_action_buttons.dart b/lib/views/tenders/widgets/tender_action_buttons.dart
index f03e233..8cc109f 100644
--- a/lib/views/tenders/widgets/tender_action_buttons.dart
+++ b/lib/views/tenders/widgets/tender_action_buttons.dart
@@ -1,167 +1,207 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
+import 'package:provider/provider.dart';
+import 'package:tm_app/view_models/tenders_view_model.dart';
+import 'package:tm_app/views/tenders/models/tender_model.dart';
import '../../../core/constants/assets.dart';
import '../../../core/theme/colors.dart';
import '../../../core/utils/size_config.dart';
class TenderActionButtons extends StatelessWidget {
- final VoidCallback? onLike;
- final VoidCallback? onDislike;
- final VoidCallback? onClose;
- final VoidCallback? onReject;
- final VoidCallback? onSubmit;
- final bool isLiked;
- final bool isDisliked;
- final bool rejected;
- final bool submitted;
+ final Tender tender;
+ final bool isDesktop;
const TenderActionButtons({
+ required this.tender,
+ this.isDesktop = false,
super.key,
- this.onLike,
- this.onDislike,
- this.onClose,
- this.onReject,
- this.onSubmit,
- this.isLiked = false,
- this.isDisliked = false,
- this.rejected = false,
- this.submitted = false,
});
@override
Widget build(BuildContext context) {
+ final viewModel = context.watch();
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(width: 16.0.w()),
// Dislike button
- GestureDetector(
- onTap: onDislike,
- child: Column(
- children: [
- SvgPicture.asset(
- width: 32.0.w(),
- height: 32.0.w(),
- fit: BoxFit.cover,
- AssetsManager.dislike,
- colorFilter: ColorFilter.mode(
- isDisliked ? AppColors.errorColor : AppColors.grey50,
- BlendMode.srcATop,
+ Tooltip(
+ message: 'Dislike',
+ child: InkWell(
+ onTap: () {
+ viewModel.onDislike(tender);
+ },
+ child: Column(
+ children: [
+ SvgPicture.asset(
+ width: 32.0.w(),
+ height: 32.0.w(),
+ fit: BoxFit.cover,
+ AssetsManager.dislike,
+ colorFilter: ColorFilter.mode(
+ viewModel.dislikedTenders.contains(tender)
+ ? AppColors.errorColor
+ : AppColors.grey50,
+ BlendMode.srcATop,
+ ),
),
- ),
- SizedBox(height: 8.0.h()),
- Text(
- 'Like',
- style: TextStyle(
- fontSize: 12.0.sp(),
- fontWeight: FontWeight.w400,
- color: isDisliked ? AppColors.errorColor : AppColors.grey50,
- ),
- ),
- ],
+ SizedBox(height: 8.0.h()),
+ if (!isDesktop)
+ Text(
+ 'Like',
+ style: TextStyle(
+ fontSize: 12.0.sp(),
+ fontWeight: FontWeight.w400,
+ color:
+ viewModel.dislikedTenders.contains(tender)
+ ? AppColors.errorColor
+ : AppColors.grey50,
+ ),
+ ),
+ ],
+ ),
),
),
SizedBox(width: 32.0.w()),
//reject button
- GestureDetector(
- onTap: onReject,
- child: Column(
- children: [
- Container(
- width: 32.0.w(),
- height: 32.0.w(),
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- border: Border.all(
- color: rejected ? AppColors.errorColor : AppColors.grey60,
- width: 1.5.w(),
+ Tooltip(
+ message: 'Reject',
+ child: InkWell(
+ onTap: () {
+ viewModel.onReject(tender);
+ },
+ child: Column(
+ children: [
+ Container(
+ width: 32.0.w(),
+ height: 32.0.w(),
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(
+ color:
+ viewModel.rejectedTenders.contains(tender)
+ ? AppColors.errorColor
+ : AppColors.grey60,
+ width: 1.5.w(),
+ ),
+ ),
+ alignment: Alignment.center,
+ child: Icon(
+ Icons.close,
+ color:
+ viewModel.rejectedTenders.contains(tender)
+ ? AppColors.errorColor
+ : AppColors.grey60,
+ size: 16,
),
),
- alignment: Alignment.center,
- child: Icon(
- Icons.close,
- color: rejected ? AppColors.errorColor : AppColors.grey60,
- size: 16,
- ),
- ),
- SizedBox(height: 8.0.h()),
- Text(
- 'Reject',
- style: TextStyle(
- fontSize: 12.0.sp(),
- fontWeight: FontWeight.w400,
- color: rejected ? AppColors.errorColor : AppColors.grey60,
- ),
- ),
- ],
+ SizedBox(height: 8.0.h()),
+ if (!isDesktop)
+ Text(
+ 'Reject',
+ style: TextStyle(
+ fontSize: 12.0.sp(),
+ fontWeight: FontWeight.w400,
+ color:
+ viewModel.rejectedTenders.contains(tender)
+ ? AppColors.errorColor
+ : AppColors.grey60,
+ ),
+ ),
+ ],
+ ),
),
),
SizedBox(width: 32.0.w()),
//submit button
- GestureDetector(
- onTap: onSubmit,
- child: Column(
- children: [
- Container(
- width: 32.0.w(),
- height: 32.0.w(),
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- border: Border.all(
- color:
- submitted ? AppColors.successColor : AppColors.grey60,
- width: 1.5.w(),
+ Tooltip(
+ message: 'Submit',
+ child: InkWell(
+ onTap: () {
+ viewModel.onSubmit(tender);
+ },
+ child: Column(
+ children: [
+ Container(
+ width: 32.0.w(),
+ height: 32.0.w(),
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(
+ color:
+ viewModel.submittedTenders.contains(tender)
+ ? AppColors.successColor
+ : AppColors.grey60,
+ width: 1.5.w(),
+ ),
+ ),
+ alignment: Alignment.center,
+ child: SvgPicture.asset(
+ AssetsManager.tick,
+ colorFilter: ColorFilter.mode(
+ viewModel.submittedTenders.contains(tender)
+ ? AppColors.successColor
+ : AppColors.grey60,
+ BlendMode.srcATop,
+ ),
),
),
- alignment: Alignment.center,
- child: SvgPicture.asset(
- AssetsManager.tick,
- colorFilter: ColorFilter.mode(
- submitted ? AppColors.successColor : AppColors.grey60,
- BlendMode.srcATop,
+ SizedBox(height: 8.0.h()),
+ if (!isDesktop)
+ Text(
+ 'Submit',
+ style: TextStyle(
+ fontSize: 12.0.sp(),
+ fontWeight: FontWeight.w400,
+ color:
+ viewModel.submittedTenders.contains(tender)
+ ? AppColors.successColor
+ : AppColors.grey60,
+ ),
),
- ),
- ),
- SizedBox(height: 8.0.h()),
- Text(
- 'Submit',
- style: TextStyle(
- fontSize: 12.0.sp(),
- fontWeight: FontWeight.w400,
- color: submitted ? AppColors.successColor : AppColors.grey60,
- ),
- ),
- ],
+ ],
+ ),
),
),
SizedBox(width: 32.0.w()),
// Like button
- GestureDetector(
- onTap: onLike,
- child: Column(
- children: [
- SvgPicture.asset(
- width: 32.0.w(),
- height: 32.0.w(),
- fit: BoxFit.cover,
- AssetsManager.like,
- colorFilter: ColorFilter.mode(
- isLiked ? AppColors.successColor : AppColors.grey50,
- BlendMode.srcATop,
+ Tooltip(
+ message: 'Like',
+ child: InkWell(
+ onTap: () {
+ viewModel.onLike(tender);
+ },
+ child: Column(
+ children: [
+ SvgPicture.asset(
+ width: 32.0.w(),
+ height: 32.0.w(),
+ fit: BoxFit.cover,
+ AssetsManager.like,
+ colorFilter: ColorFilter.mode(
+ viewModel.likedTenders.contains(tender)
+ ? AppColors.successColor
+ : AppColors.grey50,
+ BlendMode.srcATop,
+ ),
),
- ),
- SizedBox(height: 8.0.h()),
- Text(
- 'Like',
- style: TextStyle(
- fontSize: 12.0.sp(),
- fontWeight: FontWeight.w400,
- color: isLiked ? AppColors.successColor : AppColors.grey50,
- ),
- ),
- ],
+ SizedBox(height: 8.0.h()),
+ if (!isDesktop)
+ Text(
+ 'Like',
+ style: TextStyle(
+ fontSize: 12.0.sp(),
+ fontWeight: FontWeight.w400,
+ color:
+ viewModel.likedTenders.contains(tender)
+ ? AppColors.successColor
+ : AppColors.grey50,
+ ),
+ ),
+ ],
+ ),
),
),
],
diff --git a/lib/views/tenders/widgets/tender_card.dart b/lib/views/tenders/widgets/tender_card.dart
index 72ac79d..f97b95f 100644
--- a/lib/views/tenders/widgets/tender_card.dart
+++ b/lib/views/tenders/widgets/tender_card.dart
@@ -1,52 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:tm_app/core/constants/strings.dart';
+import 'package:tm_app/core/routes/app_routes.dart';
import '../../../core/constants/assets.dart';
import '../../../core/theme/colors.dart';
import '../../../core/utils/size_config.dart';
+import '../models/tender_model.dart';
import 'tender_action_buttons.dart';
class TenderCard extends StatelessWidget {
- final String date;
- final String deadline;
- final String title;
- final String description;
- final String tenderId;
- final String location;
- final String country;
- final double matchPercentage;
- final VoidCallback? onApply;
- final VoidCallback? onLike;
- final VoidCallback? onDislike;
- final VoidCallback? onClose;
- final bool isLiked;
- final bool isDisliked;
- final VoidCallback? onReject;
- final VoidCallback? onSubmit;
- final bool rejected;
- final bool submitted;
- const TenderCard({
- required this.date,
- required this.deadline,
- required this.title,
- required this.description,
- required this.tenderId,
- required this.location,
- required this.country,
- required this.matchPercentage,
- this.onApply,
- this.onLike,
- this.onDislike,
- this.onClose,
- this.isLiked = false,
- this.isDisliked = false,
- this.onReject,
- this.onSubmit,
- this.rejected = false,
- this.submitted = false,
- super.key,
- });
+ final Tender tender;
+ final bool isDesktop;
+ const TenderCard({required this.tender, super.key, this.isDesktop = false});
@override
Widget build(BuildContext context) {
@@ -71,13 +37,23 @@ class TenderCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Date
- Text(
- date,
- style: TextStyle(
- color: AppColors.grey60,
- fontSize: 12.0.sp(),
- fontWeight: FontWeight.w400,
- ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ tender.date,
+ style: TextStyle(
+ color: AppColors.grey60,
+ fontSize: 12.0.sp(),
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ if (isDesktop)
+ TenderActionButtons(
+ tender: tender,
+ isDesktop: isDesktop,
+ ),
+ ],
),
SizedBox(height: 12.0.h()),
@@ -102,7 +78,7 @@ class TenderCard extends StatelessWidget {
),
),
Text(
- deadline,
+ tender.deadline,
style: TextStyle(
color: AppColors.grey80,
fontSize: 14.0.sp(),
@@ -116,7 +92,7 @@ class TenderCard extends StatelessWidget {
// Title
Text(
- title,
+ tender.title,
style: TextStyle(
color: AppColors.grey80,
fontSize: 16.0.sp(),
@@ -127,7 +103,7 @@ class TenderCard extends StatelessWidget {
// Description
Text(
- description,
+ tender.description,
style: TextStyle(
color: AppColors.grey70,
fontSize: 16.0.sp(),
@@ -139,7 +115,7 @@ class TenderCard extends StatelessWidget {
// Tender ID
Text(
- tenderId,
+ tender.tenderId,
style: TextStyle(
color: AppColors.grey,
fontSize: 14.0.sp(),
@@ -168,7 +144,7 @@ class TenderCard extends StatelessWidget {
),
),
Text(
- '${matchPercentage.toInt()}%',
+ '${tender.matchPercentage.toInt()}%',
style: TextStyle(
color: AppColors.secondaryTextColor,
fontSize: 16.0.sp(),
@@ -189,7 +165,7 @@ class TenderCard extends StatelessWidget {
),
child: FractionallySizedBox(
alignment: Alignment.centerLeft,
- widthFactor: matchPercentage / 100,
+ widthFactor: tender.matchPercentage / 100,
child: Container(
decoration: BoxDecoration(
color: AppColors.secondary50,
@@ -210,7 +186,7 @@ class TenderCard extends StatelessWidget {
SvgPicture.asset(AssetsManager.location),
SizedBox(width: 4.0.w()),
Text(
- location,
+ tender.location,
style: TextStyle(
color: AppColors.grey80,
fontSize: 14.0.sp(),
@@ -230,7 +206,9 @@ class TenderCard extends StatelessWidget {
// See More button
GestureDetector(
- onTap: onApply,
+ onTap: () {
+ TenderDetailRouteData().push(context);
+ },
child: Container(
width: 108.0.w(),
height: 40.0.h(),
@@ -258,17 +236,7 @@ class TenderCard extends StatelessWidget {
),
),
SizedBox(height: 72.0.h()),
- TenderActionButtons(
- isDisliked: isDisliked,
- isLiked: isLiked,
- onDislike: onDislike,
- onLike: onLike,
- onClose: onClose,
- onReject: onReject,
- onSubmit: onSubmit,
- rejected: rejected,
- submitted: submitted,
- ),
+ if (!isDesktop) TenderActionButtons(tender: tender),
],
);
}