Merge pull request 'check and fix in ui' (#26) from all_fixes into main

Reviewed-on: https://repo.ravanertebat.com/TM/tm_app/pulls/26
This commit is contained in:
a.ghabeli
2025-08-10 16:05:37 +03:30
35 changed files with 1066 additions and 416 deletions
+9
View File
@@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.33203 1.33594V3.33594" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.668 1.33594V3.33594" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.33203 6.0625H13.6654" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 5.66927V11.3359C14 13.3359 13 14.6693 10.6667 14.6693H5.33333C3 14.6693 2 13.3359 2 11.3359V5.66927C2 3.66927 3 2.33594 5.33333 2.33594H10.6667C13 2.33594 14 3.66927 14 5.66927Z" stroke="#777777" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.99764 9.13021H8.00363" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.52889 9.13021H5.53488" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.52889 11.1302H5.53488" stroke="#777777" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+5
View File
@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 9.33594H28" stroke="#888888" stroke-width="1.5" stroke-linecap="round"/>
<path d="M4 16H28" stroke="#888888" stroke-width="1.5" stroke-linecap="round"/>
<path d="M4 22.6641H28" stroke="#888888" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 353 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 5.36719V8.14219" stroke="#0164FF" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
<path d="M10.0171 1.66406C6.9504 1.66406 4.46707 4.1474 4.46707 7.21406V8.96406C4.46707 9.53073 4.23374 10.3807 3.94207 10.8641L2.88374 12.6307C2.23374 13.7224 2.68374 14.9391 3.88374 15.3391C7.86707 16.6641 12.1754 16.6641 16.1587 15.3391C17.2837 14.9641 17.7671 13.6474 17.1587 12.6307L16.1004 10.8641C15.8087 10.3807 15.5754 9.5224 15.5754 8.96406V7.21406C15.5671 4.16406 13.0671 1.66406 10.0171 1.66406Z" stroke="#0164FF" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round"/>
<path d="M12.7766 15.6797C12.7766 17.2047 11.5266 18.4547 10.0016 18.4547C9.24323 18.4547 8.54323 18.138 8.04323 17.638C7.54323 17.138 7.22656 16.438 7.22656 15.6797" stroke="#0164FF" stroke-width="1.5" stroke-miterlimit="10"/>
</svg>

After

Width:  |  Height:  |  Size: 938 B

+2
View File
@@ -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<SingleChildWidget> get viewModels {
(context) =>
TenderDetailViewModel(tenderDetailRepository: context.read()),
),
ChangeNotifierProvider(create: (context) => TendersViewModel()),
];
}
+8
View File
@@ -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';
+1
View File
@@ -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';
+3 -3
View File
@@ -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';
@@ -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,
),
);
}
}
@@ -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<int> 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),
),
),
],
),
),
);
}
}
@@ -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<int> 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',
),
@@ -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<int> onTap;
final GlobalKey<ScaffoldState> _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),
),
),
],
),
),
),
);
}
}
+2 -1
View File
@@ -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<void> toggleTheme() async {
+1 -1
View File
@@ -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');
+1
View File
@@ -43,6 +43,7 @@ class _MyAppState extends State<MyApp> {
return MaterialApp.router(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
scrollBehavior: MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
+85
View File
@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:tm_app/views/tenders/models/tender_model.dart';
class TendersViewModel with ChangeNotifier {
List<Tender> likedTenders = [];
List<Tender> dislikedTenders = [];
int currentPage = 1;
List<Tender> rejectedTenders = [];
List<Tender> 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();
}
}
+3 -3
View File
@@ -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'),
);
}
}
+3
View File
@@ -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!),
+3
View File
@@ -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!),
+3
View File
@@ -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!),
@@ -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(
'Youve 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,
),
),
],
),
),
],
),
);
}
}
@@ -14,8 +14,6 @@ class LoginMobilePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
SizeConfig.init(context);
return Scaffold(
backgroundColor: AppColors.backgroundColor,
body: Center(
@@ -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(),
),
],
),
),
),
),
);
}
}
@@ -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,
),
],
),
),
);
}
}
@@ -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(),
);
}
}
@@ -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(),
),
],
),
),
),
);
}
}
@@ -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});
@@ -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,
),
],
),
),
);
}
}
+91 -60
View File
@@ -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,
// ),
// ),
// ],
// ),
// ),
// );
// }
@@ -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),
),
),
),
),
),
);
}
}
@@ -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) {
@@ -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(),
),
),
),
);
}
}
@@ -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(),
);
}
}
@@ -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<MainTendersSlider> createState() => _MainTendersSliderState();
@@ -18,37 +18,7 @@ class MainTendersSlider extends StatefulWidget {
class _MainTendersSliderState extends State<MainTendersSlider> {
final PageController pageController = PageController(initialPage: 0);
List<Tender> likedTenders = [];
List<Tender> dislikedTenders = [];
int currentPage = 1;
List<Tender> rejectedTenders = [];
List<Tender> 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<MainTendersSlider> {
});
}
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<MainTendersSlider> {
},
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);
},
),
),
@@ -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<TendersViewModel>();
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,
),
),
],
),
),
),
],
+33 -65
View File
@@ -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),
],
);
}