From 68a0bcd5827ccc5c44a361293e1b229397f54e82 Mon Sep 17 00:00:00 2001 From: llsajjad Date: Sat, 9 Aug 2025 12:20:42 +0330 Subject: [PATCH 1/6] refactor names in logo and detail and folder stracture --- lib/core/routes/app_routes.dart | 4 ++-- lib/views/detail/detail_screen.dart | 24 ------------------- .../detail_desktop_page.dart} | 4 ++-- .../detail_mobile_page.dart} | 4 ++-- lib/views/detail/pages/detail_screen.dart | 20 ++++++++++++++++ .../detail_tablet_page.dart} | 4 ++-- lib/views/login/login_screen.dart | 24 ------------------- .../login_desktop_page.dart} | 18 +++++++------- .../login_mobile_page.dart} | 18 +++++++------- lib/views/login/pages/login_screen.dart | 20 ++++++++++++++++ .../login_tablet_page.dart} | 18 +++++++------- 11 files changed, 75 insertions(+), 83 deletions(-) delete mode 100644 lib/views/detail/detail_screen.dart rename lib/views/detail/{detail_desktop_screen.dart => pages/detail_desktop_page.dart} (94%) rename lib/views/detail/{detail_mobile_screen.dart => pages/detail_mobile_page.dart} (94%) create mode 100644 lib/views/detail/pages/detail_screen.dart rename lib/views/detail/{detail_tablet_screen.dart => pages/detail_tablet_page.dart} (94%) delete mode 100644 lib/views/login/login_screen.dart rename lib/views/login/{login_desktop_screen.dart => pages/login_desktop_page.dart} (85%) rename lib/views/login/{login_mobile_screen.dart => pages/login_mobile_page.dart} (83%) create mode 100644 lib/views/login/pages/login_screen.dart rename lib/views/login/{login_tablet_screen.dart => pages/login_tablet_page.dart} (85%) diff --git a/lib/core/routes/app_routes.dart b/lib/core/routes/app_routes.dart index 0e74829..eb55985 100644 --- a/lib/core/routes/app_routes.dart +++ b/lib/core/routes/app_routes.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:tm_app/views/detail/detail_screen.dart'; +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/login_screen.dart'; +import 'package:tm_app/views/login/pages/login_screen.dart'; import '../../views/profile/profile_screen.dart'; import '../../views/tenders/tenders_screen.dart'; diff --git a/lib/views/detail/detail_screen.dart b/lib/views/detail/detail_screen.dart deleted file mode 100644 index f51b915..0000000 --- a/lib/views/detail/detail_screen.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tm_app/core/utils/display_size_detector.dart'; -import 'package:tm_app/views/detail/detail_desktop_screen.dart'; -import 'package:tm_app/views/detail/detail_mobile_screen.dart'; -import 'package:tm_app/views/detail/detail_tablet_screen.dart'; - -class TenderDetailScreen extends StatelessWidget { - const TenderDetailScreen({super.key}); - - @override - Widget build(BuildContext context) { - final displaySize = MediaQuery.of(context).size.displaySize; - - switch (displaySize) { - case DisplaySize.large: - case DisplaySize.medium: - return const TenderDetailDesktopScreen(); - case DisplaySize.small: - return const TenderDetailTabletScreen(); - case DisplaySize.extraSmall: - return const TenderDetailMobileScreen(); - } - } -} diff --git a/lib/views/detail/detail_desktop_screen.dart b/lib/views/detail/pages/detail_desktop_page.dart similarity index 94% rename from lib/views/detail/detail_desktop_screen.dart rename to lib/views/detail/pages/detail_desktop_page.dart index f054eeb..5b2efb5 100644 --- a/lib/views/detail/detail_desktop_screen.dart +++ b/lib/views/detail/pages/detail_desktop_page.dart @@ -7,8 +7,8 @@ import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; -class TenderDetailDesktopScreen extends StatelessWidget { - const TenderDetailDesktopScreen({super.key}); +class TenderDetailDesktopPage extends StatelessWidget { + const TenderDetailDesktopPage({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/views/detail/detail_mobile_screen.dart b/lib/views/detail/pages/detail_mobile_page.dart similarity index 94% rename from lib/views/detail/detail_mobile_screen.dart rename to lib/views/detail/pages/detail_mobile_page.dart index eadc2e8..04c841e 100644 --- a/lib/views/detail/detail_mobile_screen.dart +++ b/lib/views/detail/pages/detail_mobile_page.dart @@ -7,8 +7,8 @@ import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; -class TenderDetailMobileScreen extends StatelessWidget { - const TenderDetailMobileScreen({super.key}); +class TenderDetailMobilePage extends StatelessWidget { + const TenderDetailMobilePage({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/views/detail/pages/detail_screen.dart b/lib/views/detail/pages/detail_screen.dart new file mode 100644 index 0000000..92e7db6 --- /dev/null +++ b/lib/views/detail/pages/detail_screen.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:tm_app/core/utils/size_config.dart'; +import 'package:tm_app/views/detail/pages/detail_desktop_page.dart'; +import 'package:tm_app/views/detail/pages/detail_mobile_page.dart'; +import 'package:tm_app/views/detail/pages/detail_tablet_page.dart'; +import 'package:tm_app/views/shared/responsive_builder.dart'; + +class TenderDetailScreen extends StatelessWidget { + const TenderDetailScreen({super.key}); + + @override + Widget build(BuildContext context) { + SizeConfig.init(context); + return ResponsiveBuilder( + mobile: TenderDetailMobilePage(), + tablet: TenderDetailTabletPage(), + desktop: TenderDetailDesktopPage(), + ); + } +} diff --git a/lib/views/detail/detail_tablet_screen.dart b/lib/views/detail/pages/detail_tablet_page.dart similarity index 94% rename from lib/views/detail/detail_tablet_screen.dart rename to lib/views/detail/pages/detail_tablet_page.dart index 03df0c8..21e193e 100644 --- a/lib/views/detail/detail_tablet_screen.dart +++ b/lib/views/detail/pages/detail_tablet_page.dart @@ -7,8 +7,8 @@ import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; -class TenderDetailTabletScreen extends StatelessWidget { - const TenderDetailTabletScreen({super.key}); +class TenderDetailTabletPage extends StatelessWidget { + const TenderDetailTabletPage({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/views/login/login_screen.dart b/lib/views/login/login_screen.dart deleted file mode 100644 index 3846846..0000000 --- a/lib/views/login/login_screen.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tm_app/core/utils/display_size_detector.dart'; -import 'package:tm_app/views/login/login_desktop_screen.dart'; -import 'package:tm_app/views/login/login_mobile_screen.dart'; -import 'package:tm_app/views/login/login_tablet_screen.dart'; - -class LoginScreen extends StatelessWidget { - const LoginScreen({super.key}); - - @override - Widget build(BuildContext context) { - final displaySize = MediaQuery.of(context).size.displaySize; - - switch (displaySize) { - case DisplaySize.large: - case DisplaySize.medium: - return const LoginDesktopScreen(); - case DisplaySize.small: - return const LoginTabletScreen(); - case DisplaySize.extraSmall: - return const LoginMobileScreen(); - } - } -} diff --git a/lib/views/login/login_desktop_screen.dart b/lib/views/login/pages/login_desktop_page.dart similarity index 85% rename from lib/views/login/login_desktop_screen.dart rename to lib/views/login/pages/login_desktop_page.dart index 83c1167..d5fc7a1 100644 --- a/lib/views/login/login_desktop_screen.dart +++ b/lib/views/login/pages/login_desktop_page.dart @@ -2,20 +2,20 @@ import 'package:flutter/material.dart'; import 'package:tm_app/core/routes/app_routes.dart'; -import '../../core/constants/assets.dart'; -import '../../core/constants/strings.dart'; -import '../../core/utils/size_config.dart'; -import 'widgets/widgets.dart'; -import '../../view_models/login_view_model.dart'; +import '../../../core/constants/assets.dart'; +import '../../../core/constants/strings.dart'; +import '../../../core/utils/size_config.dart'; +import '../widgets/widgets.dart'; +import '../../../view_models/login_view_model.dart'; -class LoginDesktopScreen extends StatefulWidget { - const LoginDesktopScreen({super.key}); +class LoginDesktopPage extends StatefulWidget { + const LoginDesktopPage({super.key}); @override - State createState() => _LoginDesktopScreenState(); + State createState() => _LoginDesktopPageState(); } -class _LoginDesktopScreenState extends State { +class _LoginDesktopPageState extends State { late final LoginViewModel _viewModel; @override diff --git a/lib/views/login/login_mobile_screen.dart b/lib/views/login/pages/login_mobile_page.dart similarity index 83% rename from lib/views/login/login_mobile_screen.dart rename to lib/views/login/pages/login_mobile_page.dart index 0515193..bd68703 100644 --- a/lib/views/login/login_mobile_screen.dart +++ b/lib/views/login/pages/login_mobile_page.dart @@ -2,20 +2,20 @@ import 'package:flutter/material.dart'; import 'package:tm_app/core/routes/app_routes.dart'; -import '../../core/constants/assets.dart'; -import '../../core/constants/strings.dart'; -import '../../core/utils/size_config.dart'; -import 'widgets/widgets.dart'; -import '../../view_models/login_view_model.dart'; +import '../../../core/constants/assets.dart'; +import '../../../core/constants/strings.dart'; +import '../../../core/utils/size_config.dart'; +import '../widgets/widgets.dart'; +import '../../../view_models/login_view_model.dart'; -class LoginMobileScreen extends StatefulWidget { - const LoginMobileScreen({super.key}); +class LoginMobilePage extends StatefulWidget { + const LoginMobilePage({super.key}); @override - State createState() => _LoginMobileScreenState(); + State createState() => _LoginMobilePageState(); } -class _LoginMobileScreenState extends State { +class _LoginMobilePageState extends State { late final LoginViewModel _viewModel; @override diff --git a/lib/views/login/pages/login_screen.dart b/lib/views/login/pages/login_screen.dart new file mode 100644 index 0000000..e170e00 --- /dev/null +++ b/lib/views/login/pages/login_screen.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:tm_app/core/utils/size_config.dart'; +import 'package:tm_app/views/login/pages/login_desktop_page.dart'; +import 'package:tm_app/views/login/pages/login_mobile_page.dart'; +import 'package:tm_app/views/login/pages/login_tablet_page.dart'; +import 'package:tm_app/views/shared/responsive_builder.dart'; + +class LoginScreen extends StatelessWidget { + const LoginScreen({super.key}); + + @override + Widget build(BuildContext context) { + SizeConfig.init(context); + return ResponsiveBuilder( + mobile: LoginMobilePage(), + tablet: LoginTabletPage(), + desktop: LoginDesktopPage(), + ); + } +} \ No newline at end of file diff --git a/lib/views/login/login_tablet_screen.dart b/lib/views/login/pages/login_tablet_page.dart similarity index 85% rename from lib/views/login/login_tablet_screen.dart rename to lib/views/login/pages/login_tablet_page.dart index 96bf8e9..d6ab9cf 100644 --- a/lib/views/login/login_tablet_screen.dart +++ b/lib/views/login/pages/login_tablet_page.dart @@ -2,20 +2,20 @@ import 'package:flutter/material.dart'; import 'package:tm_app/core/routes/app_routes.dart'; -import '../../core/constants/assets.dart'; -import '../../core/constants/strings.dart'; -import '../../core/utils/size_config.dart'; -import 'widgets/widgets.dart'; -import '../../view_models/login_view_model.dart'; +import '../../../core/constants/assets.dart'; +import '../../../core/constants/strings.dart'; +import '../../../core/utils/size_config.dart'; +import '../widgets/widgets.dart'; +import '../../../view_models/login_view_model.dart'; -class LoginTabletScreen extends StatefulWidget { - const LoginTabletScreen({super.key}); +class LoginTabletPage extends StatefulWidget { + const LoginTabletPage({super.key}); @override - State createState() => _LoginTabletScreenState(); + State createState() => _LoginTabletPageState(); } -class _LoginTabletScreenState extends State { +class _LoginTabletPageState extends State { late final LoginViewModel _viewModel; @override From 080125b85120b40b7b7836040cac25a63b16bacd Mon Sep 17 00:00:00 2001 From: llsajjad Date: Sat, 9 Aug 2025 13:13:32 +0330 Subject: [PATCH 2/6] fixed colors for darkTheme --- lib/core/theme/colors.dart | 1 + .../detail/pages/detail_desktop_page.dart | 9 +++---- .../detail/pages/detail_mobile_page.dart | 7 +++--- .../detail/pages/detail_tablet_page.dart | 9 +++---- .../detail/widgets/tender_detail_action.dart | 2 +- .../detail/widgets/tender_detail_card.dart | 24 +++++++++---------- .../detail/widgets/tender_detail_header.dart | 2 +- .../widgets/tender_document_section.dart | 3 ++- lib/views/login/pages/login_desktop_page.dart | 3 ++- lib/views/login/pages/login_mobile_page.dart | 3 ++- lib/views/login/pages/login_tablet_page.dart | 3 ++- lib/views/login/widgets/login_button.dart | 6 ++--- lib/views/login/widgets/login_textfield.dart | 4 ++-- lib/views/login/widgets/login_title.dart | 3 ++- 14 files changed, 44 insertions(+), 35 deletions(-) diff --git a/lib/core/theme/colors.dart b/lib/core/theme/colors.dart index 7cb19d1..6249f1c 100644 --- a/lib/core/theme/colors.dart +++ b/lib/core/theme/colors.dart @@ -121,6 +121,7 @@ class AppColors { // Red variations (status colors) static const Color red0 = Color(0xFFF8D6D6); static const Color red10 = Color(0xFFC02525); + static const Color red = Color(0xFFFF3B30); // Other colors static const Color paleOrange = Color(0xFFfff7ee); diff --git a/lib/views/detail/pages/detail_desktop_page.dart b/lib/views/detail/pages/detail_desktop_page.dart index 5b2efb5..651fbf5 100644 --- a/lib/views/detail/pages/detail_desktop_page.dart +++ b/lib/views/detail/pages/detail_desktop_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_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'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; @@ -13,7 +14,7 @@ class TenderDetailDesktopPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, appBar: _buildAppBar(context), body: SingleChildScrollView( child: Padding( @@ -24,7 +25,7 @@ class TenderDetailDesktopPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TenderDetailHeader(isBig: true,), + TenderDetailHeader(isBig: true), SizedBox(height: 24.0.h()), const TenderDetailCard(), SizedBox(height: 24.0.h()), @@ -42,7 +43,7 @@ class TenderDetailDesktopPage extends StatelessWidget { return PreferredSize( preferredSize: const Size.fromHeight(60), child: AppBar( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, elevation: 0, titleSpacing: 0, leading: IconButton( @@ -56,7 +57,7 @@ class TenderDetailDesktopPage extends StatelessWidget { title: Text( AppStrings.tenderDetailTitle, style: TextStyle( - color: Colors.black, + color: AppColors.grey70, fontWeight: FontWeight.w600, fontSize: 20.0.sp(), ), diff --git a/lib/views/detail/pages/detail_mobile_page.dart b/lib/views/detail/pages/detail_mobile_page.dart index 04c841e..829d12c 100644 --- a/lib/views/detail/pages/detail_mobile_page.dart +++ b/lib/views/detail/pages/detail_mobile_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_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'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; @@ -13,7 +14,7 @@ class TenderDetailMobilePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, appBar: _buildAppBar(context), body: SingleChildScrollView( child: Padding( @@ -37,7 +38,7 @@ class TenderDetailMobilePage extends StatelessWidget { return PreferredSize( preferredSize: const Size.fromHeight(60), child: AppBar( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, elevation: 0, titleSpacing: 0, leading: IconButton( @@ -51,7 +52,7 @@ class TenderDetailMobilePage extends StatelessWidget { title: Text( AppStrings.tenderDetailTitle, style: TextStyle( - color: Colors.black, + color: AppColors.grey70, fontWeight: FontWeight.w600, fontSize: 20.0.sp(), ), diff --git a/lib/views/detail/pages/detail_tablet_page.dart b/lib/views/detail/pages/detail_tablet_page.dart index 21e193e..643cd7c 100644 --- a/lib/views/detail/pages/detail_tablet_page.dart +++ b/lib/views/detail/pages/detail_tablet_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_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'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; @@ -13,7 +14,7 @@ class TenderDetailTabletPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, appBar: _buildAppBar(context), body: SingleChildScrollView( child: Padding( @@ -24,7 +25,7 @@ class TenderDetailTabletPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TenderDetailHeader(isBig: true,), + TenderDetailHeader(isBig: true), SizedBox(height: 24.0.h()), const TenderDetailCard(), SizedBox(height: 24.0.h()), @@ -42,7 +43,7 @@ class TenderDetailTabletPage extends StatelessWidget { return PreferredSize( preferredSize: const Size.fromHeight(60), child: AppBar( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, elevation: 0, titleSpacing: 0, leading: IconButton( @@ -56,7 +57,7 @@ class TenderDetailTabletPage extends StatelessWidget { title: Text( AppStrings.tenderDetailTitle, style: TextStyle( - color: Colors.black, + color: AppColors.grey70, fontWeight: FontWeight.w600, fontSize: 20.0.sp(), ), diff --git a/lib/views/detail/widgets/tender_detail_action.dart b/lib/views/detail/widgets/tender_detail_action.dart index 0019d01..514ee7d 100644 --- a/lib/views/detail/widgets/tender_detail_action.dart +++ b/lib/views/detail/widgets/tender_detail_action.dart @@ -24,7 +24,7 @@ class TenderDetailActions extends StatelessWidget { text: AppStrings.tenderRejectButton, borderColor: AppColors.red10, textColor: AppColors.red10, - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, borderWidth: 1, onPressed: () {}, ), diff --git a/lib/views/detail/widgets/tender_detail_card.dart b/lib/views/detail/widgets/tender_detail_card.dart index 4b59081..59476fb 100644 --- a/lib/views/detail/widgets/tender_detail_card.dart +++ b/lib/views/detail/widgets/tender_detail_card.dart @@ -20,11 +20,11 @@ class TenderDetailCard extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( AppStrings.tenderMatchProfile, style: TextStyle( - fontSize: 12.0, - color: Colors.black87, + fontSize: 12.0.sp(), + color: AppColors.grey80, fontWeight: FontWeight.w400, ), ), @@ -46,7 +46,7 @@ class TenderDetailCard extends StatelessWidget { value: 0.75, backgroundColor: AppColors.grey20, valueColor: AlwaysStoppedAnimation(AppColors.jellyBean), - minHeight: 6, + minHeight: 6.0.h(), ), ), SizedBox(height: 16.0.h()), @@ -54,26 +54,26 @@ class TenderDetailCard extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Icon(Icons.warning_rounded, color: Colors.red), + const Icon(Icons.warning_rounded, color: AppColors.red), SizedBox(width: 8.0.w()), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ + children: [ Text( AppStrings.tenderIncompleteResume, style: TextStyle( fontWeight: FontWeight.w600, - fontSize: 16.0, - color: Colors.black87, + fontSize: 16.0.sp(), + color: AppColors.grey80, ), ), - SizedBox(height: 4.0), + SizedBox(height: 4.0.h()), Text( AppStrings.tenderNoExperience, style: TextStyle( - fontSize: 14.0, - color: Colors.black54, + fontSize: 14.0.sp(), + color: AppColors.grey70, fontWeight: FontWeight.w400, ), ), @@ -86,4 +86,4 @@ class TenderDetailCard extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/detail/widgets/tender_detail_header.dart b/lib/views/detail/widgets/tender_detail_header.dart index 1d814d1..6ee55f7 100644 --- a/lib/views/detail/widgets/tender_detail_header.dart +++ b/lib/views/detail/widgets/tender_detail_header.dart @@ -16,7 +16,7 @@ class TenderDetailHeader extends StatelessWidget { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: AppColors.backgroundColor, borderRadius: BorderRadius.circular(5), border: Border.all(width: 0.5, color: AppColors.grey50), ), diff --git a/lib/views/detail/widgets/tender_document_section.dart b/lib/views/detail/widgets/tender_document_section.dart index f2e59aa..c716f2d 100644 --- a/lib/views/detail/widgets/tender_document_section.dart +++ b/lib/views/detail/widgets/tender_document_section.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_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 TenderDocumentSection extends StatelessWidget { @@ -19,7 +20,7 @@ class TenderDocumentSection extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.w400, fontSize: 16.0.sp(), - color: Colors.blue, + color: AppColors.textBlue, decoration: TextDecoration.underline, ), ), diff --git a/lib/views/login/pages/login_desktop_page.dart b/lib/views/login/pages/login_desktop_page.dart index d5fc7a1..12ea8a3 100644 --- a/lib/views/login/pages/login_desktop_page.dart +++ b/lib/views/login/pages/login_desktop_page.dart @@ -1,6 +1,7 @@ // lib/views/login_desktop_screen.dart import 'package:flutter/material.dart'; import 'package:tm_app/core/routes/app_routes.dart'; +import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; @@ -34,7 +35,7 @@ class _LoginDesktopPageState extends State { Widget build(BuildContext context) { SizeConfig.init(context); return Scaffold( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, body: Center( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 24.0.w()), diff --git a/lib/views/login/pages/login_mobile_page.dart b/lib/views/login/pages/login_mobile_page.dart index bd68703..2f1ac24 100644 --- a/lib/views/login/pages/login_mobile_page.dart +++ b/lib/views/login/pages/login_mobile_page.dart @@ -1,6 +1,7 @@ // lib/views/login_screen.dart import 'package:flutter/material.dart'; import 'package:tm_app/core/routes/app_routes.dart'; +import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; @@ -35,7 +36,7 @@ class _LoginMobilePageState extends State { SizeConfig.init(context); return Scaffold( - backgroundColor: Colors.white, + backgroundColor: AppColors.backgroundColor, body: Center( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 24.0.w()), diff --git a/lib/views/login/pages/login_tablet_page.dart b/lib/views/login/pages/login_tablet_page.dart index d6ab9cf..255b7c3 100644 --- a/lib/views/login/pages/login_tablet_page.dart +++ b/lib/views/login/pages/login_tablet_page.dart @@ -1,6 +1,7 @@ // lib/views/login_tablet_screen.dart import 'package:flutter/material.dart'; import 'package:tm_app/core/routes/app_routes.dart'; +import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; @@ -34,7 +35,7 @@ class _LoginTabletPageState extends State { Widget build(BuildContext context) { SizeConfig.init(context); return Scaffold( - backgroundColor: Colors.white, + backgroundColor:AppColors.backgroundColor, body: Center( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 24.0.w()), diff --git a/lib/views/login/widgets/login_button.dart b/lib/views/login/widgets/login_button.dart index 10c58de..9b7d34a 100644 --- a/lib/views/login/widgets/login_button.dart +++ b/lib/views/login/widgets/login_button.dart @@ -38,8 +38,8 @@ class BaseButton extends StatelessWidget { style: ElevatedButton.styleFrom( backgroundColor: backgroundColor ?? - (isEnabled ? AppColors.mainBlue : Colors.grey[300]), - foregroundColor: textColor ?? Colors.white, + (isEnabled ? AppColors.mainBlue : AppColors.backgroundColor), + foregroundColor: textColor ?? AppColors.backgroundColor, elevation: elevation ?? 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadius ?? 28.0.w()), @@ -54,7 +54,7 @@ class BaseButton extends StatelessWidget { style: TextStyle( fontSize: 16.0.sp(), fontWeight: FontWeight.bold, - color: textColor ?? Colors.white, + color: textColor ?? AppColors.backgroundColor, ), ), ), diff --git a/lib/views/login/widgets/login_textfield.dart b/lib/views/login/widgets/login_textfield.dart index a98561a..696eb36 100644 --- a/lib/views/login/widgets/login_textfield.dart +++ b/lib/views/login/widgets/login_textfield.dart @@ -52,7 +52,7 @@ class LoginTextField extends StatelessWidget { width: 24.0.w(), height: 24.0.h(), colorFilter: ColorFilter.mode( - focusNode.hasFocus ? Colors.black87 : Colors.grey, + focusNode.hasFocus ? AppColors.grey80 : AppColors.grey60, BlendMode.srcIn, ), ), @@ -68,7 +68,7 @@ class LoginTextField extends StatelessWidget { width: 24.0.w(), height: 24.0.h(), colorFilter: ColorFilter.mode( - focusNode.hasFocus ? Colors.black87 : Colors.grey, + focusNode.hasFocus ? AppColors.grey80 : AppColors.grey60, BlendMode.srcIn, ), ), diff --git a/lib/views/login/widgets/login_title.dart b/lib/views/login/widgets/login_title.dart index 174a936..5ee7eca 100644 --- a/lib/views/login/widgets/login_title.dart +++ b/lib/views/login/widgets/login_title.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/strings.dart'; import '../../../core/utils/size_config.dart'; @@ -20,7 +21,7 @@ class LoginTitle extends StatelessWidget { style: TextStyle( fontSize: 14.0.sp(), fontWeight: FontWeight.w400, - color: Colors.grey, + color: AppColors.grey60, ), textAlign: TextAlign.center, ), From e468ec9c43ce936dfad7827e1663edff6dba815a Mon Sep 17 00:00:00 2001 From: llsajjad Date: Sat, 9 Aug 2025 16:55:29 +0330 Subject: [PATCH 3/6] Fixed size on web and changed details design button in desktop and tablet --- .../detail/pages/detail_desktop_page.dart | 6 +- .../detail/pages/detail_mobile_page.dart | 6 +- .../detail/pages/detail_tablet_page.dart | 6 +- lib/views/detail/widgets/deadline_item.dart | 278 +++++++++--------- .../detail/widgets/tender_detail_action.dart | 71 +++-- .../detail/widgets/tender_detail_header.dart | 6 +- .../widgets/tender_detail_info_section.dart | 7 +- lib/views/login/pages/login_desktop_page.dart | 11 +- lib/views/login/pages/login_screen.dart | 2 +- lib/views/login/pages/login_tablet_page.dart | 13 +- lib/views/login/widgets/login_button.dart | 4 +- 11 files changed, 225 insertions(+), 185 deletions(-) diff --git a/lib/views/detail/pages/detail_desktop_page.dart b/lib/views/detail/pages/detail_desktop_page.dart index 651fbf5..4829a59 100644 --- a/lib/views/detail/pages/detail_desktop_page.dart +++ b/lib/views/detail/pages/detail_desktop_page.dart @@ -13,6 +13,8 @@ class TenderDetailDesktopPage extends StatelessWidget { @override Widget build(BuildContext context) { + SizeConfig.init(context); + return Scaffold( backgroundColor: AppColors.backgroundColor, appBar: _buildAppBar(context), @@ -25,11 +27,11 @@ class TenderDetailDesktopPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TenderDetailHeader(isBig: true), + TenderDetailHeader(isScreenBig: true), SizedBox(height: 24.0.h()), const TenderDetailCard(), SizedBox(height: 24.0.h()), - TenderDetailActions(), + TenderDetailActions(isScreenBig: true), ], ), ), diff --git a/lib/views/detail/pages/detail_mobile_page.dart b/lib/views/detail/pages/detail_mobile_page.dart index 829d12c..9a71896 100644 --- a/lib/views/detail/pages/detail_mobile_page.dart +++ b/lib/views/detail/pages/detail_mobile_page.dart @@ -22,11 +22,11 @@ class TenderDetailMobilePage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TenderDetailHeader(isBig: false), + TenderDetailHeader(isScreenBig: false), SizedBox(height: 24.0.h()), const TenderDetailCard(), SizedBox(height: 24.0.h()), - TenderDetailActions(), + TenderDetailActions(isScreenBig: false,), ], ), ), @@ -38,7 +38,7 @@ class TenderDetailMobilePage extends StatelessWidget { return PreferredSize( preferredSize: const Size.fromHeight(60), child: AppBar( - backgroundColor: AppColors.backgroundColor, + backgroundColor: AppColors.backgroundColor, elevation: 0, titleSpacing: 0, leading: IconButton( diff --git a/lib/views/detail/pages/detail_tablet_page.dart b/lib/views/detail/pages/detail_tablet_page.dart index 643cd7c..b567541 100644 --- a/lib/views/detail/pages/detail_tablet_page.dart +++ b/lib/views/detail/pages/detail_tablet_page.dart @@ -25,11 +25,11 @@ class TenderDetailTabletPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TenderDetailHeader(isBig: true), + TenderDetailHeader(isScreenBig: true), SizedBox(height: 24.0.h()), const TenderDetailCard(), SizedBox(height: 24.0.h()), - TenderDetailActions(), + TenderDetailActions(isScreenBig: true,), ], ), ), @@ -43,7 +43,7 @@ class TenderDetailTabletPage extends StatelessWidget { return PreferredSize( preferredSize: const Size.fromHeight(60), child: AppBar( - backgroundColor: AppColors.backgroundColor, + backgroundColor: AppColors.backgroundColor, elevation: 0, titleSpacing: 0, leading: IconButton( diff --git a/lib/views/detail/widgets/deadline_item.dart b/lib/views/detail/widgets/deadline_item.dart index 81ffba5..23947ef 100644 --- a/lib/views/detail/widgets/deadline_item.dart +++ b/lib/views/detail/widgets/deadline_item.dart @@ -8,156 +8,164 @@ class DeadlineItem extends StatelessWidget { final String approvalDate; final String submissionText; final String submissionDate; - final bool isBig; + final bool isScreenBig; const DeadlineItem({ - required this.title, required this.approvalText, required this.approvalDate, required this.submissionText, required this.submissionDate, required this.isBig, super.key, + required this.title, + required this.approvalText, + required this.approvalDate, + required this.submissionText, + required this.submissionDate, + required this.isScreenBig, + super.key, }); @override Widget build(BuildContext context) { - return isBig ? Padding( - padding: EdgeInsets.symmetric(vertical: 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: 45.0.h(), - child: Text( - title, - style: TextStyle( - color: AppColors.grey80, - fontWeight: FontWeight.w600, - fontSize: 16.0.sp(), - ), - ), - ), - IntrinsicHeight( - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - approvalText, - style: TextStyle( - color: AppColors.grey70, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), - ), - SizedBox(height: 4.0.h()), - Text( - approvalDate, - style: TextStyle( - color: AppColors.grey80, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), - ), - ], + return isScreenBig + ? Padding( + padding: EdgeInsets.symmetric(vertical: 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 45.0.h(), + child: Text( + title, + style: TextStyle( + color: AppColors.grey80, + fontWeight: FontWeight.w600, + fontSize: 16.0.sp(), ), ), - VerticalDivider( - color: AppColors.grey30, - thickness: 1, - width: 24, + ), + IntrinsicHeight( + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + approvalText, + style: TextStyle( + color: AppColors.grey70, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + SizedBox(height: 4.0.h()), + Text( + approvalDate, + style: TextStyle( + color: AppColors.grey80, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + ], + ), + ), + VerticalDivider( + color: AppColors.grey30, + thickness: 1, + width: 24, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + submissionText, + style: TextStyle( + color: AppColors.grey70, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + SizedBox(height: 4.0.h()), + Text( + submissionDate, + style: TextStyle( + color: AppColors.grey80, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + ], + ), + ), + ], ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - submissionText, - style: TextStyle( - color: AppColors.grey70, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), - ), - SizedBox(height: 4.0.h()), - Text( - submissionDate, - style: TextStyle( - color: AppColors.grey80, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), - ), - ], + ), + SizedBox(height: 8.0.h()), + const Divider(), + ], + ), + ) + : Padding( + padding: EdgeInsets.symmetric(vertical: 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 45.0.h(), + child: Text( + title, + style: TextStyle( + color: AppColors.grey80, + fontWeight: FontWeight.w600, + fontSize: 16.0.sp(), ), ), - ], - ), - ), - SizedBox(height: 8.0.h()), - const Divider(), - ], - ), - ) : Padding( - padding: EdgeInsets.symmetric(vertical: 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: 45.0.h(), - child: Text( - title, - style: TextStyle( - color: AppColors.grey80, - fontWeight: FontWeight.w600, - fontSize: 16.0.sp(), ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - approvalText, - style: TextStyle( - color: AppColors.grey70, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + approvalText, + style: TextStyle( + color: AppColors.grey70, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + Text( + approvalDate, + style: TextStyle( + color: AppColors.grey70, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + ], ), - Text( - approvalDate, - style: TextStyle( - color: AppColors.grey70, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), + SizedBox(height: 4.0.h()), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + submissionText, + style: TextStyle( + color: AppColors.grey70, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + Text( + submissionDate, + style: TextStyle( + color: AppColors.grey70, + fontWeight: FontWeight.w400, + fontSize: 14.0.sp(), + ), + ), + ], ), + SizedBox(height: 8.0.h()), + const Divider(), ], ), - SizedBox(height: 4.0.h()), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - submissionText, - style: TextStyle( - color: AppColors.grey70, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), - ), - Text( - submissionDate, - style: TextStyle( - color: AppColors.grey70, - fontWeight: FontWeight.w400, - fontSize: 14.0.sp(), - ), - ), - ], - ), - SizedBox(height: 8.0.h()), - const Divider(), - ], - ), - ); + ); } } diff --git a/lib/views/detail/widgets/tender_detail_action.dart b/lib/views/detail/widgets/tender_detail_action.dart index 514ee7d..21db0a6 100644 --- a/lib/views/detail/widgets/tender_detail_action.dart +++ b/lib/views/detail/widgets/tender_detail_action.dart @@ -5,31 +5,56 @@ import 'package:tm_app/core/utils/size_config.dart'; import 'package:tm_app/views/login/widgets/login_button.dart'; class TenderDetailActions extends StatelessWidget { - const TenderDetailActions({super.key}); + final bool isScreenBig; + const TenderDetailActions({required this.isScreenBig, super.key}); @override Widget build(BuildContext context) { - return Column( - children: [ - BaseButton( - isEnabled: true, - text: AppStrings.tenderSubmitButton, - backgroundColor: AppColors.lightBlue, - textColor: AppColors.mainBlue, - onPressed: () {}, - ), - SizedBox(height: 16.0.h()), - BaseButton( - isEnabled: true, - text: AppStrings.tenderRejectButton, - borderColor: AppColors.red10, - textColor: AppColors.red10, - backgroundColor: AppColors.backgroundColor, - borderWidth: 1, - onPressed: () {}, - ), - SizedBox(height: 16.0.h()), - ], - ); + return isScreenBig + ? Row( + children: [ + Spacer(), + BaseButton( + isEnabled: true, + text: AppStrings.tenderRejectButton, + textColor: AppColors.red10, + backgroundColor: AppColors.backgroundColor, + borderWidth: 1, + onPressed: () {}, + width: 120.0.w(), + ), + SizedBox(width: 5.0.w()), + BaseButton( + isEnabled: true, + text: AppStrings.tenderSubmitButton, + backgroundColor: AppColors.lightBlue, + textColor: AppColors.mainBlue, + onPressed: () {}, + width: 120.0.w(), + ), + ], + ) + : Column( + children: [ + BaseButton( + isEnabled: true, + text: AppStrings.tenderSubmitButton, + backgroundColor: AppColors.lightBlue, + textColor: AppColors.mainBlue, + onPressed: () {}, + ), + SizedBox(height: 16.0.h()), + BaseButton( + isEnabled: true, + text: AppStrings.tenderRejectButton, + borderColor: AppColors.red10, + textColor: AppColors.red10, + backgroundColor: AppColors.backgroundColor, + borderWidth: 1, + onPressed: () {}, + ), + SizedBox(height: 16.0.h()), + ], + ); } } diff --git a/lib/views/detail/widgets/tender_detail_header.dart b/lib/views/detail/widgets/tender_detail_header.dart index 6ee55f7..a1a71aa 100644 --- a/lib/views/detail/widgets/tender_detail_header.dart +++ b/lib/views/detail/widgets/tender_detail_header.dart @@ -8,8 +8,8 @@ import 'package:tm_app/views/detail/widgets/tender_document_section.dart'; import 'package:tm_app/views/detail/widgets/tender_location_section.dart'; class TenderDetailHeader extends StatelessWidget { - final bool isBig; - const TenderDetailHeader({required this.isBig, super.key}); + final bool isScreenBig; + const TenderDetailHeader({required this.isScreenBig, super.key}); @override Widget build(BuildContext context) { @@ -29,7 +29,7 @@ class TenderDetailHeader extends StatelessWidget { SizedBox(height: 10.0.h()), const Divider(), SizedBox(height: 5.0.h()), - TenderDetailInfoSection(isBig: isBig,), + TenderDetailInfoSection(isScreenBig: isScreenBig), TenderLocationSection(), SizedBox(height: 16.0.h()), TenderDocumentSection(), diff --git a/lib/views/detail/widgets/tender_detail_info_section.dart b/lib/views/detail/widgets/tender_detail_info_section.dart index eaac5d5..440829d 100644 --- a/lib/views/detail/widgets/tender_detail_info_section.dart +++ b/lib/views/detail/widgets/tender_detail_info_section.dart @@ -4,9 +4,9 @@ import 'package:tm_app/views/detail/widgets/deadline_item.dart'; import 'package:tm_app/views/detail/widgets/info_item.dart'; class TenderDetailInfoSection extends StatelessWidget { - final bool isBig; + final bool isScreenBig; - const TenderDetailInfoSection({required this.isBig ,super.key}); + const TenderDetailInfoSection({required this.isScreenBig, super.key}); @override Widget build(BuildContext context) { @@ -22,7 +22,8 @@ class TenderDetailInfoSection extends StatelessWidget { approvalText: AppStrings.tenderApprovalText, approvalDate: AppStrings.tenderApprovalDateExample, submissionText: AppStrings.tenderSubmissionText, - submissionDate: AppStrings.tenderSubmissionDateExample, isBig: isBig, + submissionDate: AppStrings.tenderSubmissionDateExample, + isScreenBig: isScreenBig, ), InfoItem( title: AppStrings.tenderClientLabel, diff --git a/lib/views/login/pages/login_desktop_page.dart b/lib/views/login/pages/login_desktop_page.dart index 12ea8a3..f9bf5ed 100644 --- a/lib/views/login/pages/login_desktop_page.dart +++ b/lib/views/login/pages/login_desktop_page.dart @@ -6,8 +6,8 @@ import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; import '../../../core/utils/size_config.dart'; -import '../widgets/widgets.dart'; import '../../../view_models/login_view_model.dart'; +import '../widgets/widgets.dart'; class LoginDesktopPage extends StatefulWidget { const LoginDesktopPage({super.key}); @@ -40,7 +40,7 @@ class _LoginDesktopPageState extends State { child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 24.0.w()), child: SizedBox( - width: 120.0.w(), + width: 526, child: ListenableBuilder( listenable: _viewModel, builder: (context, _) { @@ -79,9 +79,10 @@ class _LoginDesktopPageState extends State { BaseButton( isEnabled: _viewModel.isLoginEnabled, - onPressed: _viewModel.isLoginEnabled - ? () => HomeRouteData().go(context) - : null, + onPressed: + _viewModel.isLoginEnabled + ? () => HomeRouteData().go(context) + : null, ), ], ); diff --git a/lib/views/login/pages/login_screen.dart b/lib/views/login/pages/login_screen.dart index e170e00..6a0e0e7 100644 --- a/lib/views/login/pages/login_screen.dart +++ b/lib/views/login/pages/login_screen.dart @@ -12,7 +12,7 @@ class LoginScreen extends StatelessWidget { Widget build(BuildContext context) { SizeConfig.init(context); return ResponsiveBuilder( - mobile: LoginMobilePage(), + mobile: LoginMobilePage(), tablet: LoginTabletPage(), desktop: LoginDesktopPage(), ); diff --git a/lib/views/login/pages/login_tablet_page.dart b/lib/views/login/pages/login_tablet_page.dart index 255b7c3..64a36aa 100644 --- a/lib/views/login/pages/login_tablet_page.dart +++ b/lib/views/login/pages/login_tablet_page.dart @@ -6,8 +6,8 @@ import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; import '../../../core/utils/size_config.dart'; -import '../widgets/widgets.dart'; import '../../../view_models/login_view_model.dart'; +import '../widgets/widgets.dart'; class LoginTabletPage extends StatefulWidget { const LoginTabletPage({super.key}); @@ -35,12 +35,12 @@ class _LoginTabletPageState extends State { Widget build(BuildContext context) { SizeConfig.init(context); return Scaffold( - backgroundColor:AppColors.backgroundColor, + backgroundColor: AppColors.backgroundColor, body: Center( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 24.0.w()), child: SizedBox( - width: 120.0.w(), + width: 526, child: ListenableBuilder( listenable: _viewModel, builder: (context, _) { @@ -79,9 +79,10 @@ class _LoginTabletPageState extends State { BaseButton( isEnabled: _viewModel.isLoginEnabled, - onPressed: _viewModel.isLoginEnabled - ? () => HomeRouteData().go(context) - : null, + onPressed: + _viewModel.isLoginEnabled + ? () => HomeRouteData().go(context) + : null, ), ], ); diff --git a/lib/views/login/widgets/login_button.dart b/lib/views/login/widgets/login_button.dart index 9b7d34a..50a78be 100644 --- a/lib/views/login/widgets/login_button.dart +++ b/lib/views/login/widgets/login_button.dart @@ -14,6 +14,7 @@ class BaseButton extends StatelessWidget { final double? borderWidth; final double? borderRadius; final double? elevation; + final double? width; const BaseButton({ required this.isEnabled, @@ -25,13 +26,14 @@ class BaseButton extends StatelessWidget { this.borderWidth, this.borderRadius, this.elevation, + this.width, super.key, }); @override Widget build(BuildContext context) { return SizedBox( - width: double.infinity, + width: width ?? double.infinity, height: 55.0.h(), child: ElevatedButton( onPressed: isEnabled ? onPressed : null, From 83ca1690591e80a8fa8217b8c2841fccfa4c3f65 Mon Sep 17 00:00:00 2001 From: llsajjad Date: Sat, 9 Aug 2025 17:25:19 +0330 Subject: [PATCH 4/6] changed logoRepository to authRepository --- lib/core/config/dependencies.dart | 2 +- lib/view_models/auth_view_model.dart | 47 ++++++++++-- lib/view_models/login_view_model.dart | 42 ----------- lib/views/login/pages/login_desktop_page.dart | 70 +++++++++--------- lib/views/login/pages/login_mobile_page.dart | 71 +++++++++---------- lib/views/login/pages/login_tablet_page.dart | 70 +++++++++--------- 6 files changed, 138 insertions(+), 164 deletions(-) delete mode 100644 lib/view_models/login_view_model.dart diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart index 47949cb..b6fb3cc 100644 --- a/lib/core/config/dependencies.dart +++ b/lib/core/config/dependencies.dart @@ -32,7 +32,7 @@ List get repositories { List get viewModels { return [ - Provider( + ChangeNotifierProvider( create: (context) => AuthViewModel(authRepository: context.read()), ), ]; diff --git a/lib/view_models/auth_view_model.dart b/lib/view_models/auth_view_model.dart index 40492ac..0b2ba07 100644 --- a/lib/view_models/auth_view_model.dart +++ b/lib/view_models/auth_view_model.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import '../core/utils/result.dart'; import '../data/models/user_model.dart'; import '../data/repositories/auth_repository.dart'; @@ -8,8 +7,25 @@ class AuthViewModel with ChangeNotifier { final AuthRepository _authRepository; AuthViewModel({required AuthRepository authRepository}) - : _authRepository = authRepository; + : _authRepository = authRepository { + usernameController.addListener(_onTextChanged); + passwordController.addListener(_onTextChanged); + usernameFocus.addListener(_onTextChanged); + passwordFocus.addListener(_onTextChanged); + } + final TextEditingController usernameController = TextEditingController(); + final TextEditingController passwordController = TextEditingController(); + final FocusNode usernameFocus = FocusNode(); + final FocusNode passwordFocus = FocusNode(); + + bool _obscurePassword = true; + bool get obscurePassword => _obscurePassword; + bool get isLoginEnabled => + usernameController.text.isNotEmpty && + passwordController.text.isNotEmpty; + + // Auth state bool _isLoading = false; String? _errorMessage; UserModel? _loggedInUser; @@ -18,25 +34,44 @@ class AuthViewModel with ChangeNotifier { String? get errorMessage => _errorMessage; UserModel? get loggedInUser => _loggedInUser; - Future login(String email, String password) async { + void togglePasswordVisibility() { + _obscurePassword = !_obscurePassword; + notifyListeners(); + } + + void _onTextChanged() { + notifyListeners(); + } + + Future login() async { _isLoading = true; _errorMessage = null; notifyListeners(); - final result = await _authRepository.login(email, password); + final result = await _authRepository.login( + usernameController.text.trim(), + passwordController.text.trim(), + ); switch (result) { case Ok(): _loggedInUser = result.value; - // موفقیت‌آمیز break; case Error(): _errorMessage = result.error.toString(); - // خطا break; } _isLoading = false; notifyListeners(); } + + @override + void dispose() { + usernameController.dispose(); + passwordController.dispose(); + usernameFocus.dispose(); + passwordFocus.dispose(); + super.dispose(); + } } diff --git a/lib/view_models/login_view_model.dart b/lib/view_models/login_view_model.dart deleted file mode 100644 index 340539d..0000000 --- a/lib/view_models/login_view_model.dart +++ /dev/null @@ -1,42 +0,0 @@ -// lib/view_models/login_view_model.dart -import 'package:flutter/material.dart'; - -class LoginViewModel with ChangeNotifier { - final TextEditingController usernameController = TextEditingController(); - final TextEditingController passwordController = TextEditingController(); - - final FocusNode usernameFocus = FocusNode(); - final FocusNode passwordFocus = FocusNode(); - - bool _obscurePassword = true; - - LoginViewModel() { - usernameController.addListener(_onTextChanged); - passwordController.addListener(_onTextChanged); - usernameFocus.addListener(_onTextChanged); - passwordFocus.addListener(_onTextChanged); - } - - bool get obscurePassword => _obscurePassword; - bool get isLoginEnabled => - usernameController.text.isNotEmpty && - passwordController.text.isNotEmpty; - - void togglePasswordVisibility() { - _obscurePassword = !_obscurePassword; - notifyListeners(); - } - - void _onTextChanged() { - notifyListeners(); - } - - @override - void dispose() { - usernameController.dispose(); - passwordController.dispose(); - usernameFocus.dispose(); - passwordFocus.dispose(); - super.dispose(); - } -} diff --git a/lib/views/login/pages/login_desktop_page.dart b/lib/views/login/pages/login_desktop_page.dart index f9bf5ed..c706ab0 100644 --- a/lib/views/login/pages/login_desktop_page.dart +++ b/lib/views/login/pages/login_desktop_page.dart @@ -1,36 +1,17 @@ -// lib/views/login_desktop_screen.dart import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; import '../../../core/utils/size_config.dart'; -import '../../../view_models/login_view_model.dart'; +import '../../../view_models/auth_view_model.dart'; import '../widgets/widgets.dart'; -class LoginDesktopPage extends StatefulWidget { +class LoginDesktopPage extends StatelessWidget { const LoginDesktopPage({super.key}); - @override - State createState() => _LoginDesktopPageState(); -} - -class _LoginDesktopPageState extends State { - late final LoginViewModel _viewModel; - - @override - void initState() { - super.initState(); - _viewModel = LoginViewModel(); - } - - @override - void dispose() { - _viewModel.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { SizeConfig.init(context); @@ -41,9 +22,8 @@ class _LoginDesktopPageState extends State { padding: EdgeInsets.symmetric(horizontal: 24.0.w()), child: SizedBox( width: 526, - child: ListenableBuilder( - listenable: _viewModel, - builder: (context, _) { + child: Consumer( + builder: (context, viewModel, _) { return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -53,8 +33,8 @@ class _LoginDesktopPageState extends State { SizedBox(height: 32.0.h()), LoginTextField( - controller: _viewModel.usernameController, - focusNode: _viewModel.usernameFocus, + controller: viewModel.usernameController, + focusNode: viewModel.usernameFocus, label: AppStrings.usernameLabel, iconPath: AssetsManager.userIcon, fieldHeight: 60.0.h(), @@ -64,26 +44,40 @@ class _LoginDesktopPageState extends State { SizedBox(height: 16.0.h()), LoginTextField( - controller: _viewModel.passwordController, - focusNode: _viewModel.passwordFocus, + controller: viewModel.passwordController, + focusNode: viewModel.passwordFocus, label: AppStrings.passwordLabel, iconPath: AssetsManager.passwordIcon, prefixIconPadding: 4.0.w(), isPassword: true, - obscureText: _viewModel.obscurePassword, + obscureText: viewModel.obscurePassword, fieldHeight: 60.0.h(), - onToggleVisibility: _viewModel.togglePasswordVisibility, + onToggleVisibility: viewModel.togglePasswordVisibility, borderRedus: 4.0.w(), ), SizedBox(height: 32.0.h()), - BaseButton( - isEnabled: _viewModel.isLoginEnabled, - onPressed: - _viewModel.isLoginEnabled - ? () => HomeRouteData().go(context) - : null, - ), + if (viewModel.isLoading) + const CircularProgressIndicator() + else + BaseButton( + isEnabled: viewModel.isLoginEnabled, + onPressed: + viewModel.isLoginEnabled + ? () async { + await viewModel.login(); + if (viewModel.loggedInUser != null) { + HomeRouteData().go(context); + } else if (viewModel.errorMessage != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(viewModel.errorMessage!), + ), + ); + } + } + : null, + ), ], ); }, diff --git a/lib/views/login/pages/login_mobile_page.dart b/lib/views/login/pages/login_mobile_page.dart index 2f1ac24..9912c6d 100644 --- a/lib/views/login/pages/login_mobile_page.dart +++ b/lib/views/login/pages/login_mobile_page.dart @@ -1,36 +1,17 @@ -// lib/views/login_screen.dart import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; import '../../../core/utils/size_config.dart'; +import '../../../view_models/auth_view_model.dart'; import '../widgets/widgets.dart'; -import '../../../view_models/login_view_model.dart'; -class LoginMobilePage extends StatefulWidget { +class LoginMobilePage extends StatelessWidget { const LoginMobilePage({super.key}); - @override - State createState() => _LoginMobilePageState(); -} - -class _LoginMobilePageState extends State { - late final LoginViewModel _viewModel; - - @override - void initState() { - super.initState(); - _viewModel = LoginViewModel(); - } - - @override - void dispose() { - _viewModel.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { SizeConfig.init(context); @@ -40,9 +21,8 @@ class _LoginMobilePageState extends State { body: Center( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 24.0.w()), - child: ListenableBuilder( - listenable: _viewModel, - builder: (context, _) { + child: Consumer( + builder: (context, viewModel, _) { return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -52,32 +32,45 @@ class _LoginMobilePageState extends State { SizedBox(height: 32.0.h()), LoginTextField( - controller: _viewModel.usernameController, - focusNode: _viewModel.usernameFocus, + controller: viewModel.usernameController, + focusNode: viewModel.usernameFocus, label: AppStrings.usernameLabel, iconPath: AssetsManager.userIcon, ), SizedBox(height: 16.0.h()), LoginTextField( - controller: _viewModel.passwordController, - focusNode: _viewModel.passwordFocus, + controller: viewModel.passwordController, + focusNode: viewModel.passwordFocus, label: AppStrings.passwordLabel, iconPath: AssetsManager.passwordIcon, isPassword: true, - obscureText: _viewModel.obscurePassword, - onToggleVisibility: _viewModel.togglePasswordVisibility, + obscureText: viewModel.obscurePassword, + onToggleVisibility: viewModel.togglePasswordVisibility, ), SizedBox(height: 32.0.h()), - BaseButton( - isEnabled: _viewModel.isLoginEnabled, - onPressed: _viewModel.isLoginEnabled - ? () { - HomeRouteData().go(context); - } - : null, - ), + if (viewModel.isLoading) + const CircularProgressIndicator() + else + BaseButton( + isEnabled: viewModel.isLoginEnabled, + onPressed: + viewModel.isLoginEnabled + ? () async { + await viewModel.login(); + if (viewModel.loggedInUser != null) { + HomeRouteData().go(context); + } else if (viewModel.errorMessage != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(viewModel.errorMessage!), + ), + ); + } + } + : null, + ), ], ); }, diff --git a/lib/views/login/pages/login_tablet_page.dart b/lib/views/login/pages/login_tablet_page.dart index 64a36aa..d054420 100644 --- a/lib/views/login/pages/login_tablet_page.dart +++ b/lib/views/login/pages/login_tablet_page.dart @@ -1,36 +1,17 @@ -// lib/views/login_tablet_screen.dart import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:tm_app/core/routes/app_routes.dart'; import 'package:tm_app/core/theme/colors.dart'; import '../../../core/constants/assets.dart'; import '../../../core/constants/strings.dart'; import '../../../core/utils/size_config.dart'; -import '../../../view_models/login_view_model.dart'; +import '../../../view_models/auth_view_model.dart'; import '../widgets/widgets.dart'; -class LoginTabletPage extends StatefulWidget { +class LoginTabletPage extends StatelessWidget { const LoginTabletPage({super.key}); - @override - State createState() => _LoginTabletPageState(); -} - -class _LoginTabletPageState extends State { - late final LoginViewModel _viewModel; - - @override - void initState() { - super.initState(); - _viewModel = LoginViewModel(); - } - - @override - void dispose() { - _viewModel.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { SizeConfig.init(context); @@ -41,9 +22,8 @@ class _LoginTabletPageState extends State { padding: EdgeInsets.symmetric(horizontal: 24.0.w()), child: SizedBox( width: 526, - child: ListenableBuilder( - listenable: _viewModel, - builder: (context, _) { + child: Consumer( + builder: (context, viewModel, _) { return Column( mainAxisSize: MainAxisSize.min, children: [ @@ -53,8 +33,8 @@ class _LoginTabletPageState extends State { SizedBox(height: 32.0.h()), LoginTextField( - controller: _viewModel.usernameController, - focusNode: _viewModel.usernameFocus, + controller: viewModel.usernameController, + focusNode: viewModel.usernameFocus, label: AppStrings.usernameLabel, iconPath: AssetsManager.userIcon, fieldHeight: 60.0.h(), @@ -64,26 +44,40 @@ class _LoginTabletPageState extends State { SizedBox(height: 16.0.h()), LoginTextField( - controller: _viewModel.passwordController, - focusNode: _viewModel.passwordFocus, + controller: viewModel.passwordController, + focusNode: viewModel.passwordFocus, label: AppStrings.passwordLabel, iconPath: AssetsManager.passwordIcon, prefixIconPadding: 4.0.w(), isPassword: true, - obscureText: _viewModel.obscurePassword, + obscureText: viewModel.obscurePassword, fieldHeight: 60.0.h(), - onToggleVisibility: _viewModel.togglePasswordVisibility, + onToggleVisibility: viewModel.togglePasswordVisibility, borderRedus: 4.0.w(), ), SizedBox(height: 32.0.h()), - BaseButton( - isEnabled: _viewModel.isLoginEnabled, - onPressed: - _viewModel.isLoginEnabled - ? () => HomeRouteData().go(context) - : null, - ), + if (viewModel.isLoading) + const CircularProgressIndicator() + else + BaseButton( + isEnabled: viewModel.isLoginEnabled, + onPressed: + viewModel.isLoginEnabled + ? () async { + await viewModel.login(); + if (viewModel.loggedInUser != null) { + HomeRouteData().go(context); + } else if (viewModel.errorMessage != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(viewModel.errorMessage!), + ), + ); + } + } + : null, + ), ], ); }, From d0afc23be1cd40be0f3bd171400aff25e2e1b98c Mon Sep 17 00:00:00 2001 From: llsajjad Date: Sat, 9 Aug 2025 19:26:56 +0330 Subject: [PATCH 5/6] added logic for detail --- lib/core/config/dependencies.dart | 9 +- .../tender_detail_repository.dart | 14 + .../mock_data/tender_detail_mock.dart | 24 + .../tender_detail_response_model.dart | 34 + .../tender_detail_response_model.freezed.dart | 599 ++++++++++++++++++ .../tender_detail_response_model.g.dart | 53 ++ lib/data/services/tender_detail_service.dart | 34 + lib/view_models/tender_detail_view_model.dart | 39 ++ .../detail/pages/detail_desktop_page.dart | 77 ++- .../detail/pages/detail_mobile_page.dart | 69 +- lib/views/detail/pages/detail_screen.dart | 6 +- .../detail/pages/detail_tablet_page.dart | 74 ++- .../detail/widgets/tender_detail_header.dart | 20 +- .../widgets/tender_detail_info_section.dart | 16 +- .../widgets/tender_document_section.dart | 42 +- .../widgets/tender_location_section.dart | 13 +- pubspec.lock | 75 +-- pubspec.yaml | 3 +- 18 files changed, 1051 insertions(+), 150 deletions(-) create mode 100644 lib/data/repositories/tender_detail_repository.dart create mode 100644 lib/data/services/mock_data/tender_detail_mock.dart create mode 100644 lib/data/services/model/tender_detail_response/tender_detail_response_model.dart create mode 100644 lib/data/services/model/tender_detail_response/tender_detail_response_model.freezed.dart create mode 100644 lib/data/services/model/tender_detail_response/tender_detail_response_model.g.dart create mode 100644 lib/data/services/tender_detail_service.dart create mode 100644 lib/view_models/tender_detail_view_model.dart diff --git a/lib/core/config/dependencies.dart b/lib/core/config/dependencies.dart index b6fb3cc..dc5911a 100644 --- a/lib/core/config/dependencies.dart +++ b/lib/core/config/dependencies.dart @@ -1,6 +1,9 @@ import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; import 'package:tm_app/core/theme/theme_provider.dart'; +import 'package:tm_app/data/repositories/tender_detail_repository.dart'; +import 'package:tm_app/data/services/tender_detail_service.dart'; +import 'package:tm_app/view_models/tender_detail_view_model.dart'; import '../../data/repositories/auth_repository.dart'; import '../../data/services/auth_service.dart'; @@ -21,12 +24,13 @@ List get cores { List get apiClients { return [ Provider(create: (context) => AuthService(networkManager: context.read())), - ]; + Provider(create: (context) => TenderDetailService(networkManager: context.read())),]; } List get repositories { return [ Provider(create: (context) => AuthRepository(authService: context.read())), + Provider(create: (context) => TenderDetailRepository(tenderDetailService: context.read())), ]; } @@ -34,6 +38,9 @@ List get viewModels { return [ ChangeNotifierProvider( create: (context) => AuthViewModel(authRepository: context.read()), + ), + ChangeNotifierProvider( + create: (context) => TenderDetailViewModel(tenderDetailRepository: context.read()), ), ]; } diff --git a/lib/data/repositories/tender_detail_repository.dart b/lib/data/repositories/tender_detail_repository.dart new file mode 100644 index 0000000..752ff28 --- /dev/null +++ b/lib/data/repositories/tender_detail_repository.dart @@ -0,0 +1,14 @@ +import 'package:tm_app/core/utils/result.dart'; +import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; +import 'package:tm_app/data/services/tender_detail_service.dart'; + +class TenderDetailRepository { + TenderDetailRepository({required TenderDetailService tenderDetailService}) + : _tenderDetailService = tenderDetailService; + + final TenderDetailService _tenderDetailService; + + Future> getTenderDetail({required String id}) async { + return _tenderDetailService.getTenderDetail(id: id); + } +} diff --git a/lib/data/services/mock_data/tender_detail_mock.dart b/lib/data/services/mock_data/tender_detail_mock.dart new file mode 100644 index 0000000..0b8cd3f --- /dev/null +++ b/lib/data/services/mock_data/tender_detail_mock.dart @@ -0,0 +1,24 @@ +const tenderDetailMockData = ''' +{ + "data": { + "date": "2025-05-21", + "status": "Open", + "title": "Operation, support and further development of open e-platform Operation, support and further development of open e-platform", + "id": "JNDFKMDV-100JF", + "approvalDate": "2025-05-21", + "submissionDate": "2025-05-21", + "client": "Procurement Notice Procurement NoticeProcurement Notice", + "deliveryLocation": "Norrbotten County", + "referenceNumber": "KLF 2025/120", + "country": "UK", + "flagAsset": "assets/icons/SE.pngtest@example.compassword123", + "locationTitle": "Lorem ipsum is amet", + "locationDescription": "Luleå Municipality is procuring on behalf of Norrbotten’s e-board (all municipalities in Norrbotten). The procurement includes operation, support, and further development of the existing open e-platform. No additional information, amendments, or answers to questions will be provided after June 16.", + "documentName": "PDF, Document", + "documentUrl": "https://www.tenders.com", + "profileMatch": 0.75, + "incompleteResume": true, + "incompleteResumeReason": "No experience in e-platform development" + } +} +'''; diff --git a/lib/data/services/model/tender_detail_response/tender_detail_response_model.dart b/lib/data/services/model/tender_detail_response/tender_detail_response_model.dart new file mode 100644 index 0000000..4b89668 --- /dev/null +++ b/lib/data/services/model/tender_detail_response/tender_detail_response_model.dart @@ -0,0 +1,34 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'tender_detail_response_model.freezed.dart'; +part 'tender_detail_response_model.g.dart'; + +@freezed +class TenderDetailResponseModel with _$TenderDetailResponseModel { + @JsonSerializable(explicitToJson: true) + const factory TenderDetailResponseModel({ + required String? date, + required String? status, + required String? title, + required String? id, + required String? approvalDate, + required String? submissionDate, + required String? client, + required String? deliveryLocation, + required String? referenceNumber, + required String? country, + required String? flagAsset, + required String? locationTitle, + required String? locationDescription, + required String? documentName, + required String? documentUrl, + required double? profileMatch, + required bool? incompleteResume, + required String? incompleteResumeReason, + }) = _TenderDetailResponseModel; + + factory TenderDetailResponseModel.fromJson(Map json) => + _$TenderDetailResponseModelFromJson(json); +} diff --git a/lib/data/services/model/tender_detail_response/tender_detail_response_model.freezed.dart b/lib/data/services/model/tender_detail_response/tender_detail_response_model.freezed.dart new file mode 100644 index 0000000..58a1f29 --- /dev/null +++ b/lib/data/services/model/tender_detail_response/tender_detail_response_model.freezed.dart @@ -0,0 +1,599 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'tender_detail_response_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +TenderDetailResponseModel _$TenderDetailResponseModelFromJson( + Map json, +) { + return _TenderDetailResponseModel.fromJson(json); +} + +/// @nodoc +mixin _$TenderDetailResponseModel { + String? get date => throw _privateConstructorUsedError; + String? get status => throw _privateConstructorUsedError; + String? get title => throw _privateConstructorUsedError; + String? get id => throw _privateConstructorUsedError; + String? get approvalDate => throw _privateConstructorUsedError; + String? get submissionDate => throw _privateConstructorUsedError; + String? get client => throw _privateConstructorUsedError; + String? get deliveryLocation => throw _privateConstructorUsedError; + String? get referenceNumber => throw _privateConstructorUsedError; + String? get country => throw _privateConstructorUsedError; + String? get flagAsset => throw _privateConstructorUsedError; + String? get locationTitle => throw _privateConstructorUsedError; + String? get locationDescription => throw _privateConstructorUsedError; + String? get documentName => throw _privateConstructorUsedError; + String? get documentUrl => throw _privateConstructorUsedError; + double? get profileMatch => throw _privateConstructorUsedError; + bool? get incompleteResume => throw _privateConstructorUsedError; + String? get incompleteResumeReason => throw _privateConstructorUsedError; + + /// Serializes this TenderDetailResponseModel to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of TenderDetailResponseModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $TenderDetailResponseModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TenderDetailResponseModelCopyWith<$Res> { + factory $TenderDetailResponseModelCopyWith( + TenderDetailResponseModel value, + $Res Function(TenderDetailResponseModel) then, + ) = _$TenderDetailResponseModelCopyWithImpl<$Res, TenderDetailResponseModel>; + @useResult + $Res call({ + String? date, + String? status, + String? title, + String? id, + String? approvalDate, + String? submissionDate, + String? client, + String? deliveryLocation, + String? referenceNumber, + String? country, + String? flagAsset, + String? locationTitle, + String? locationDescription, + String? documentName, + String? documentUrl, + double? profileMatch, + bool? incompleteResume, + String? incompleteResumeReason, + }); +} + +/// @nodoc +class _$TenderDetailResponseModelCopyWithImpl< + $Res, + $Val extends TenderDetailResponseModel +> + implements $TenderDetailResponseModelCopyWith<$Res> { + _$TenderDetailResponseModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of TenderDetailResponseModel + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? date = freezed, + Object? status = freezed, + Object? title = freezed, + Object? id = freezed, + Object? approvalDate = freezed, + Object? submissionDate = freezed, + Object? client = freezed, + Object? deliveryLocation = freezed, + Object? referenceNumber = freezed, + Object? country = freezed, + Object? flagAsset = freezed, + Object? locationTitle = freezed, + Object? locationDescription = freezed, + Object? documentName = freezed, + Object? documentUrl = freezed, + Object? profileMatch = freezed, + Object? incompleteResume = freezed, + Object? incompleteResumeReason = freezed, + }) { + return _then( + _value.copyWith( + date: + freezed == date + ? _value.date + : date // ignore: cast_nullable_to_non_nullable + as String?, + status: + freezed == status + ? _value.status + : status // ignore: cast_nullable_to_non_nullable + as String?, + title: + freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + id: + freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + approvalDate: + freezed == approvalDate + ? _value.approvalDate + : approvalDate // ignore: cast_nullable_to_non_nullable + as String?, + submissionDate: + freezed == submissionDate + ? _value.submissionDate + : submissionDate // ignore: cast_nullable_to_non_nullable + as String?, + client: + freezed == client + ? _value.client + : client // ignore: cast_nullable_to_non_nullable + as String?, + deliveryLocation: + freezed == deliveryLocation + ? _value.deliveryLocation + : deliveryLocation // ignore: cast_nullable_to_non_nullable + as String?, + referenceNumber: + freezed == referenceNumber + ? _value.referenceNumber + : referenceNumber // ignore: cast_nullable_to_non_nullable + as String?, + country: + freezed == country + ? _value.country + : country // ignore: cast_nullable_to_non_nullable + as String?, + flagAsset: + freezed == flagAsset + ? _value.flagAsset + : flagAsset // ignore: cast_nullable_to_non_nullable + as String?, + locationTitle: + freezed == locationTitle + ? _value.locationTitle + : locationTitle // ignore: cast_nullable_to_non_nullable + as String?, + locationDescription: + freezed == locationDescription + ? _value.locationDescription + : locationDescription // ignore: cast_nullable_to_non_nullable + as String?, + documentName: + freezed == documentName + ? _value.documentName + : documentName // ignore: cast_nullable_to_non_nullable + as String?, + documentUrl: + freezed == documentUrl + ? _value.documentUrl + : documentUrl // ignore: cast_nullable_to_non_nullable + as String?, + profileMatch: + freezed == profileMatch + ? _value.profileMatch + : profileMatch // ignore: cast_nullable_to_non_nullable + as double?, + incompleteResume: + freezed == incompleteResume + ? _value.incompleteResume + : incompleteResume // ignore: cast_nullable_to_non_nullable + as bool?, + incompleteResumeReason: + freezed == incompleteResumeReason + ? _value.incompleteResumeReason + : incompleteResumeReason // ignore: cast_nullable_to_non_nullable + as String?, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$TenderDetailResponseModelImplCopyWith<$Res> + implements $TenderDetailResponseModelCopyWith<$Res> { + factory _$$TenderDetailResponseModelImplCopyWith( + _$TenderDetailResponseModelImpl value, + $Res Function(_$TenderDetailResponseModelImpl) then, + ) = __$$TenderDetailResponseModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({ + String? date, + String? status, + String? title, + String? id, + String? approvalDate, + String? submissionDate, + String? client, + String? deliveryLocation, + String? referenceNumber, + String? country, + String? flagAsset, + String? locationTitle, + String? locationDescription, + String? documentName, + String? documentUrl, + double? profileMatch, + bool? incompleteResume, + String? incompleteResumeReason, + }); +} + +/// @nodoc +class __$$TenderDetailResponseModelImplCopyWithImpl<$Res> + extends + _$TenderDetailResponseModelCopyWithImpl< + $Res, + _$TenderDetailResponseModelImpl + > + implements _$$TenderDetailResponseModelImplCopyWith<$Res> { + __$$TenderDetailResponseModelImplCopyWithImpl( + _$TenderDetailResponseModelImpl _value, + $Res Function(_$TenderDetailResponseModelImpl) _then, + ) : super(_value, _then); + + /// Create a copy of TenderDetailResponseModel + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? date = freezed, + Object? status = freezed, + Object? title = freezed, + Object? id = freezed, + Object? approvalDate = freezed, + Object? submissionDate = freezed, + Object? client = freezed, + Object? deliveryLocation = freezed, + Object? referenceNumber = freezed, + Object? country = freezed, + Object? flagAsset = freezed, + Object? locationTitle = freezed, + Object? locationDescription = freezed, + Object? documentName = freezed, + Object? documentUrl = freezed, + Object? profileMatch = freezed, + Object? incompleteResume = freezed, + Object? incompleteResumeReason = freezed, + }) { + return _then( + _$TenderDetailResponseModelImpl( + date: + freezed == date + ? _value.date + : date // ignore: cast_nullable_to_non_nullable + as String?, + status: + freezed == status + ? _value.status + : status // ignore: cast_nullable_to_non_nullable + as String?, + title: + freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + id: + freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + approvalDate: + freezed == approvalDate + ? _value.approvalDate + : approvalDate // ignore: cast_nullable_to_non_nullable + as String?, + submissionDate: + freezed == submissionDate + ? _value.submissionDate + : submissionDate // ignore: cast_nullable_to_non_nullable + as String?, + client: + freezed == client + ? _value.client + : client // ignore: cast_nullable_to_non_nullable + as String?, + deliveryLocation: + freezed == deliveryLocation + ? _value.deliveryLocation + : deliveryLocation // ignore: cast_nullable_to_non_nullable + as String?, + referenceNumber: + freezed == referenceNumber + ? _value.referenceNumber + : referenceNumber // ignore: cast_nullable_to_non_nullable + as String?, + country: + freezed == country + ? _value.country + : country // ignore: cast_nullable_to_non_nullable + as String?, + flagAsset: + freezed == flagAsset + ? _value.flagAsset + : flagAsset // ignore: cast_nullable_to_non_nullable + as String?, + locationTitle: + freezed == locationTitle + ? _value.locationTitle + : locationTitle // ignore: cast_nullable_to_non_nullable + as String?, + locationDescription: + freezed == locationDescription + ? _value.locationDescription + : locationDescription // ignore: cast_nullable_to_non_nullable + as String?, + documentName: + freezed == documentName + ? _value.documentName + : documentName // ignore: cast_nullable_to_non_nullable + as String?, + documentUrl: + freezed == documentUrl + ? _value.documentUrl + : documentUrl // ignore: cast_nullable_to_non_nullable + as String?, + profileMatch: + freezed == profileMatch + ? _value.profileMatch + : profileMatch // ignore: cast_nullable_to_non_nullable + as double?, + incompleteResume: + freezed == incompleteResume + ? _value.incompleteResume + : incompleteResume // ignore: cast_nullable_to_non_nullable + as bool?, + incompleteResumeReason: + freezed == incompleteResumeReason + ? _value.incompleteResumeReason + : incompleteResumeReason // ignore: cast_nullable_to_non_nullable + as String?, + ), + ); + } +} + +/// @nodoc + +@JsonSerializable(explicitToJson: true) +class _$TenderDetailResponseModelImpl implements _TenderDetailResponseModel { + const _$TenderDetailResponseModelImpl({ + required this.date, + required this.status, + required this.title, + required this.id, + required this.approvalDate, + required this.submissionDate, + required this.client, + required this.deliveryLocation, + required this.referenceNumber, + required this.country, + required this.flagAsset, + required this.locationTitle, + required this.locationDescription, + required this.documentName, + required this.documentUrl, + required this.profileMatch, + required this.incompleteResume, + required this.incompleteResumeReason, + }); + + factory _$TenderDetailResponseModelImpl.fromJson(Map json) => + _$$TenderDetailResponseModelImplFromJson(json); + + @override + final String? date; + @override + final String? status; + @override + final String? title; + @override + final String? id; + @override + final String? approvalDate; + @override + final String? submissionDate; + @override + final String? client; + @override + final String? deliveryLocation; + @override + final String? referenceNumber; + @override + final String? country; + @override + final String? flagAsset; + @override + final String? locationTitle; + @override + final String? locationDescription; + @override + final String? documentName; + @override + final String? documentUrl; + @override + final double? profileMatch; + @override + final bool? incompleteResume; + @override + final String? incompleteResumeReason; + + @override + String toString() { + return 'TenderDetailResponseModel(date: $date, status: $status, title: $title, id: $id, approvalDate: $approvalDate, submissionDate: $submissionDate, client: $client, deliveryLocation: $deliveryLocation, referenceNumber: $referenceNumber, country: $country, flagAsset: $flagAsset, locationTitle: $locationTitle, locationDescription: $locationDescription, documentName: $documentName, documentUrl: $documentUrl, profileMatch: $profileMatch, incompleteResume: $incompleteResume, incompleteResumeReason: $incompleteResumeReason)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TenderDetailResponseModelImpl && + (identical(other.date, date) || other.date == date) && + (identical(other.status, status) || other.status == status) && + (identical(other.title, title) || other.title == title) && + (identical(other.id, id) || other.id == id) && + (identical(other.approvalDate, approvalDate) || + other.approvalDate == approvalDate) && + (identical(other.submissionDate, submissionDate) || + other.submissionDate == submissionDate) && + (identical(other.client, client) || other.client == client) && + (identical(other.deliveryLocation, deliveryLocation) || + other.deliveryLocation == deliveryLocation) && + (identical(other.referenceNumber, referenceNumber) || + other.referenceNumber == referenceNumber) && + (identical(other.country, country) || other.country == country) && + (identical(other.flagAsset, flagAsset) || + other.flagAsset == flagAsset) && + (identical(other.locationTitle, locationTitle) || + other.locationTitle == locationTitle) && + (identical(other.locationDescription, locationDescription) || + other.locationDescription == locationDescription) && + (identical(other.documentName, documentName) || + other.documentName == documentName) && + (identical(other.documentUrl, documentUrl) || + other.documentUrl == documentUrl) && + (identical(other.profileMatch, profileMatch) || + other.profileMatch == profileMatch) && + (identical(other.incompleteResume, incompleteResume) || + other.incompleteResume == incompleteResume) && + (identical(other.incompleteResumeReason, incompleteResumeReason) || + other.incompleteResumeReason == incompleteResumeReason)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + date, + status, + title, + id, + approvalDate, + submissionDate, + client, + deliveryLocation, + referenceNumber, + country, + flagAsset, + locationTitle, + locationDescription, + documentName, + documentUrl, + profileMatch, + incompleteResume, + incompleteResumeReason, + ); + + /// Create a copy of TenderDetailResponseModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$TenderDetailResponseModelImplCopyWith<_$TenderDetailResponseModelImpl> + get copyWith => __$$TenderDetailResponseModelImplCopyWithImpl< + _$TenderDetailResponseModelImpl + >(this, _$identity); + + @override + Map toJson() { + return _$$TenderDetailResponseModelImplToJson(this); + } +} + +abstract class _TenderDetailResponseModel implements TenderDetailResponseModel { + const factory _TenderDetailResponseModel({ + required final String? date, + required final String? status, + required final String? title, + required final String? id, + required final String? approvalDate, + required final String? submissionDate, + required final String? client, + required final String? deliveryLocation, + required final String? referenceNumber, + required final String? country, + required final String? flagAsset, + required final String? locationTitle, + required final String? locationDescription, + required final String? documentName, + required final String? documentUrl, + required final double? profileMatch, + required final bool? incompleteResume, + required final String? incompleteResumeReason, + }) = _$TenderDetailResponseModelImpl; + + factory _TenderDetailResponseModel.fromJson(Map json) = + _$TenderDetailResponseModelImpl.fromJson; + + @override + String? get date; + @override + String? get status; + @override + String? get title; + @override + String? get id; + @override + String? get approvalDate; + @override + String? get submissionDate; + @override + String? get client; + @override + String? get deliveryLocation; + @override + String? get referenceNumber; + @override + String? get country; + @override + String? get flagAsset; + @override + String? get locationTitle; + @override + String? get locationDescription; + @override + String? get documentName; + @override + String? get documentUrl; + @override + double? get profileMatch; + @override + bool? get incompleteResume; + @override + String? get incompleteResumeReason; + + /// Create a copy of TenderDetailResponseModel + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$TenderDetailResponseModelImplCopyWith<_$TenderDetailResponseModelImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/data/services/model/tender_detail_response/tender_detail_response_model.g.dart b/lib/data/services/model/tender_detail_response/tender_detail_response_model.g.dart new file mode 100644 index 0000000..55a4ed6 --- /dev/null +++ b/lib/data/services/model/tender_detail_response/tender_detail_response_model.g.dart @@ -0,0 +1,53 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tender_detail_response_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$TenderDetailResponseModelImpl _$$TenderDetailResponseModelImplFromJson( + Map json, +) => _$TenderDetailResponseModelImpl( + date: json['date'] as String?, + status: json['status'] as String?, + title: json['title'] as String?, + id: json['id'] as String?, + approvalDate: json['approvalDate'] as String?, + submissionDate: json['submissionDate'] as String?, + client: json['client'] as String?, + deliveryLocation: json['deliveryLocation'] as String?, + referenceNumber: json['referenceNumber'] as String?, + country: json['country'] as String?, + flagAsset: json['flagAsset'] as String?, + locationTitle: json['locationTitle'] as String?, + locationDescription: json['locationDescription'] as String?, + documentName: json['documentName'] as String?, + documentUrl: json['documentUrl'] as String?, + profileMatch: (json['profileMatch'] as num?)?.toDouble(), + incompleteResume: json['incompleteResume'] as bool?, + incompleteResumeReason: json['incompleteResumeReason'] as String?, +); + +Map _$$TenderDetailResponseModelImplToJson( + _$TenderDetailResponseModelImpl instance, +) => { + 'date': instance.date, + 'status': instance.status, + 'title': instance.title, + 'id': instance.id, + 'approvalDate': instance.approvalDate, + 'submissionDate': instance.submissionDate, + 'client': instance.client, + 'deliveryLocation': instance.deliveryLocation, + 'referenceNumber': instance.referenceNumber, + 'country': instance.country, + 'flagAsset': instance.flagAsset, + 'locationTitle': instance.locationTitle, + 'locationDescription': instance.locationDescription, + 'documentName': instance.documentName, + 'documentUrl': instance.documentUrl, + 'profileMatch': instance.profileMatch, + 'incompleteResume': instance.incompleteResume, + 'incompleteResumeReason': instance.incompleteResumeReason, +}; diff --git a/lib/data/services/tender_detail_service.dart b/lib/data/services/tender_detail_service.dart new file mode 100644 index 0000000..45d9716 --- /dev/null +++ b/lib/data/services/tender_detail_service.dart @@ -0,0 +1,34 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:tm_app/core/utils/logger.dart'; +import 'package:tm_app/core/utils/result.dart'; +import 'package:tm_app/data/services/mock_data/tender_detail_mock.dart'; +import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; +import 'package:tm_app/data/services/network_manager.dart'; + +class TenderDetailService { + TenderDetailService({required NetworkManager networkManager}) + : _networkManager = networkManager; + + final NetworkManager _networkManager; + + Future> getTenderDetail({required String id}) async { + try { + await Future.delayed(Duration(seconds: 2)); // شبیه سازی لود + appLogger.info('get tender detail success'); + + return Result.ok( + TenderDetailResponseModel.fromJson( + jsonDecode(tenderDetailMockData)['data'], + ), + ); + } on DioException catch (e) { + appLogger.error('get tender detail failed: $e'); + return Result.error(e); + } on Exception catch (e, stackTrace) { + appLogger.error('get tender detail failed: $e', stackTrace: stackTrace); + return Result.error(Exception(e)); + } + } +} diff --git a/lib/view_models/tender_detail_view_model.dart b/lib/view_models/tender_detail_view_model.dart new file mode 100644 index 0000000..823b7dc --- /dev/null +++ b/lib/view_models/tender_detail_view_model.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:tm_app/core/utils/result.dart'; +import 'package:tm_app/data/repositories/tender_detail_repository.dart'; +import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; + +class TenderDetailViewModel with ChangeNotifier { + final TenderDetailRepository _tenderDetailRepository; + + TenderDetailViewModel({required TenderDetailRepository tenderDetailRepository}) + : _tenderDetailRepository = tenderDetailRepository; + + bool _isLoading = false; + String? _errorMessage; + TenderDetailResponseModel? _tenderDetail; + + bool get isLoading => _isLoading; + String? get errorMessage => _errorMessage; + TenderDetailResponseModel? get tenderDetail => _tenderDetail; + + Future getTenderDetail({required String id}) async { + _isLoading = true; + _errorMessage = null; + notifyListeners(); + + final result = await _tenderDetailRepository.getTenderDetail(id: id); + + switch (result) { + case Ok(): + _tenderDetail = result.value; + break; + case Error(): + _errorMessage = result.error.toString(); + break; + } + + _isLoading = false; + notifyListeners(); + } +} diff --git a/lib/views/detail/pages/detail_desktop_page.dart b/lib/views/detail/pages/detail_desktop_page.dart index 4829a59..f23f25b 100644 --- a/lib/views/detail/pages/detail_desktop_page.dart +++ b/lib/views/detail/pages/detail_desktop_page.dart @@ -1,42 +1,75 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.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'; +import 'package:tm_app/view_models/tender_detail_view_model.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; -import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; -class TenderDetailDesktopPage extends StatelessWidget { - const TenderDetailDesktopPage({super.key}); +class TenderDetailDesktopPage extends StatefulWidget { + final String tenderId; + const TenderDetailDesktopPage({required this.tenderId, super.key}); + + @override + State createState() => + _TenderDetailDesktopPageState(); +} + +class _TenderDetailDesktopPageState extends State { + @override + void initState() { + super.initState(); + Future.microtask(() { + context.read().getTenderDetail( + id: widget.tenderId, + ); + }); + } @override Widget build(BuildContext context) { - SizeConfig.init(context); - return Scaffold( backgroundColor: AppColors.backgroundColor, appBar: _buildAppBar(context), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Center( - child: SizedBox( - width: 740, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TenderDetailHeader(isScreenBig: true), - SizedBox(height: 24.0.h()), - const TenderDetailCard(), - SizedBox(height: 24.0.h()), - TenderDetailActions(isScreenBig: true), - ], + body: Consumer( + builder: (context, tenderViewModel, child) { + if (tenderViewModel.isLoading) { + return const Center( + child: CircularProgressIndicator(color: AppColors.secondary50), + ); + } + + if (tenderViewModel.errorMessage != null) { + return Center(child: Text(tenderViewModel.errorMessage!)); + } + + final detail = tenderViewModel.tenderDetail; + if (detail == null) { + return const Center(child: Text('No tender details available')); + } + + return SingleChildScrollView( + child: Center( + child: SizedBox( + width: 740, + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TenderDetailHeader(isScreenBig: true, detail: detail), + SizedBox(height: 32.0.h()), + TenderDetailActions(isScreenBig: true), + ], + ), + ), ), ), - ), - ), + ); + }, ), ); } diff --git a/lib/views/detail/pages/detail_mobile_page.dart b/lib/views/detail/pages/detail_mobile_page.dart index 9a71896..03a2a65 100644 --- a/lib/views/detail/pages/detail_mobile_page.dart +++ b/lib/views/detail/pages/detail_mobile_page.dart @@ -1,35 +1,70 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.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'; +import 'package:tm_app/view_models/tender_detail_view_model.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; -import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; -class TenderDetailMobilePage extends StatelessWidget { - const TenderDetailMobilePage({super.key}); +class TenderDetailMobilePage extends StatefulWidget { + final String tenderId; + const TenderDetailMobilePage({super.key, required this.tenderId}); + + @override + State createState() => _TenderDetailMobilePageState(); + +} + + +class _TenderDetailMobilePageState extends State { + + @override +void initState() { + super.initState(); + Future.microtask(() { + context.read().getTenderDetail(id: widget.tenderId); + }); +} @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.backgroundColor, appBar: _buildAppBar(context), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TenderDetailHeader(isScreenBig: false), - SizedBox(height: 24.0.h()), - const TenderDetailCard(), - SizedBox(height: 24.0.h()), - TenderDetailActions(isScreenBig: false,), - ], - ), - ), + body: Consumer( + builder: (context, tenderViewModel, child) { + if (tenderViewModel.isLoading) { + return const Center( + child: CircularProgressIndicator(color: AppColors.secondary50), + ); + } + + if (tenderViewModel.errorMessage != null) { + return Center(child: Text(tenderViewModel.errorMessage!)); + } + + final detail = tenderViewModel.tenderDetail; + if (detail == null) { + return const Center(child: Text("No tender details available")); + } + + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TenderDetailHeader(isScreenBig: false, detail: detail), + SizedBox(height: 24.0.h()), + TenderDetailActions(isScreenBig: false), + ], + ), + ), + ); + }, ), ); } diff --git a/lib/views/detail/pages/detail_screen.dart b/lib/views/detail/pages/detail_screen.dart index 92e7db6..93464a1 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(), - tablet: TenderDetailTabletPage(), - desktop: TenderDetailDesktopPage(), + mobile: TenderDetailMobilePage(tenderId: 'JNDFKMDV-100JF',), + tablet: TenderDetailTabletPage(tenderId: 'JNDFKMDV-100JF',), + desktop: TenderDetailDesktopPage(tenderId: 'JNDFKMDV-100JF',), ); } } diff --git a/lib/views/detail/pages/detail_tablet_page.dart b/lib/views/detail/pages/detail_tablet_page.dart index b567541..067ece9 100644 --- a/lib/views/detail/pages/detail_tablet_page.dart +++ b/lib/views/detail/pages/detail_tablet_page.dart @@ -1,40 +1,74 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.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'; +import 'package:tm_app/view_models/tender_detail_view_model.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_action.dart'; -import 'package:tm_app/views/detail/widgets/tender_detail_card.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_header.dart'; -class TenderDetailTabletPage extends StatelessWidget { - const TenderDetailTabletPage({super.key}); +class TenderDetailTabletPage extends StatefulWidget { + final String tenderId; + const TenderDetailTabletPage({required this.tenderId, super.key}); + + @override + State createState() => _TenderDetailTabletPageState(); +} + +class _TenderDetailTabletPageState extends State { + @override + void initState() { + super.initState(); + Future.microtask(() { + context.read().getTenderDetail( + id: widget.tenderId, + ); + }); + } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.backgroundColor, appBar: _buildAppBar(context), - body: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Center( - child: SizedBox( - width: 768, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TenderDetailHeader(isScreenBig: true), - SizedBox(height: 24.0.h()), - const TenderDetailCard(), - SizedBox(height: 24.0.h()), - TenderDetailActions(isScreenBig: true,), - ], + body: Consumer( + builder: (context, tenderViewModel, child) { + if (tenderViewModel.isLoading) { + return const Center( + child: CircularProgressIndicator(color: AppColors.secondary50), + ); + } + + if (tenderViewModel.errorMessage != null) { + return Center(child: Text(tenderViewModel.errorMessage!)); + } + + final detail = tenderViewModel.tenderDetail; + if (detail == null) { + return const Center(child: Text('No tender details available')); + } + return SingleChildScrollView( + child: Center( + child: SizedBox( + width: 768, + + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TenderDetailHeader(isScreenBig: true, detail: detail), + SizedBox(height: 28.0.h()), + TenderDetailActions(isScreenBig: true), + ], + ), + ), ), ), - ), - ), + ); + }, ), ); } diff --git a/lib/views/detail/widgets/tender_detail_header.dart b/lib/views/detail/widgets/tender_detail_header.dart index a1a71aa..07accb1 100644 --- a/lib/views/detail/widgets/tender_detail_header.dart +++ b/lib/views/detail/widgets/tender_detail_header.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.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'; +import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; import 'package:tm_app/views/detail/widgets/status_tag.dart'; import 'package:tm_app/views/detail/widgets/tender_detail_info_section.dart'; import 'package:tm_app/views/detail/widgets/tender_document_section.dart'; @@ -9,7 +9,9 @@ import 'package:tm_app/views/detail/widgets/tender_location_section.dart'; class TenderDetailHeader extends StatelessWidget { final bool isScreenBig; - const TenderDetailHeader({required this.isScreenBig, super.key}); + final TenderDetailResponseModel detail; + + const TenderDetailHeader({required this.isScreenBig, required this.detail, super.key}); @override Widget build(BuildContext context) { @@ -29,10 +31,10 @@ class TenderDetailHeader extends StatelessWidget { SizedBox(height: 10.0.h()), const Divider(), SizedBox(height: 5.0.h()), - TenderDetailInfoSection(isScreenBig: isScreenBig), - TenderLocationSection(), + TenderDetailInfoSection(isScreenBig: isScreenBig, detail: detail), + TenderLocationSection(detail: detail), SizedBox(height: 16.0.h()), - TenderDocumentSection(), + TenderDocumentSection(detail: detail), ], ), ); @@ -42,15 +44,15 @@ class TenderDetailHeader extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - AppStrings.tenderDateExample, + detail.date ?? '', style: TextStyle( color: AppColors.grey60, fontWeight: FontWeight.w400, fontSize: 12.0.sp(), ), ), - const StatusTag( - text: AppStrings.tenderStatusOpen, + StatusTag( + text: detail.status ?? '', textColor: AppColors.cyanTeal, backgroundColor: AppColors.cyanAqua, ), @@ -58,7 +60,7 @@ class TenderDetailHeader extends StatelessWidget { ); Widget _buildTitle() => Text( - AppStrings.tenderTitleExample, + detail.title ?? '', style: TextStyle( color: AppColors.grey80, fontWeight: FontWeight.w600, diff --git a/lib/views/detail/widgets/tender_detail_info_section.dart b/lib/views/detail/widgets/tender_detail_info_section.dart index 440829d..e504b0f 100644 --- a/lib/views/detail/widgets/tender_detail_info_section.dart +++ b/lib/views/detail/widgets/tender_detail_info_section.dart @@ -1,12 +1,14 @@ import 'package:flutter/material.dart'; import 'package:tm_app/core/constants/strings.dart'; +import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; import 'package:tm_app/views/detail/widgets/deadline_item.dart'; import 'package:tm_app/views/detail/widgets/info_item.dart'; class TenderDetailInfoSection extends StatelessWidget { final bool isScreenBig; + final TenderDetailResponseModel detail; - const TenderDetailInfoSection({required this.isScreenBig, super.key}); + const TenderDetailInfoSection({required this.isScreenBig, required this.detail, super.key}); @override Widget build(BuildContext context) { @@ -15,27 +17,27 @@ class TenderDetailInfoSection extends StatelessWidget { children: [ InfoItem( title: AppStrings.tenderIdLabel, - value: AppStrings.tenderIdExample, + value: detail.id ?? '', ), DeadlineItem( title: AppStrings.tenderDeadlineLabel, approvalText: AppStrings.tenderApprovalText, - approvalDate: AppStrings.tenderApprovalDateExample, + approvalDate: detail.approvalDate ?? '', submissionText: AppStrings.tenderSubmissionText, - submissionDate: AppStrings.tenderSubmissionDateExample, + submissionDate: detail.submissionDate ?? '', isScreenBig: isScreenBig, ), InfoItem( title: AppStrings.tenderClientLabel, - value: AppStrings.tenderClientExample, + value: detail.client ?? '', ), InfoItem( title: AppStrings.tenderDeliveryLocationsLabel, - value: AppStrings.tenderDeliveryLocationExample, + value: detail.deliveryLocation ?? '', ), InfoItem( title: AppStrings.tenderReferenceNumberLabel, - value: AppStrings.tenderReferenceNumberExample, + value: detail.referenceNumber ?? '', ), ], ); diff --git a/lib/views/detail/widgets/tender_document_section.dart b/lib/views/detail/widgets/tender_document_section.dart index c716f2d..0a41f85 100644 --- a/lib/views/detail/widgets/tender_document_section.dart +++ b/lib/views/detail/widgets/tender_document_section.dart @@ -1,30 +1,38 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_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'; - +import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; class TenderDocumentSection extends StatelessWidget { - const TenderDocumentSection({super.key}); + final TenderDetailResponseModel detail; + + const TenderDocumentSection({super.key, required this.detail}); @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SvgPicture.asset(AssetsManager.export), - SizedBox(height: 6.0.h()), - Text( - AppStrings.tenderPdfDocument, - style: TextStyle( - fontWeight: FontWeight.w400, - fontSize: 16.0.sp(), - color: AppColors.textBlue, - decoration: TextDecoration.underline, + return InkWell( + onTap: () { + if (detail.documentUrl != null) { + //launchUrl(Uri.parse(detail.documentUrl!)); + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset(AssetsManager.export), + SizedBox(height: 6.0.h()), + Text( + detail.documentName ?? '', + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: 16.0.sp(), + color: AppColors.textBlue, + decoration: TextDecoration.underline, + ), ), - ), - ], + ], + ), ); } } diff --git a/lib/views/detail/widgets/tender_location_section.dart b/lib/views/detail/widgets/tender_location_section.dart index d652767..418118f 100644 --- a/lib/views/detail/widgets/tender_location_section.dart +++ b/lib/views/detail/widgets/tender_location_section.dart @@ -4,9 +4,12 @@ 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'; +import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; class TenderLocationSection extends StatelessWidget { - const TenderLocationSection({super.key}); + final TenderDetailResponseModel detail; + + const TenderLocationSection({super.key, required this.detail}); @override Widget build(BuildContext context) { @@ -18,7 +21,7 @@ class TenderLocationSection extends StatelessWidget { SizedBox( height: 45.0.h(), child: Text( - AppStrings.tenderReferenceNumberLabel, + detail.locationTitle ?? '', style: TextStyle( color: AppColors.grey80, fontWeight: FontWeight.w600, @@ -35,7 +38,7 @@ class TenderLocationSection extends StatelessWidget { ), SizedBox(width: 4.0.w()), Text( - AppStrings.tenderLocationCountry, + detail.country ?? '', style: TextStyle( color: AppColors.grey70, fontSize: 14.0.sp(), @@ -44,7 +47,7 @@ class TenderLocationSection extends StatelessWidget { ), SizedBox(width: 4.0.w()), Image.asset( - AssetsManager.seFlag, + detail.flagAsset ?? AssetsManager.seFlag, height: 20.0.h(), width: 20.0.w(), ), @@ -57,7 +60,7 @@ class TenderLocationSection extends StatelessWidget { ), SizedBox(height: 8.0.h()), Text( - AppStrings.locationDescription, + detail.locationDescription ?? '', style: TextStyle(fontWeight: FontWeight.w400, fontSize: 14.0.sp()), ), SizedBox(height: 16.0.h()), diff --git a/pubspec.lock b/pubspec.lock index f20f77f..0ce118b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.12.0" boolean_selector: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: built_value - sha256: "0b1b12a0a549605e5f04476031cd0bc91ead1d7c8e830773a18ee54179b3cb62" + sha256: ba95c961bafcd8686d1cf63be864eb59447e795e124d98d6a27d91fcd13602fb url: "https://pub.dev" source: hosted - version: "8.11.0" + version: "8.11.1" characters: dependency: transitive description: @@ -177,30 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" - device_frame: - dependency: transitive - description: - name: device_frame - sha256: "7b2ebb2a09d6cc0f086b51bd1412d7be83e0170056a7290349169be41164c86a" - url: "https://pub.dev" - source: hosted - version: "1.4.0" - device_preview: - dependency: "direct main" - description: - name: device_preview - sha256: "88aa1cc73ee9a8ec771b309dcbc4000cc66b5d8456b825980997640ab1195bf5" - url: "https://pub.dev" - source: hosted - version: "1.3.1" dio: dependency: "direct main" description: name: dio - sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 url: "https://pub.dev" source: hosted - version: "5.8.0+1" + version: "5.9.0" dio_web_adapter: dependency: transitive description: @@ -213,10 +197,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.2" ffi: dependency: transitive description: @@ -254,11 +238,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" - flutter_localizations: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" flutter_svg: dependency: "direct main" description: @@ -337,10 +316,10 @@ packages: dependency: transitive description: name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" http_multi_server: dependency: transitive description: @@ -357,14 +336,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" - intl: - dependency: transitive - description: - name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.dev" - source: hosted - version: "0.20.2" io: dependency: transitive description: @@ -382,21 +353,29 @@ packages: source: hosted version: "0.7.2" json_annotation: - dependency: transitive + dependency: "direct main" description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c + url: "https://pub.dev" + source: hosted + version: "6.9.5" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: @@ -593,10 +572,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.11" shared_preferences_foundation: dependency: transitive description: @@ -782,10 +761,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.3.1" watcher: dependency: transitive description: @@ -843,5 +822,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.32.0" + dart: ">=3.7.2 <4.0.0" + flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3b69769..7e010fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: logger: ^2.6.0 dio: ^5.8.0+1 shared_preferences: ^2.5.3 - + json_annotation: ^4.8.1 dev_dependencies: flutter_test: @@ -28,6 +28,7 @@ dev_dependencies: flutter_lints: ^5.0.0 build_runner: ^2.5.4 go_router_builder: ^3.0.0 + json_serializable: ^6.7.1 flutter: From 360680d9901003894688a4465d0e0d24ac25d96f Mon Sep 17 00:00:00 2001 From: llsajjad Date: Sat, 9 Aug 2025 19:50:48 +0330 Subject: [PATCH 6/6] added TenderDetailRequest --- .../tender_detail_repository.dart | 10 +- .../mock_data/tender_detail_mock.dart | 4 +- .../tender_detail_request.dart | 16 ++ .../tender_detail_request.freezed.dart | 177 ++++++++++++++++++ .../tender_detail_request.g.dart | 15 ++ lib/data/services/tender_detail_service.dart | 34 ++-- lib/view_models/tender_detail_view_model.dart | 5 +- 7 files changed, 238 insertions(+), 23 deletions(-) create mode 100644 lib/data/services/model/tender_detail_request/tender_detail_request.dart create mode 100644 lib/data/services/model/tender_detail_request/tender_detail_request.freezed.dart create mode 100644 lib/data/services/model/tender_detail_request/tender_detail_request.g.dart diff --git a/lib/data/repositories/tender_detail_repository.dart b/lib/data/repositories/tender_detail_repository.dart index 752ff28..3ad1065 100644 --- a/lib/data/repositories/tender_detail_repository.dart +++ b/lib/data/repositories/tender_detail_repository.dart @@ -1,4 +1,5 @@ import 'package:tm_app/core/utils/result.dart'; +import 'package:tm_app/data/services/model/tender_detail_request/tender_detail_request.dart'; import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; import 'package:tm_app/data/services/tender_detail_service.dart'; @@ -8,7 +9,10 @@ class TenderDetailRepository { final TenderDetailService _tenderDetailService; - Future> getTenderDetail({required String id}) async { - return _tenderDetailService.getTenderDetail(id: id); - } +Future> getTenderDetail({ + required TenderDetailRequest request, +}) async { + return await _tenderDetailService.getTenderDetail(request: request); +} + } diff --git a/lib/data/services/mock_data/tender_detail_mock.dart b/lib/data/services/mock_data/tender_detail_mock.dart index 0b8cd3f..4be8c1e 100644 --- a/lib/data/services/mock_data/tender_detail_mock.dart +++ b/lib/data/services/mock_data/tender_detail_mock.dart @@ -11,7 +11,7 @@ const tenderDetailMockData = ''' "deliveryLocation": "Norrbotten County", "referenceNumber": "KLF 2025/120", "country": "UK", - "flagAsset": "assets/icons/SE.pngtest@example.compassword123", + "flagAsset": "assets/icons/SE.png", "locationTitle": "Lorem ipsum is amet", "locationDescription": "Luleå Municipality is procuring on behalf of Norrbotten’s e-board (all municipalities in Norrbotten). The procurement includes operation, support, and further development of the existing open e-platform. No additional information, amendments, or answers to questions will be provided after June 16.", "documentName": "PDF, Document", @@ -21,4 +21,4 @@ const tenderDetailMockData = ''' "incompleteResumeReason": "No experience in e-platform development" } } -'''; +'''; \ No newline at end of file diff --git a/lib/data/services/model/tender_detail_request/tender_detail_request.dart b/lib/data/services/model/tender_detail_request/tender_detail_request.dart new file mode 100644 index 0000000..6b0aacd --- /dev/null +++ b/lib/data/services/model/tender_detail_request/tender_detail_request.dart @@ -0,0 +1,16 @@ +// ignore_for_file: invalid_annotation_target + +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'tender_detail_request.freezed.dart'; +part 'tender_detail_request.g.dart'; + +@freezed +class TenderDetailRequest with _$TenderDetailRequest { + const factory TenderDetailRequest({ + @JsonKey(name: 'id') required String id, + }) = _TenderDetailRequest; + + factory TenderDetailRequest.fromJson(Map json) => + _$TenderDetailRequestFromJson(json); +} diff --git a/lib/data/services/model/tender_detail_request/tender_detail_request.freezed.dart b/lib/data/services/model/tender_detail_request/tender_detail_request.freezed.dart new file mode 100644 index 0000000..25106c6 --- /dev/null +++ b/lib/data/services/model/tender_detail_request/tender_detail_request.freezed.dart @@ -0,0 +1,177 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'tender_detail_request.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models', +); + +TenderDetailRequest _$TenderDetailRequestFromJson(Map json) { + return _TenderDetailRequest.fromJson(json); +} + +/// @nodoc +mixin _$TenderDetailRequest { + @JsonKey(name: "id") + String get id => throw _privateConstructorUsedError; + + /// Serializes this TenderDetailRequest to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of TenderDetailRequest + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $TenderDetailRequestCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TenderDetailRequestCopyWith<$Res> { + factory $TenderDetailRequestCopyWith( + TenderDetailRequest value, + $Res Function(TenderDetailRequest) then, + ) = _$TenderDetailRequestCopyWithImpl<$Res, TenderDetailRequest>; + @useResult + $Res call({@JsonKey(name: "id") String id}); +} + +/// @nodoc +class _$TenderDetailRequestCopyWithImpl<$Res, $Val extends TenderDetailRequest> + implements $TenderDetailRequestCopyWith<$Res> { + _$TenderDetailRequestCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of TenderDetailRequest + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? id = null}) { + return _then( + _value.copyWith( + id: + null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + ) + as $Val, + ); + } +} + +/// @nodoc +abstract class _$$TenderDetailRequestImplCopyWith<$Res> + implements $TenderDetailRequestCopyWith<$Res> { + factory _$$TenderDetailRequestImplCopyWith( + _$TenderDetailRequestImpl value, + $Res Function(_$TenderDetailRequestImpl) then, + ) = __$$TenderDetailRequestImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({@JsonKey(name: "id") String id}); +} + +/// @nodoc +class __$$TenderDetailRequestImplCopyWithImpl<$Res> + extends _$TenderDetailRequestCopyWithImpl<$Res, _$TenderDetailRequestImpl> + implements _$$TenderDetailRequestImplCopyWith<$Res> { + __$$TenderDetailRequestImplCopyWithImpl( + _$TenderDetailRequestImpl _value, + $Res Function(_$TenderDetailRequestImpl) _then, + ) : super(_value, _then); + + /// Create a copy of TenderDetailRequest + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({Object? id = null}) { + return _then( + _$TenderDetailRequestImpl( + id: + null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + ), + ); + } +} + +/// @nodoc +@JsonSerializable() +class _$TenderDetailRequestImpl implements _TenderDetailRequest { + const _$TenderDetailRequestImpl({@JsonKey(name: "id") required this.id}); + + factory _$TenderDetailRequestImpl.fromJson(Map json) => + _$$TenderDetailRequestImplFromJson(json); + + @override + @JsonKey(name: "id") + final String id; + + @override + String toString() { + return 'TenderDetailRequest(id: $id)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TenderDetailRequestImpl && + (identical(other.id, id) || other.id == id)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, id); + + /// Create a copy of TenderDetailRequest + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$TenderDetailRequestImplCopyWith<_$TenderDetailRequestImpl> get copyWith => + __$$TenderDetailRequestImplCopyWithImpl<_$TenderDetailRequestImpl>( + this, + _$identity, + ); + + @override + Map toJson() { + return _$$TenderDetailRequestImplToJson(this); + } +} + +abstract class _TenderDetailRequest implements TenderDetailRequest { + const factory _TenderDetailRequest({ + @JsonKey(name: "id") required final String id, + }) = _$TenderDetailRequestImpl; + + factory _TenderDetailRequest.fromJson(Map json) = + _$TenderDetailRequestImpl.fromJson; + + @override + @JsonKey(name: "id") + String get id; + + /// Create a copy of TenderDetailRequest + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$TenderDetailRequestImplCopyWith<_$TenderDetailRequestImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/data/services/model/tender_detail_request/tender_detail_request.g.dart b/lib/data/services/model/tender_detail_request/tender_detail_request.g.dart new file mode 100644 index 0000000..25a3342 --- /dev/null +++ b/lib/data/services/model/tender_detail_request/tender_detail_request.g.dart @@ -0,0 +1,15 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tender_detail_request.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$TenderDetailRequestImpl _$$TenderDetailRequestImplFromJson( + Map json, +) => _$TenderDetailRequestImpl(id: json['id'] as String); + +Map _$$TenderDetailRequestImplToJson( + _$TenderDetailRequestImpl instance, +) => {'id': instance.id}; diff --git a/lib/data/services/tender_detail_service.dart b/lib/data/services/tender_detail_service.dart index 45d9716..fa5cc99 100644 --- a/lib/data/services/tender_detail_service.dart +++ b/lib/data/services/tender_detail_service.dart @@ -4,6 +4,7 @@ import 'package:dio/dio.dart'; import 'package:tm_app/core/utils/logger.dart'; import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/services/mock_data/tender_detail_mock.dart'; +import 'package:tm_app/data/services/model/tender_detail_request/tender_detail_request.dart'; import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; import 'package:tm_app/data/services/network_manager.dart'; @@ -13,22 +14,23 @@ class TenderDetailService { final NetworkManager _networkManager; - Future> getTenderDetail({required String id}) async { - try { - await Future.delayed(Duration(seconds: 2)); // شبیه سازی لود - appLogger.info('get tender detail success'); +Future> getTenderDetail({ + required TenderDetailRequest request, +}) async { + try { + await Future.delayed(const Duration(seconds: 2)); + appLogger.info('get tender detail success'); - return Result.ok( - TenderDetailResponseModel.fromJson( - jsonDecode(tenderDetailMockData)['data'], - ), - ); - } on DioException catch (e) { - appLogger.error('get tender detail failed: $e'); - return Result.error(e); - } on Exception catch (e, stackTrace) { - appLogger.error('get tender detail failed: $e', stackTrace: stackTrace); - return Result.error(Exception(e)); - } + return Result.ok( + TenderDetailResponseModel.fromJson( + jsonDecode(tenderDetailMockData)['data'], + ), + ); + } catch (e, stackTrace) { + appLogger.error('get tender detail failed: $e', stackTrace: stackTrace); + return Result.error(Exception(e)); } } + + +} diff --git a/lib/view_models/tender_detail_view_model.dart b/lib/view_models/tender_detail_view_model.dart index 823b7dc..398b8af 100644 --- a/lib/view_models/tender_detail_view_model.dart +++ b/lib/view_models/tender_detail_view_model.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:tm_app/core/utils/result.dart'; import 'package:tm_app/data/repositories/tender_detail_repository.dart'; +import 'package:tm_app/data/services/model/tender_detail_request/tender_detail_request.dart'; import 'package:tm_app/data/services/model/tender_detail_response/tender_detail_response_model.dart'; class TenderDetailViewModel with ChangeNotifier { @@ -22,8 +23,8 @@ class TenderDetailViewModel with ChangeNotifier { _errorMessage = null; notifyListeners(); - final result = await _tenderDetailRepository.getTenderDetail(id: id); - + final request = TenderDetailRequest(id: id); +final result = await _tenderDetailRepository.getTenderDetail(request: request); switch (result) { case Ok(): _tenderDetail = result.value;