added your tenders screen

This commit is contained in:
amirrezaghabeli
2025-08-05 19:18:47 +03:30
parent 9f6c458e15
commit 21d13ac072
24 changed files with 682 additions and 63 deletions
-1
View File
@@ -17,7 +17,6 @@ linter:
avoid_bool_literals_in_conditional_expressions: true avoid_bool_literals_in_conditional_expressions: true
avoid_catches_without_on_clauses: true avoid_catches_without_on_clauses: true
avoid_catching_errors: true avoid_catching_errors: true
avoid_classes_with_only_static_members: true
avoid_final_parameters: true avoid_final_parameters: true
avoid_function_literals_in_foreach_calls: true avoid_function_literals_in_foreach_calls: true
avoid_relative_lib_imports: true avoid_relative_lib_imports: true
+1 -1
View File
@@ -8,7 +8,7 @@ plugins {
android { android {
namespace = "com.example.tm_app" namespace = "com.example.tm_app"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = "27.0.12077973"
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
+4 -4
View File
@@ -1,5 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C17.5 2 22 6.5 22 12C22 17.5 17.5 22 12 22C6.5 22 2 17.5 2 12C2 6.5 6.5 2 12 2Z" stroke="#9E9E9E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7.9987 14.6693C11.6654 14.6693 14.6654 11.6693 14.6654 8.0026C14.6654 4.33594 11.6654 1.33594 7.9987 1.33594C4.33203 1.33594 1.33203 4.33594 1.33203 8.0026C1.33203 11.6693 4.33203 14.6693 7.9987 14.6693Z" stroke="#C02525" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.17188 9.16813L14.8319 14.8281" stroke="#9E9E9E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M6.11328 9.89052L9.88661 6.11719" stroke="#C02525" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.8319 9.16813L9.17188 14.8281" stroke="#9E9E9E" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M9.88661 9.89052L6.11328 6.11719" stroke="#C02525" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 535 B

After

Width:  |  Height:  |  Size: 602 B

-11
View File
@@ -1,11 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_70_3561)">
<path d="M9.02 2.83998L3.63 7.03998C2.73 7.73998 2 9.22998 2 10.36V17.77C2 20.09 3.89 21.99 6.21 21.99H17.79C20.11 21.99 22 20.09 22 17.78V10.5C22 9.28998 21.19 7.73998 20.2 7.04998L14.02 2.71998C12.62 1.73998 10.37 1.78998 9.02 2.83998Z" fill="#0164FF"/>
<path d="M12 17.99V14.99" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_70_3561">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 610 B

+11
View File
@@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_719_496)">
<path d="M9.02 2.83821L3.63 7.03821C2.73 7.73821 2 9.22821 2 10.3582V17.7682C2 20.0882 3.89 21.9882 6.21 21.9882H17.79C20.11 21.9882 22 20.0882 22 17.7782V10.4982C22 9.28821 21.19 7.73821 20.2 7.04821L14.02 2.71821C12.62 1.73821 10.37 1.78821 9.02 2.83821Z" fill="#0164FF"/>
<path d="M12 17.9922V14.9922" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_719_496">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 633 B

+4
View File
@@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.9987 14.6693C11.6654 14.6693 14.6654 11.6693 14.6654 8.0026C14.6654 4.33594 11.6654 1.33594 7.9987 1.33594C4.33203 1.33594 1.33203 4.33594 1.33203 8.0026C1.33203 11.6693 4.33203 14.6693 7.9987 14.6693Z" stroke="#289744" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.16797 8.00385L7.05464 9.89052L10.8346 6.11719" stroke="#289744" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 509 B

+6
View File
@@ -1,6 +1,9 @@
class AssetsManager { class AssetsManager {
AssetsManager._(); AssetsManager._();
//bottom navigation
static const homeActive = 'assets/icons/home_active.svg';
// login page // login page
static const logo = 'assets/pngs/logo.png'; static const logo = 'assets/pngs/logo.png';
static const userIcon = 'assets/svgs/user_icon.svg'; static const userIcon = 'assets/svgs/user_icon.svg';
@@ -17,4 +20,7 @@ class AssetsManager {
static const location = 'assets/icons/location.svg'; static const location = 'assets/icons/location.svg';
static const arrowCircleLeft = 'assets/icons/arrow-circle-left.svg'; static const arrowCircleLeft = 'assets/icons/arrow-circle-left.svg';
static const arrowCircleRight = 'assets/icons/arrow-circle-right.svg'; static const arrowCircleRight = 'assets/icons/arrow-circle-right.svg';
//your tenders page
static const tickCircle = 'assets/icons/tick-circle.svg';
} }
+13 -2
View File
@@ -8,8 +8,8 @@ class AppColors {
static const Color accentColor = Color(0xFFE83E8C); static const Color accentColor = Color(0xFFE83E8C);
// Backgrounds // Backgrounds
static const Color lightBackgroundColor = Color(0xFFF8F9FA); static const Color lightBackgroundColor = Color(0xFFFFFFFF);
static const Color darkBackgroundColor = Color(0xFF343A40); static const Color darkBackgroundColor = Color(0xFF222222);
// Text Colors // Text Colors
static const Color primaryTextColor = Color(0xFF212529); static const Color primaryTextColor = Color(0xFF212529);
@@ -22,6 +22,8 @@ class AppColors {
static const Color warningColor = Color(0xFFFFC107); static const Color warningColor = Color(0xFFFFC107);
static const Color grey = Color(0xFF555555); static const Color grey = Color(0xFF555555);
static const Color grey20 = Color(0xFFE4E4E4);
static const Color grey40 = Color(0xFFDADADA);
static const Color grey50 = Color(0xFF9E9E9E); static const Color grey50 = Color(0xFF9E9E9E);
static const Color grey60 = Color(0xFF777777); static const Color grey60 = Color(0xFF777777);
static const Color grey70 = Color(0xFF444444); static const Color grey70 = Color(0xFF444444);
@@ -31,4 +33,13 @@ class AppColors {
static const Color backgroundColor = Color(0xFFFFFFFF); static const Color backgroundColor = Color(0xFFFFFFFF);
static const Color borderColor = Color(0xFFE1E1E1); static const Color borderColor = Color(0xFFE1E1E1);
static const Color primary2 = Color(0xFFE5EFFF); static const Color primary2 = Color(0xFFE5EFFF);
static const Color dividerColor = Color(0xFFE1E1E1);
static const Color gren0 = Color(0xFFF9FCF8);
static const Color green10 = Color(0xFFE0F0DC);
static const Color gren20 = Color(0xFFDAF8D6);
static const Color gren30 = Color(0xFF289744);
static const Color red0 = Color(0xFFF8D6D6);
static const Color red10 = Color(0xFFC02525);
} }
+6
View File
@@ -35,4 +35,10 @@ class AppStrings {
//Tenders //Tenders
static const String tendersTitle = 'Tenders'; static const String tendersTitle = 'Tenders';
//profile
static const String profile = 'Profile';
//your tenders
static const String yourTendersTitle = 'Your tenders';
} }
+12 -1
View File
@@ -5,6 +5,7 @@ import '../../views/home/home_screen.dart';
import '../../views/login/login_screen.dart'; import '../../views/login/login_screen.dart';
import '../../views/profile/profile_screen.dart'; import '../../views/profile/profile_screen.dart';
import '../../views/tenders/tenders_screen.dart'; import '../../views/tenders/tenders_screen.dart';
import '../../views/your_tenders/your_tenders_screen.dart';
import 'app_shell_screen.dart'; import 'app_shell_screen.dart';
part 'app_routes.g.dart'; part 'app_routes.g.dart';
@@ -20,7 +21,7 @@ final GlobalKey<NavigatorState> profileNavigatorKey =
// Main app router // Main app router
final GoRouter appRouter = GoRouter( final GoRouter appRouter = GoRouter(
routes: $appRoutes, routes: $appRoutes,
initialLocation: '/login', initialLocation: '/home',
navigatorKey: rootNavigatorKey, navigatorKey: rootNavigatorKey,
); );
@@ -134,3 +135,13 @@ class ProfileRouteData extends GoRouteData with _$ProfileRouteData {
return const ProfileScreen(); return const ProfileScreen();
} }
} }
@TypedGoRoute<YourTendersRouteData>(path: '/your-tenders')
class YourTendersRouteData extends GoRouteData with _$YourTendersRouteData {
const YourTendersRouteData();
@override
Widget build(BuildContext context, GoRouterState state) {
return const YourTendersScreen();
}
}
+32 -1
View File
@@ -6,7 +6,11 @@ part of 'app_routes.dart';
// GoRouterGenerator // GoRouterGenerator
// ************************************************************************** // **************************************************************************
List<RouteBase> get $appRoutes => [$loginScreenRoute, $appShellRouteData]; List<RouteBase> get $appRoutes => [
$loginScreenRoute,
$appShellRouteData,
$yourTendersRouteData,
];
RouteBase get $loginScreenRoute => RouteBase get $loginScreenRoute =>
GoRouteData.$route(path: '/login', factory: _$LoginScreenRoute._fromState); GoRouteData.$route(path: '/login', factory: _$LoginScreenRoute._fromState);
@@ -134,3 +138,30 @@ mixin _$ProfileRouteData on GoRouteData {
@override @override
void replace(BuildContext context) => context.replace(location); void replace(BuildContext context) => context.replace(location);
} }
RouteBase get $yourTendersRouteData => GoRouteData.$route(
path: '/your-tenders',
factory: _$YourTendersRouteData._fromState,
);
mixin _$YourTendersRouteData on GoRouteData {
static YourTendersRouteData _fromState(GoRouterState state) =>
const YourTendersRouteData();
@override
String get location => GoRouteData.$location('/your-tenders');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
+4 -1
View File
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../constants/assets.dart';
import '../utils/size_config.dart'; import '../utils/size_config.dart';
class AppShellScreen extends StatelessWidget { class AppShellScreen extends StatelessWidget {
@@ -19,6 +20,7 @@ class AppShellScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SizeConfig.init(context);
return Scaffold( return Scaffold(
body: navigationShell, body: navigationShell,
bottomNavigationBar: Container( bottomNavigationBar: Container(
@@ -38,7 +40,7 @@ class AppShellScreen extends StatelessWidget {
isActive: navigationShell.currentIndex == 0, isActive: navigationShell.currentIndex == 0,
onTap: () => _gotoBranch(0), onTap: () => _gotoBranch(0),
iconPath: 'assets/icons/home.svg', iconPath: 'assets/icons/home.svg',
activeIconPath: 'assets/icons/homeActive.svg', activeIconPath: AssetsManager.homeActive,
), ),
_bottomNavigationItem( _bottomNavigationItem(
context: context, context: context,
@@ -81,6 +83,7 @@ class AppShellScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
SvgPicture.asset(isActive ? activeIconPath : iconPath), SvgPicture.asset(isActive ? activeIconPath : iconPath),
Text( Text(
text, text,
style: TextStyle( style: TextStyle(
-2
View File
@@ -5,7 +5,6 @@ import 'package:tm_app/core/config/dependencies.dart';
import 'core/routes/app_routes.dart'; import 'core/routes/app_routes.dart';
import 'core/utils/logger.dart'; import 'core/utils/logger.dart';
import 'core/utils/size_config.dart';
void main() { void main() {
// Initialize the logger // Initialize the logger
@@ -23,7 +22,6 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SizeConfig.init(context);
return MaterialApp.router( return MaterialApp.router(
title: 'Flutter Demo', title: 'Flutter Demo',
theme: ThemeData( theme: ThemeData(
+7 -3
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tm_app/core/routes/app_routes.dart';
import '../../../core/constants/strings.dart'; import '../../../core/constants/strings.dart';
import '../../core/constants/colors.dart'; import '../../core/constants/colors.dart';
@@ -10,6 +11,7 @@ class HomeScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SizeConfig.init(context);
return SafeArea( return SafeArea(
child: Scaffold( child: Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
@@ -36,7 +38,7 @@ class HomeScreen extends StatelessWidget {
// SizedBox(height: 32.0.h()), // SizedBox(height: 32.0.h()),
_progressBarsRow(), _progressBarsRow(),
SizedBox(height: 32.0.h()), SizedBox(height: 32.0.h()),
_firstStatisticsRow(), _firstStatisticsRow(context),
SizedBox(height: 8.0.h()), SizedBox(height: 8.0.h()),
_secondStatisticsRow(), _secondStatisticsRow(),
SizedBox(height: 32.0.h()), SizedBox(height: 32.0.h()),
@@ -104,7 +106,7 @@ class HomeScreen extends StatelessWidget {
); );
} }
Widget _firstStatisticsRow() { Widget _firstStatisticsRow(BuildContext context) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@@ -124,7 +126,9 @@ class HomeScreen extends StatelessWidget {
amount: '03', amount: '03',
textColor: Color(0xFF24848E), textColor: Color(0xFF24848E),
enableTap: true, enableTap: true,
onTap: () {}, onTap: () {
YourTendersRouteData().push(context);
},
), ),
], ],
); );
+114 -11
View File
@@ -1,4 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tm_app/core/constants/strings.dart';
import 'package:tm_app/core/utils/size_config.dart';
import '../../core/constants/colors.dart'; import '../../core/constants/colors.dart';
@@ -7,17 +9,118 @@ class ProfileScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return SafeArea(
backgroundColor: AppColors.backgroundColor, child: Scaffold(
appBar: AppBar( backgroundColor: AppColors.backgroundColor,
title: const Text('Profile'), appBar: PreferredSize(
backgroundColor: AppColors.grey10, preferredSize: Size.fromHeight(56.0.h()),
elevation: 0, child: Container(
), color: AppColors.backgroundColor,
body: const Center( padding: EdgeInsets.symmetric(horizontal: 24.0.w()),
child: Text( height: 56.0.h(),
'Profile Screen', width: double.infinity,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
AppStrings.profile,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF222222),
),
),
Image.asset(
'assets/icons/tenderLogo.png',
width: 59.0.w(),
height: 28.0.h(),
fit: BoxFit.cover,
),
],
),
),
),
body: Padding(
padding: EdgeInsets.symmetric(
horizontal: 24.0.w(),
vertical: 16.0.h(),
),
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'),
],
),
),
),
);
}
}
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,
),
],
), ),
), ),
); );
+2 -1
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:tm_app/core/constants/assets.dart';
import '../../core/utils/size_config.dart'; import '../../core/utils/size_config.dart';
@@ -33,7 +34,7 @@ class _TenderBottomNavigationState extends State<TenderBottomNavigation> {
}); });
}, },
iconPath: 'assets/icons/home.svg', iconPath: 'assets/icons/home.svg',
activeIconPath: 'assets/icons/homeActive.svg', activeIconPath: AssetsManager.homeActive,
), ),
_bottomNavigationItem( _bottomNavigationItem(
text: 'Tenders', text: 'Tenders',
@@ -1,13 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../core/constants/colors.dart'; import '../../core/constants/colors.dart';
import '../../../core/utils/size_config.dart'; import '../../core/utils/size_config.dart';
import 'tab_item.dart';
class MainTabBar extends StatefulWidget { class MainTabBar extends StatefulWidget {
const MainTabBar({super.key, required this.controller}); const MainTabBar({required this.controller, required this.titles, super.key});
final TabController controller; final TabController controller;
final List<String> titles;
@override @override
State<MainTabBar> createState() => _MainTabBarState(); State<MainTabBar> createState() => _MainTabBarState();
@@ -41,22 +41,14 @@ class _MainTabBarState extends State<MainTabBar> {
], ],
), ),
controller: widget.controller, controller: widget.controller,
tabs: [ labelColor: AppColors.mainBlue,
TabItem( unselectedLabelColor: AppColors.grey60,
title: 'New', labelStyle: TextStyle(fontSize: 16.0.sp(), fontWeight: FontWeight.w600),
isActive: widget.controller.index == 0, unselectedLabelStyle: TextStyle(
onTap: () { fontSize: 16.0.sp(),
widget.controller.animateTo(0); fontWeight: FontWeight.w600,
}, ),
), tabs: [Tab(text: widget.titles[0]), Tab(text: widget.titles[1])],
TabItem(
title: 'Last',
isActive: widget.controller.index == 1,
onTap: () {
widget.controller.animateTo(1);
},
),
],
), ),
); );
} }
@@ -1,14 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../core/constants/colors.dart'; import '../../core/constants/colors.dart';
import '../../../core/utils/size_config.dart'; import '../../core/utils/size_config.dart';
class TabItem extends StatelessWidget { class TabItem extends StatelessWidget {
const TabItem({ const TabItem({
super.key,
required this.title, required this.title,
required this.isActive, required this.isActive,
required this.onTap, required this.onTap,
super.key,
}); });
final String title; final String title;
+1 -1
View File
@@ -1,4 +1,4 @@
export 'last_tenders_tab.dart'; export 'last_tenders_tab.dart';
export 'main_tab_bar.dart'; export '../../shared/main_tab_bar.dart';
export 'tender_app_bar.dart'; export 'tender_app_bar.dart';
export 'tender_card.dart'; export 'tender_card.dart';
@@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
class TenderModel {
final String date;
final String title;
final String description;
final String location;
final String countryFlag;
final String status;
final String projectStatus;
final Color? backgroundColor;
final Color? borderColor;
final String? statusIcon;
final Color? statusCardColor;
TenderModel({
required this.date,
required this.title,
required this.description,
required this.location,
required this.countryFlag,
required this.status,
required this.projectStatus,
this.backgroundColor,
this.borderColor,
this.statusIcon,
this.statusCardColor,
});
}
List<TenderModel> approvedTenders = [
TenderModel(
date: '2025-01-01',
title:
'Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.',
description:
'Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. ',
location: 'Stockholm, Sweden',
countryFlag: 'assets/icons/SE.png',
status: 'Completed',
projectStatus: 'Self Control',
),
TenderModel(
date: '2025-01-01',
title:
'Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.',
description:
'Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. ',
location: 'Stockholm, Sweden',
countryFlag: 'assets/icons/SE.png',
status: 'Completed',
projectStatus: 'Patner Ship',
),
TenderModel(
date: '2025-01-01',
title:
'Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.',
description:
'Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. ',
location: 'Stockholm, Sweden',
countryFlag: 'assets/icons/SE.png',
status: 'Completed',
projectStatus: 'Self Control',
),
];
List<TenderModel> tendersSubmitted = [
TenderModel(
date: '2025-01-01',
title:
'Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.',
description:
'Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. ',
location: 'Stockholm, Sweden',
countryFlag: 'assets/icons/SE.png',
status: 'Won',
projectStatus: 'Self Control',
),
TenderModel(
date: '2025-01-01',
title:
'Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.',
description:
'Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. ',
location: 'Stockholm, Sweden',
countryFlag: 'assets/icons/SE.png',
status: 'Reject',
projectStatus: 'Patner Ship',
),
TenderModel(
date: '2025-01-01',
title:
'Lorem ipsum dolor sit amet consectetur. Sed mi tortor eu purus senectus.',
description:
'Lorem ipsum dolor sit amet consectetur. Volutpat velit tincidunt amet diam. Placerat congue ut Sed facilisis faucibus porttitor. Proin suspendisse eu euismod sit lorem quis. ',
location: 'Stockholm, Sweden',
countryFlag: 'assets/icons/SE.png',
status: 'Won',
projectStatus: 'Self Control',
),
];
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/views/your_tenders/model/tender_model.dart';
import 'package:tm_app/views/your_tenders/widgets/tender_card.dart';
class ApprovedTenders extends StatelessWidget {
const ApprovedTenders({super.key});
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.only(top: 20.0.h()),
itemCount: approvedTenders.length,
itemBuilder: (context, index) {
final tender = approvedTenders[index];
return TenderCard(
date: tender.date,
title: tender.title,
description: tender.description,
location: tender.location,
countryFlag: tender.countryFlag,
status: tender.status,
projectStatus: tender.projectStatus,
backgroundColor: tender.backgroundColor,
borderColor: tender.borderColor,
statusIcon: tender.statusIcon,
);
},
);
}
}
@@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import '../../../core/constants/assets.dart';
import '../../../core/constants/colors.dart';
import '../../../core/utils/size_config.dart';
class TenderCard extends StatelessWidget {
final String date;
final String title;
final String description;
final String location;
final String countryFlag;
final String status;
final String projectStatus;
final Color? backgroundColor;
final Color? borderColor;
final String? statusIcon;
final Color? statusCardColor;
final Color? statusTextColor;
const TenderCard({
required this.date,
required this.title,
required this.description,
required this.location,
this.countryFlag = 'assets/icons/SE.png',
this.status = 'Completed',
this.projectStatus = 'Self Control',
this.backgroundColor,
this.borderColor,
this.statusIcon,
this.statusCardColor,
this.statusTextColor,
super.key,
});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
margin: EdgeInsets.only(
left: 24.0.w(),
right: 24.0.w(),
bottom: 24.0.h(),
),
decoration: BoxDecoration(
color: backgroundColor ?? AppColors.grey0,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: borderColor ?? AppColors.borderColor),
),
child: Padding(
padding: EdgeInsets.all(16.0.w()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Top section with date and status
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Date
Text(
date,
style: TextStyle(
color: AppColors.grey60,
fontSize: 12.0.sp(),
fontWeight: FontWeight.w400,
),
),
// Completed status badge
Container(
padding: EdgeInsets.symmetric(
horizontal: 16.0.w(),
vertical: 4.0.h(),
),
decoration: BoxDecoration(
color: statusCardColor ?? AppColors.gren20,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
if (statusIcon != null)
SvgPicture.asset(
statusIcon!,
width: 16.0.w(),
height: 16.0.h(),
),
SizedBox(width: 4.0.w()),
Text(
status,
style: TextStyle(
color: statusTextColor ?? AppColors.gren30,
fontSize: 12.0.sp(),
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
SizedBox(height: 12.0.h()),
// Title
Text(
title,
style: TextStyle(
color: AppColors.grey80,
fontSize: 16.0.sp(),
fontWeight: FontWeight.w600,
height: 1.3,
),
),
SizedBox(height: 8.0.h()),
// Description
Text(
description,
style: TextStyle(
color: AppColors.grey70,
fontSize: 14.0.sp(),
fontWeight: FontWeight.w400,
height: 1.4,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 24.0.h()),
// Bottom section with location and action button
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Location with flag
Expanded(
child: Row(
children: [
SvgPicture.asset(
AssetsManager.location,
width: 16.0.w(),
height: 16.0.h(),
),
SizedBox(width: 4.0.w()),
Text(
location,
style: TextStyle(
color: AppColors.grey80,
fontSize: 12.0.sp(),
fontWeight: FontWeight.w400,
),
),
SizedBox(width: 8.0.w()),
// Country flag
Image.asset(
countryFlag,
width: 32.0.w(),
height: 21.0.h(),
),
],
),
),
// Self Control button
Container(
padding: EdgeInsets.symmetric(
horizontal: 16.0.w(),
vertical: 4.0.h(),
),
decoration: BoxDecoration(
color: AppColors.grey20,
borderRadius: BorderRadius.circular(88),
border: Border.all(color: AppColors.grey40),
),
child: Text(
projectStatus,
style: TextStyle(
color: AppColors.grey60,
fontSize: 12.0.sp(),
fontWeight: FontWeight.w500,
),
),
),
],
),
],
),
),
);
}
}
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:tm_app/core/constants/assets.dart';
import 'package:tm_app/core/constants/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/views/your_tenders/model/tender_model.dart';
import 'package:tm_app/views/your_tenders/widgets/tender_card.dart';
class TendersSubmitted extends StatelessWidget {
const TendersSubmitted({super.key});
@override
Widget build(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.only(top: 20.0.h()),
itemCount: tendersSubmitted.length,
itemBuilder: (context, index) {
final tender = tendersSubmitted[index];
return TenderCard(
date: tender.date,
title: tender.title,
description: tender.description,
location: tender.location,
countryFlag: tender.countryFlag,
status: tender.status,
projectStatus: tender.projectStatus,
backgroundColor:
tender.status == 'Won'
? AppColors.gren0
: AppColors.red0.withValues(alpha: 0.2),
borderColor:
tender.status == 'Won' ? AppColors.green10 : AppColors.red0,
statusIcon:
tender.status == 'Won'
? AssetsManager.tickCircle
: AssetsManager.closeCircle,
statusCardColor:
tender.status == 'Won' ? AppColors.gren20 : AppColors.red0,
statusTextColor:
tender.status == 'Won' ? AppColors.gren30 : AppColors.red10,
);
},
);
}
}
@@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:tm_app/core/constants/colors.dart';
import 'package:tm_app/core/constants/strings.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/views/your_tenders/widgets/approved_tenders.dart';
import 'package:tm_app/views/your_tenders/widgets/tenders_submitted.dart';
import '../shared/main_tab_bar.dart';
class YourTendersScreen extends StatefulWidget {
const YourTendersScreen({super.key});
@override
State<YourTendersScreen> createState() => _YourTendersScreenState();
}
class _YourTendersScreenState extends State<YourTendersScreen>
with SingleTickerProviderStateMixin {
late TabController controller;
@override
void initState() {
super.initState();
controller = TabController(length: 2, vsync: this);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: AppColors.backgroundColor,
appBar: PreferredSize(
preferredSize: Size.fromHeight(56.0.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(
AppStrings.yourTenders,
style: TextStyle(
fontSize: 20.0.sp(),
fontWeight: FontWeight.bold,
color: Color(0xFF222222),
),
),
Image.asset(
'assets/icons/tenderLogo.png',
width: 59.0.w(),
height: 28.0.h(),
fit: BoxFit.cover,
),
],
),
),
),
body: Column(
children: [
MainTabBar(
controller: controller,
titles: ['Approved tenders', 'Tenders submitted'],
),
Expanded(
child: TabBarView(
controller: controller,
children: [ApprovedTenders(), TendersSubmitted()],
),
),
],
),
),
);
}
}