From 9ea9480818e048288082bfdc8657cdccd61c284c Mon Sep 17 00:00:00 2001 From: amirrezaghabeli Date: Sun, 10 Aug 2025 16:05:03 +0330 Subject: [PATCH] check and fix in ui --- assets/icons/calendar.svg | 9 + assets/icons/menu.svg | 5 + assets/icons/notification.svg | 5 + lib/core/config/dependencies.dart | 2 + lib/core/constants/assets.dart | 8 + lib/core/constants/strings.dart | 1 + lib/core/routes/app_routes.dart | 6 +- .../routes/app_shell/app_shell_screen.dart | 37 +++ .../routes/app_shell/desktop_shell_page.dart | 109 +++++++ .../mobile_shell_page.dart} | 31 +- .../routes/app_shell/tablet_shell_page.dart | 126 ++++++++ lib/core/theme/theme_provider.dart | 3 +- lib/data/services/auth_service.dart | 2 +- lib/main.dart | 1 + lib/view_models/tenders_view_model.dart | 85 ++++++ lib/views/detail/pages/detail_screen.dart | 6 +- lib/views/home/pages/d_home_page.dart | 3 + lib/views/home/pages/m_home_page.dart | 3 + lib/views/home/pages/t_home_page.dart | 3 + lib/views/home/widgets/notification_card.dart | 70 +++++ lib/views/login/pages/login_mobile_page.dart | 2 - lib/views/profile/pages/d_profile_page.dart | 61 ++++ .../m_profile_page.dart} | 67 +--- lib/views/profile/pages/profile_screen.dart | 19 ++ lib/views/profile/pages/t_profile_page.dart | 61 ++++ .../profile}/widgets/theme_toggle.dart | 4 +- .../profile/widgets/title_description.dart | 57 ++++ lib/views/shared/tender_app_bar.dart | 151 +++++---- lib/views/tenders/pages/d_tenders_page.dart | 29 ++ .../m_tenders_page.dart} | 14 +- lib/views/tenders/pages/t_tenders_page.dart | 24 ++ lib/views/tenders/pages/tenders_screen.dart | 19 ++ .../tenders/widgets/main_tenders_slider.dart | 73 +---- .../widgets/tender_action_buttons.dart | 288 ++++++++++-------- lib/views/tenders/widgets/tender_card.dart | 98 ++---- 35 files changed, 1066 insertions(+), 416 deletions(-) create mode 100644 assets/icons/calendar.svg create mode 100644 assets/icons/menu.svg create mode 100644 assets/icons/notification.svg create mode 100644 lib/core/routes/app_shell/app_shell_screen.dart create mode 100644 lib/core/routes/app_shell/desktop_shell_page.dart rename lib/core/routes/{app_shell_screen.dart => app_shell/mobile_shell_page.dart} (81%) create mode 100644 lib/core/routes/app_shell/tablet_shell_page.dart create mode 100644 lib/view_models/tenders_view_model.dart create mode 100644 lib/views/home/widgets/notification_card.dart create mode 100644 lib/views/profile/pages/d_profile_page.dart rename lib/views/profile/{profile_screen.dart => pages/m_profile_page.dart} (51%) create mode 100644 lib/views/profile/pages/profile_screen.dart create mode 100644 lib/views/profile/pages/t_profile_page.dart rename lib/{ => views/profile}/widgets/theme_toggle.dart (94%) create mode 100644 lib/views/profile/widgets/title_description.dart create mode 100644 lib/views/tenders/pages/d_tenders_page.dart rename lib/views/tenders/{tenders_screen.dart => pages/m_tenders_page.dart} (57%) create mode 100644 lib/views/tenders/pages/t_tenders_page.dart create mode 100644 lib/views/tenders/pages/tenders_screen.dart 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), ], ); }