From 70ede0ce6719d4d192482cc514a7dc2c161631c8 Mon Sep 17 00:00:00 2001 From: jayesh Date: Thu, 6 Jun 2024 14:51:31 +0530 Subject: [PATCH 1/2] language change screen ui --- .../contact_admin_screen/svg/mail_icon.svg | 7 ++ .../contact_admin_screen/svg/phone_icon.svg | 3 + .../images/language_screen/png/info_icon.png | Bin 0 -> 1647 bytes .../images/language_screen/svg/info_icon.svg | 11 +++ lib/core/routes/route_name.dart | 7 +- lib/core/routes/routes.dart | 16 ++++ lib/core/styles/app_color.dart | 7 ++ lib/core/styles/app_images.dart | 10 +++ lib/core/styles/app_text.dart | 11 +++ lib/core/utils/url_launcher/url_launcher.dart | 19 ++++ .../widgets/general_settings_section.dart | 7 +- .../widgets/privacy_settings_section.dart | 6 +- .../widgets/settings_list_tile_item.dart | 3 + .../widgets/support_settings_section.dart | 16 +++- .../pages/contact_admin_screen.dart | 26 ++++++ .../presentation/widgets/bottom_section.dart | 81 ++++++++++++++++++ .../presentation/widgets/top_section.dart | 27 ++++++ .../bloc/choose_language_bloc.dart | 33 +++++++ .../bloc/choose_language_event.dart | 19 ++++ .../bloc/choose_language_state.dart | 19 ++++ .../pages/language_change_layout.dart | 52 +++++++++++ .../pages/language_change_screen.dart | 26 ++++++ .../presentation/widgets/bottom_section.dart | 41 +++++++++ .../widgets/language_change_list.dart | 52 +++++++++++ pubspec.lock | 6 +- pubspec.yaml | 7 ++ 26 files changed, 501 insertions(+), 11 deletions(-) create mode 100644 assets/images/contact_admin_screen/svg/mail_icon.svg create mode 100644 assets/images/contact_admin_screen/svg/phone_icon.svg create mode 100644 assets/images/language_screen/png/info_icon.png create mode 100644 assets/images/language_screen/svg/info_icon.svg create mode 100644 lib/core/utils/url_launcher/url_launcher.dart create mode 100644 lib/features/contactAdmin/presentation/pages/contact_admin_screen.dart create mode 100644 lib/features/contactAdmin/presentation/widgets/bottom_section.dart create mode 100644 lib/features/contactAdmin/presentation/widgets/top_section.dart create mode 100644 lib/features/languageChange/presentation/bloc/choose_language_bloc.dart create mode 100644 lib/features/languageChange/presentation/bloc/choose_language_event.dart create mode 100644 lib/features/languageChange/presentation/bloc/choose_language_state.dart create mode 100644 lib/features/languageChange/presentation/pages/language_change_layout.dart create mode 100644 lib/features/languageChange/presentation/pages/language_change_screen.dart create mode 100644 lib/features/languageChange/presentation/widgets/bottom_section.dart create mode 100644 lib/features/languageChange/presentation/widgets/language_change_list.dart diff --git a/assets/images/contact_admin_screen/svg/mail_icon.svg b/assets/images/contact_admin_screen/svg/mail_icon.svg new file mode 100644 index 0000000..bcbaeae --- /dev/null +++ b/assets/images/contact_admin_screen/svg/mail_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/contact_admin_screen/svg/phone_icon.svg b/assets/images/contact_admin_screen/svg/phone_icon.svg new file mode 100644 index 0000000..ac5ae1a --- /dev/null +++ b/assets/images/contact_admin_screen/svg/phone_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/language_screen/png/info_icon.png b/assets/images/language_screen/png/info_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fcd032baba39060374c849730de93d9108467849 GIT binary patch literal 1647 zcmV-#29WuQP)@~0drDELIAGL9O(c600d`2O+f$vv5yP`z_!ofZ{fwE`zBJGJjxyac-X&-DgH*e}dTjLEb)v3bgyH_a~-n7_49G7P>CojgGJX%7`sodD_6 z9^WK|E=FMVQ?Z`GxC6{=Rpk|<7oZM?KS?SC-AA}^2P0;*0(AE}MA$HsWE*v4;1S`~ z`xp_U5um$ovX^+%7(c)mz`z!Cylm!(2qmLXHY?wCNyYo)Vtk8vDm$c_`{;hZgpw)G z1CC?;?H93rY%$&tVtrv5B~gw5Cgc#s56!wScGB(m=@y?C{Tn#($M1xcS?+JF%?;&=ELJmZ#ae* zpu4*~~i2Z4Ak9A;>ILNYN&y7}kkdF@F-m6Mq40+&3#+)TN+S9evjEyzhMA zAA(f80WrR%W#$9#DBvf64Lzn_0^t~2=Q5REc;FR+XnF5xm03^w%~^c&`6bM~eF3ct zO>F(x$MW{CIQ;Vnm64{VGxK?0?Bw@=mEGG_8~=yZ_Yl1MMrwRZ=ff8ESKr4)^I2u2 zn+1+?7asEoVDesKT0(_vbl!Ps_*ZFNXkhb;OQ;f3>So@xK~4{N-l=-Qb^U|0zu(+j zucKmGdA%T)07Po&g-4lKFQ}T-3v%Etz^%u@RFxb2X_@Lk;5yh{fTXbm!3_mxFK`pU zPQFB6LU1D8VCFa(J9|`@10SM|d2)Q7_b(eA30@zq4 z*^dX*c9{pV1!z8-zLzaU(KrgShGYw%RRF;c&W;?}0@zb2f+y;7<_KhtpoMBwk{CMy4u1dV%+I!e zs_l8~GjS2%5H;efZ=+Map#LmEcl$TgjLauu>^va)<3D_~{^aG;ZVkWl%ioy4`viyP zvsyuE>x6S@W({D%Q?sC`)nd$wu(vW7U+g1A;8Xq>PQZ`K%xg_u5o;7Rtw z>)-*+2!bD4^|FRIOu~&G1W%fo(IZ=cwpG{ z06EcQyK`lexeiE)O)OKfb*IWJqx$ZZb3a$Bd#DA;Y|mwwn+Mof#(D>W6J%SWIUdw- zE=6!dd7lKFHngS(u4CP6L0>QQ;80u=Z1$7d%86XXIEKCl;anKV)pssmHB`^qFeF}Z zCg$naGr2Oxd;*w>MZDl_$j1KCPf4a;KoNu!aHPY9HRQyvRe^0ksrVs;moWGS*7LW! z^zIcwtGkcP{~ACr=xtvA*y{|1_bW+Gnl~U5vZc>j=RTl4!2g|aI3MLrxh4@4dWLH7 zI^0Y7^@Wg_rZy`g=6gPI|5b+G6`{GYX8USl+A!Z)Z(Q{bJp+c{mSWci++3Wl)YNuH zi5F1-#kQ?f$s=IW&yyMB>$T_GNpwpBin{k$vt9fWp|BX`w!Jd2pewh zY26G*eVIk zD6Kn8wntH6o^fc>$t`WSSlV5Fa+#+iR*+^q2dx9eX!G$^wkDlT`&XPyYhUc(=ExX3 t(!>X#W`v|stJ_lrK@bE%5ClOG{s-m5MieF`djJ3c002ovPDHLkV1f?V1XBP2 literal 0 HcmV?d00001 diff --git a/assets/images/language_screen/svg/info_icon.svg b/assets/images/language_screen/svg/info_icon.svg new file mode 100644 index 0000000..127689b --- /dev/null +++ b/assets/images/language_screen/svg/info_icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/lib/core/routes/route_name.dart b/lib/core/routes/route_name.dart index a442173..b8b5540 100644 --- a/lib/core/routes/route_name.dart +++ b/lib/core/routes/route_name.dart @@ -43,8 +43,13 @@ class RouteName { static const String confirmPinScreen = 'confirmPinScreen'; //Forgot Password - static const String forgotPasswordPhoneVerificationScreen = 'forgotPasswordPhoneVerificationScreen'; static const String forgotPasswordScreen = 'forgotPasswordScreen'; + + //contact Admin + static const String contactAdminScreen = 'contactAdminScreen'; + + //language change + static const String languageChangeScreen = 'languageChangeScreen'; } diff --git a/lib/core/routes/routes.dart b/lib/core/routes/routes.dart index aee7798..9698747 100644 --- a/lib/core/routes/routes.dart +++ b/lib/core/routes/routes.dart @@ -7,9 +7,11 @@ import 'package:tanami_app/features/MainScreens/Academy/presentation/pages/acade import 'package:tanami_app/features/MainScreens/Portfolio/presentation/pages/portfolio_details_screen.dart'; import 'package:tanami_app/features/biometric/presentation/pages/biometric_screen.dart'; +import 'package:tanami_app/features/contactAdmin/presentation/pages/contact_admin_screen.dart'; import 'package:tanami_app/features/countrySelection/presentation/pages/choose_country_screen.dart'; import 'package:tanami_app/features/forgotPassword/presentation/pages/restore_password_screen.dart'; +import 'package:tanami_app/features/languageChange/presentation/pages/language_change_screen.dart'; import 'package:tanami_app/features/otpVerification/presentation/pages/otp_screen.dart'; import 'package:tanami_app/features/register/presentation/pages/register_screen.dart'; import 'package:tanami_app/features/register/presentation/pages/register_step_screen.dart'; @@ -152,6 +154,20 @@ final goRouter = GoRouter( return const RestorePasswordScreen(); }, ), + GoRoute( + name: RouteName.contactAdminScreen, + path: RouteName.contactAdminScreen, + builder: (context, state) { + return const ContactAdminScreen(); + }, + ), + GoRoute( + name: RouteName.languageChangeScreen, + path: RouteName.languageChangeScreen, + builder: (context, state) { + return const LanguageChaneScreen(); + }, + ), ], ), // GoRoute( diff --git a/lib/core/styles/app_color.dart b/lib/core/styles/app_color.dart index b0f4a31..201e717 100644 --- a/lib/core/styles/app_color.dart +++ b/lib/core/styles/app_color.dart @@ -73,4 +73,11 @@ class AppColor { //KYC Card Color static const Color kycCardTextColor = Color(0xFF074A23); static const Color kycCardBgColor = Color(0x7F074A23); + + //Contact Admin Color + static const Color contactAdminIconColor = Color(0xFF05391B); + static const Color contactAdminBgColor = Color(0xFFE2EEE2); + + //Language Color + static const Color languageTextColor = Color(0xFF015698); } diff --git a/lib/core/styles/app_images.dart b/lib/core/styles/app_images.dart index 51f1f1e..4099df1 100644 --- a/lib/core/styles/app_images.dart +++ b/lib/core/styles/app_images.dart @@ -92,4 +92,14 @@ class AppImages { 'assets/images/settings_screen/svg/reset_pin_icon.svg'; static const String contactIcon = 'assets/images/settings_screen/svg/contact_icon.svg'; + + //Contact + static const String byPhoneIcon = + 'assets/images/contact_admin_screen/svg/phone_icon.svg'; + static const String byMailIcon = + 'assets/images/contact_admin_screen/svg/mail_icon.svg'; + + //Language + static const String infoIcon = + 'assets/images/language_screen/png/info_icon.png'; } diff --git a/lib/core/styles/app_text.dart b/lib/core/styles/app_text.dart index da589a1..d6d5870 100644 --- a/lib/core/styles/app_text.dart +++ b/lib/core/styles/app_text.dart @@ -151,4 +151,15 @@ class AppText { static const String faqText = "FAQ"; static const String logoutText = "Log Out"; static const String deleteAccountText = "Delete account"; + + //Contact Admin + static const String byPhoneText = "By phone"; + static const String byEmailText = "By-mail"; + static const String weAreHereToHelp = "We are here to help!"; + + //Language Screen + static const String arabicText = "اللغة العربية"; + static const String chooseTheLanguageText = "Choose the language"; + static const String changingTheLanguageWillReloadApp = + "Changing the language will reload the application"; } diff --git a/lib/core/utils/url_launcher/url_launcher.dart b/lib/core/utils/url_launcher/url_launcher.dart new file mode 100644 index 0000000..89d323d --- /dev/null +++ b/lib/core/utils/url_launcher/url_launcher.dart @@ -0,0 +1,19 @@ +import 'package:url_launcher/url_launcher.dart'; + +void launchPhone(String phoneNumber) async { + final url = 'tel:$phoneNumber'; + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); + } else { + throw 'Could not launch $url'; + } +} + +void launchEmail(String email) async { + final url = 'mailto:$email'; + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); + } else { + throw 'Could not launch $url'; + } +} diff --git a/lib/features/MainScreens/Settings/presentation/widgets/general_settings_section.dart b/lib/features/MainScreens/Settings/presentation/widgets/general_settings_section.dart index 5c6bb61..3d378c6 100644 --- a/lib/features/MainScreens/Settings/presentation/widgets/general_settings_section.dart +++ b/lib/features/MainScreens/Settings/presentation/widgets/general_settings_section.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; +import 'package:tanami_app/core/routes/route_name.dart'; +import 'package:tanami_app/core/routes/routes.dart'; import 'package:tanami_app/core/styles/app_color.dart'; import 'package:tanami_app/core/styles/app_images.dart'; import 'package:tanami_app/core/styles/app_text.dart'; @@ -37,7 +39,10 @@ class GeneralSettingsSection extends StatelessWidget { ], ), const Gap(12), - const SettingsListItem( + SettingsListItem( + onTapFunc: () { + goRouter.pushNamed(RouteName.languageChangeScreen); + }, icon: AppImages.languageIcon, title: AppText.languageText, trailing: AppText.englishText, diff --git a/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart b/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart index 91a64bf..e627b56 100644 --- a/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart +++ b/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart @@ -37,13 +37,15 @@ class PrivacySettingsSection extends StatelessWidget { ], ), const Gap(12), - const SettingsListItem( + SettingsListItem( + onTapFunc: () {}, icon: AppImages.resetPasswordIcon, title: AppText.resetPasswordText, trailing: "", ), const Gap(8), - const SettingsListItem( + SettingsListItem( + onTapFunc: () {}, icon: AppImages.resetPinIcon, title: AppText.resetPinCodeText, trailing: "", diff --git a/lib/features/MainScreens/Settings/presentation/widgets/settings_list_tile_item.dart b/lib/features/MainScreens/Settings/presentation/widgets/settings_list_tile_item.dart index 78b39e0..cdf93b8 100644 --- a/lib/features/MainScreens/Settings/presentation/widgets/settings_list_tile_item.dart +++ b/lib/features/MainScreens/Settings/presentation/widgets/settings_list_tile_item.dart @@ -8,12 +8,14 @@ class SettingsListItem extends StatelessWidget { final String icon; final String title; final String trailing; + final Function() onTapFunc; const SettingsListItem({ super.key, required this.icon, required this.title, required this.trailing, + required this.onTapFunc, }); @override @@ -27,6 +29,7 @@ class SettingsListItem extends StatelessWidget { color: AppColor.fillColor, ), child: ListTile( + onTap: onTapFunc, leading: SvgPicture.asset(icon), title: Text(title, style: GoogleFonts.dmSans( diff --git a/lib/features/MainScreens/Settings/presentation/widgets/support_settings_section.dart b/lib/features/MainScreens/Settings/presentation/widgets/support_settings_section.dart index 7e57a50..630e9ac 100644 --- a/lib/features/MainScreens/Settings/presentation/widgets/support_settings_section.dart +++ b/lib/features/MainScreens/Settings/presentation/widgets/support_settings_section.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; +import 'package:tanami_app/core/routes/route_name.dart'; +import 'package:tanami_app/core/routes/routes.dart'; import 'package:tanami_app/core/styles/app_color.dart'; import 'package:tanami_app/core/styles/app_images.dart'; import 'package:tanami_app/core/styles/app_text.dart'; @@ -20,25 +22,31 @@ class SupportSettingsSection extends StatelessWidget { clr: AppColor.hintTextColor, ), const Gap(8), - const SettingsListItem( + SettingsListItem( + onTapFunc: () { + goRouter.pushNamed(RouteName.contactAdminScreen); + }, icon: AppImages.contactIcon, title: AppText.contactAdminText, trailing: "", ), const Gap(8), - const SettingsListItem( + SettingsListItem( + onTapFunc: () {}, icon: AppImages.privacyIcon, title: AppText.privacyPolicy, trailing: "", ), const Gap(8), - const SettingsListItem( + SettingsListItem( + onTapFunc: () {}, icon: AppImages.privacyIcon, title: AppText.termsAndCondition, trailing: "", ), const Gap(8), - const SettingsListItem( + SettingsListItem( + onTapFunc: () {}, icon: AppImages.faqIcon, title: AppText.faqText, trailing: "", diff --git a/lib/features/contactAdmin/presentation/pages/contact_admin_screen.dart b/lib/features/contactAdmin/presentation/pages/contact_admin_screen.dart new file mode 100644 index 0000000..d681ba1 --- /dev/null +++ b/lib/features/contactAdmin/presentation/pages/contact_admin_screen.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:tanami_app/features/contactAdmin/presentation/widgets/bottom_section.dart'; + +import '../../../../core/styles/app_text.dart'; +import '../../../../shared/components/appbar_widget.dart'; +import '../widgets/top_section.dart'; + +class ContactAdminScreen extends StatelessWidget { + const ContactAdminScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const AppBarWidget( + height: 75, + titleTxt: AppText.contactAdminText, + ), + body: ListView( + children: [ + topSection(), + bottomSection(), + ], + ), + ); + } +} diff --git a/lib/features/contactAdmin/presentation/widgets/bottom_section.dart b/lib/features/contactAdmin/presentation/widgets/bottom_section.dart new file mode 100644 index 0000000..ffdc161 --- /dev/null +++ b/lib/features/contactAdmin/presentation/widgets/bottom_section.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/core/utils/url_launcher/url_launcher.dart'; + +import '../../../../shared/components/text_widget.dart'; + +Widget bottomSection() { + return Padding( + padding: const EdgeInsets.all(22.0), + child: Column( + children: [ + const Gap(50.0), + contactOption( + icon: AppImages.byPhoneIcon, + title: AppText.byPhoneText, + subtitle: '+973 12345678', + onTap: () { + launchPhone('+973 12345678'); + }, + ), + const Gap(16.0), + contactOption( + icon: AppImages.byMailIcon, + title: AppText.byEmailText, + subtitle: 'info@tanamicapital.com', + onTap: () { + launchEmail('info@tanamicapital.com'); + }, + ), + ], + ), + ); +} + +Widget contactOption({ + required String icon, + required String title, + required String subtitle, + required VoidCallback onTap, +}) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0), + decoration: BoxDecoration( + color: AppColor.contactAdminBgColor, + borderRadius: BorderRadius.circular(22.0), + ), + child: Row( + children: [ + SvgPicture.asset( + icon, + ), + const Gap(16.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget().text14W600( + title, + clr: AppColor.contactAdminIconColor, + ), + TextWidget().text14W400( + subtitle, + clr: AppColor.contactAdminIconColor, + ), + ], + ), + const Spacer(), + const Icon( + Icons.arrow_forward_ios, + color: AppColor.contactAdminIconColor, + ), + ], + ), + ), + ); +} diff --git a/lib/features/contactAdmin/presentation/widgets/top_section.dart b/lib/features/contactAdmin/presentation/widgets/top_section.dart new file mode 100644 index 0000000..6b051dc --- /dev/null +++ b/lib/features/contactAdmin/presentation/widgets/top_section.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:gap/gap.dart'; + +import '../../../../core/styles/app_color.dart'; +import '../../../../core/styles/app_images.dart'; +import '../../../../core/styles/app_text.dart'; +import '../../../../shared/components/text_widget.dart'; + +Widget topSection() { + return Column( + children: [ + const Gap(85), + Center( + child: SvgPicture.asset( + AppImages.weclomeLogo, + ), + ), + const Gap(24), + TextWidget().text17W700( + AppText.weAreHereToHelp, + clr: AppColor.textLabelColor, + ), + const Gap(10), + ], + ); +} diff --git a/lib/features/languageChange/presentation/bloc/choose_language_bloc.dart b/lib/features/languageChange/presentation/bloc/choose_language_bloc.dart new file mode 100644 index 0000000..b5f007c --- /dev/null +++ b/lib/features/languageChange/presentation/bloc/choose_language_bloc.dart @@ -0,0 +1,33 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'choose_language_event.dart'; +import 'choose_language_state.dart'; + +class ChooseLanguageBloc + extends Bloc { + ChooseLanguageBloc() : super(ChooseLanguageInitial()) { + on(_onRadioSelected); + on(_onResetRadioSelection); + } + + void _onRadioSelected( + ChooseLanguageSelected event, Emitter emit) { + emit(ChooseLanguageSelectionChanged(event.selectedIndex)); + } + + void _onResetRadioSelection( + ResetRadioSelection event, Emitter emit) { + emit(ChooseLanguageInitial()); + } + + void resetSelection() { + add(ResetRadioSelection()); + } + + int get selectedCountry { + if (state is ChooseLanguageSelectionChanged) { + return (state as ChooseLanguageSelectionChanged).selectedIndex; + } + return 0; + } +} diff --git a/lib/features/languageChange/presentation/bloc/choose_language_event.dart b/lib/features/languageChange/presentation/bloc/choose_language_event.dart new file mode 100644 index 0000000..b94db09 --- /dev/null +++ b/lib/features/languageChange/presentation/bloc/choose_language_event.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; + +abstract class ChooseLanguageEvent extends Equatable { + const ChooseLanguageEvent(); + + @override + List get props => []; +} + +class ChooseLanguageSelected extends ChooseLanguageEvent { + final int selectedIndex; + + const ChooseLanguageSelected([this.selectedIndex = 0]); + + @override + List get props => [selectedIndex]; +} + +class ResetRadioSelection extends ChooseLanguageEvent {} diff --git a/lib/features/languageChange/presentation/bloc/choose_language_state.dart b/lib/features/languageChange/presentation/bloc/choose_language_state.dart new file mode 100644 index 0000000..ede1c9f --- /dev/null +++ b/lib/features/languageChange/presentation/bloc/choose_language_state.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; + +abstract class ChooseLanguageState extends Equatable { + const ChooseLanguageState(); + + @override + List get props => []; +} + +class ChooseLanguageInitial extends ChooseLanguageState {} + +class ChooseLanguageSelectionChanged extends ChooseLanguageState { + final int selectedIndex; + + const ChooseLanguageSelectionChanged(this.selectedIndex); + + @override + List get props => [selectedIndex]; +} diff --git a/lib/features/languageChange/presentation/pages/language_change_layout.dart b/lib/features/languageChange/presentation/pages/language_change_layout.dart new file mode 100644 index 0000000..803319d --- /dev/null +++ b/lib/features/languageChange/presentation/pages/language_change_layout.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/shared/components/appbar_widget.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; +import '../widgets/bottom_section.dart'; +import '../widgets/language_change_list.dart'; + +class LanguageChangeLayout extends StatelessWidget { + const LanguageChangeLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + bottomNavigationBar: bottomSection(), + appBar: const AppBarWidget( + height: 75, + titleTxt: AppText.languageText, + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const CountrySelectionList(), + const Gap(24), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + width: 24, + height: 24, + AppImages.infoIcon, + ), + const Gap(8), + SizedBox( + width: 0.8.sw, + child: TextWidget().text12W500( + AppText.changingTheLanguageWillReloadApp, + clr: AppColor.languageTextColor, + ), + ) + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/languageChange/presentation/pages/language_change_screen.dart b/lib/features/languageChange/presentation/pages/language_change_screen.dart new file mode 100644 index 0000000..6b836c2 --- /dev/null +++ b/lib/features/languageChange/presentation/pages/language_change_screen.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/features/languageChange/presentation/bloc/choose_language_bloc.dart'; + +import 'language_change_layout.dart'; + +class LanguageChaneScreen extends StatelessWidget { + const LanguageChaneScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: true, + body: MultiBlocProvider( + // Define the providers for the OnboardingBloc and other blocs + providers: [ + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => ChooseLanguageBloc(), + ), + ], + child: const LanguageChangeLayout(), + ), + ); + } +} diff --git a/lib/features/languageChange/presentation/widgets/bottom_section.dart b/lib/features/languageChange/presentation/widgets/bottom_section.dart new file mode 100644 index 0000000..8d0ebce --- /dev/null +++ b/lib/features/languageChange/presentation/widgets/bottom_section.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gap/gap.dart'; + +import '../../../../core/routes/routes.dart'; +import '../../../../core/styles/app_color.dart'; +import '../../../../core/styles/app_text.dart'; +import '../../../../shared/components/button_widget.dart'; +import '../../../../shared/components/text_widget.dart'; + +Widget bottomSection() { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 10, + ), + width: 1.sw, + height: 56.h, + child: ButtonWidget().elevatedBtn( + txtClr: AppColor.plainWhite, + function: () { + goRouter.pop(); + }, + text: AppText.submitText, + clr: AppColor.primaryColor2, + ), + ), + ButtonWidget().textBtn( + function: () { + goRouter.pop(); + }, + text: TextWidget().text14W700(AppText.backText, + clr: AppColor.textLabelColor, + textDecoration: TextDecoration.underline)), + const Gap(20), + ], + ); +} diff --git a/lib/features/languageChange/presentation/widgets/language_change_list.dart b/lib/features/languageChange/presentation/widgets/language_change_list.dart new file mode 100644 index 0000000..9a54e29 --- /dev/null +++ b/lib/features/languageChange/presentation/widgets/language_change_list.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +import '../bloc/choose_language_bloc.dart'; +import '../bloc/choose_language_event.dart'; +import '../bloc/choose_language_state.dart'; + +class CountrySelectionList extends StatelessWidget { + const CountrySelectionList({super.key}); + + @override + Widget build(BuildContext context) { + final radioBloc = context.read(); + return BlocBuilder( + builder: (context, state) { + int selectedIndex = 0; + if (state is ChooseLanguageSelectionChanged) { + selectedIndex = state.selectedIndex; + } + + return Column( + children: List.generate(languageList.length, (int index) { + return Row( + children: [ + Radio( + activeColor: AppColor.radioActiveColor, + value: index, + groupValue: selectedIndex, + onChanged: (int? value) { + if (value != null) { + radioBloc.add(ChooseLanguageSelected(value)); + } + }, + ), + TextWidget().text14W500(languageList[index], + clr: AppColor.charcoalColor), + ], + ); + }), + ); + }, + ); + } +} + +List languageList = [ + AppText.englishText, + AppText.arabicText, +]; diff --git a/pubspec.lock b/pubspec.lock index 441a282..c56e58d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1222,13 +1222,13 @@ packages: source: hosted version: "10.3.0" url_launcher: - dependency: transitive + dependency: "direct main" description: name: url_launcher - sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" + sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" url: "https://pub.dev" source: hosted - version: "6.2.6" + version: "6.3.0" url_launcher_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5dfe525..f6efa91 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -78,6 +78,9 @@ dependencies: sms_autofill: ^2.3.1 carousel_slider: ^4.2.1 + #Url Launcher + url_launcher: ^6.3.0 + dev_dependencies: flutter_test: sdk: flutter @@ -109,6 +112,10 @@ flutter: - assets/images/biometric_screen/svg/ - assets/images/dialog/ - assets/images/dialog/svg/ + - assets/images/contact_admin_screen/ + - assets/images/contact_admin_screen/svg/ - assets/images/settings_screen/ - assets/images/settings_screen/png/ - assets/images/settings_screen/svg/ + - assets/images/language_screen/ + - assets/images/language_screen/png/ From 87ebbd288d99bcf5520cd1175d26d5a4bfa9b3e4 Mon Sep 17 00:00:00 2001 From: jayesh Date: Thu, 6 Jun 2024 18:40:58 +0530 Subject: [PATCH 2/2] delete account ui screen --- lib/core/routes/route_name.dart | 3 + lib/core/routes/routes.dart | 8 ++ lib/core/styles/app_color.dart | 3 + lib/core/styles/app_text.dart | 17 +++ .../widgets/settings_bottom_section.dart | 12 +- .../bloc/delete_account_bloc.dart | 65 ++++++++++ .../bloc/delete_account_event.dart | 30 +++++ .../bloc/delete_account_state.dart | 32 +++++ .../presentation/bloc/text_bloc.dart | 16 +++ .../presentation/bloc/text_event.dart | 15 +++ .../presentation/bloc/text_state.dart | 18 +++ .../pages/delete_account_layout.dart | 26 ++++ .../pages/delete_account_screen.dart | 40 ++++++ .../presentation/widgets/bottom_section.dart | 81 +++++++++++++ .../presentation/widgets/form_section.dart | 114 ++++++++++++++++++ .../presentation/widgets/top_section.dart | 33 +++++ .../login/presentation/bloc/login_bloc.dart | 2 + .../widgets/login_signup_button.dart | 2 +- .../bloc/checkbox/checkbox_bloc.dart | 9 ++ .../bloc/checkbox/checkbox_event.dart | 2 + .../bloc/checkbox/checkbox_state.dart | 9 ++ lib/shared/components/button_widget.dart | 24 +++- .../components/form_label_textfield.dart | 14 +++ .../components/text_from_field_widget.dart | 4 +- 24 files changed, 569 insertions(+), 10 deletions(-) create mode 100644 lib/features/deleteAccount/presentation/bloc/delete_account_bloc.dart create mode 100644 lib/features/deleteAccount/presentation/bloc/delete_account_event.dart create mode 100644 lib/features/deleteAccount/presentation/bloc/delete_account_state.dart create mode 100644 lib/features/deleteAccount/presentation/bloc/text_bloc.dart create mode 100644 lib/features/deleteAccount/presentation/bloc/text_event.dart create mode 100644 lib/features/deleteAccount/presentation/bloc/text_state.dart create mode 100644 lib/features/deleteAccount/presentation/pages/delete_account_layout.dart create mode 100644 lib/features/deleteAccount/presentation/pages/delete_account_screen.dart create mode 100644 lib/features/deleteAccount/presentation/widgets/bottom_section.dart create mode 100644 lib/features/deleteAccount/presentation/widgets/form_section.dart create mode 100644 lib/features/deleteAccount/presentation/widgets/top_section.dart diff --git a/lib/core/routes/route_name.dart b/lib/core/routes/route_name.dart index b8b5540..bb275bd 100644 --- a/lib/core/routes/route_name.dart +++ b/lib/core/routes/route_name.dart @@ -52,4 +52,7 @@ class RouteName { //language change static const String languageChangeScreen = 'languageChangeScreen'; + + //delete Account + static const String deleteAccountScreen = 'deleteAccountScreen'; } diff --git a/lib/core/routes/routes.dart b/lib/core/routes/routes.dart index 9698747..cbb9df7 100644 --- a/lib/core/routes/routes.dart +++ b/lib/core/routes/routes.dart @@ -10,6 +10,7 @@ import 'package:tanami_app/features/biometric/presentation/pages/biometric_scree import 'package:tanami_app/features/contactAdmin/presentation/pages/contact_admin_screen.dart'; import 'package:tanami_app/features/countrySelection/presentation/pages/choose_country_screen.dart'; +import 'package:tanami_app/features/deleteAccount/presentation/pages/delete_account_screen.dart'; import 'package:tanami_app/features/forgotPassword/presentation/pages/restore_password_screen.dart'; import 'package:tanami_app/features/languageChange/presentation/pages/language_change_screen.dart'; import 'package:tanami_app/features/otpVerification/presentation/pages/otp_screen.dart'; @@ -168,6 +169,13 @@ final goRouter = GoRouter( return const LanguageChaneScreen(); }, ), + GoRoute( + name: RouteName.deleteAccountScreen, + path: RouteName.deleteAccountScreen, + builder: (context, state) { + return const DeleteAccountScreen(); + }, + ), ], ), // GoRoute( diff --git a/lib/core/styles/app_color.dart b/lib/core/styles/app_color.dart index 201e717..8a54af0 100644 --- a/lib/core/styles/app_color.dart +++ b/lib/core/styles/app_color.dart @@ -80,4 +80,7 @@ class AppColor { //Language Color static const Color languageTextColor = Color(0xFF015698); + + //Delete Account Color + static const Color descriptionText = Color(0xFFC6C6C6); } diff --git a/lib/core/styles/app_text.dart b/lib/core/styles/app_text.dart index d6d5870..49e241c 100644 --- a/lib/core/styles/app_text.dart +++ b/lib/core/styles/app_text.dart @@ -162,4 +162,21 @@ class AppText { static const String chooseTheLanguageText = "Choose the language"; static const String changingTheLanguageWillReloadApp = "Changing the language will reload the application"; + + //Delete Screen + static const String weAreSadToSeeYouGo = "We're sad to see you go :("; + static const String enterAdescription = "Enter a description..."; + static const String toHelpUsImprovePleaseLetusKnowWhyYouWantToDeleteAccount = + "To help us improve, please let us know why you want to delete your account"; + static const String charactersLeft = "characters left"; + static const String iUnderstandAndAgreeThatmyDataWillBeDeleted = + "I understand and agree that my data will be deleted"; + static const String noIWantToStay = "No, i want to stay"; + static const String pleaseEnteraDescription = "Please enter a description"; + static const String pleaseCheckThisField = "Please check this field"; + static const String weHaveReceivedYourRequestToDeleteAccount = + "We have received your request to delete your account"; + static const String theRequestWillBeProcessed = + "The request will be processed within 72 hours"; + static const closeText = "Close"; } diff --git a/lib/features/MainScreens/Settings/presentation/widgets/settings_bottom_section.dart b/lib/features/MainScreens/Settings/presentation/widgets/settings_bottom_section.dart index 869f8e0..0ed36b8 100644 --- a/lib/features/MainScreens/Settings/presentation/widgets/settings_bottom_section.dart +++ b/lib/features/MainScreens/Settings/presentation/widgets/settings_bottom_section.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:gap/gap.dart'; +import 'package:tanami_app/core/routes/route_name.dart'; +import 'package:tanami_app/core/routes/routes.dart'; import '../../../../../core/styles/app_color.dart'; import '../../../../../core/styles/app_text.dart'; @@ -24,14 +26,20 @@ class SettingsBottomSection extends StatelessWidget { height: 56.h, child: ButtonWidget().elevatedBtn( txtClr: AppColor.plainWhite, - function: () {}, + function: () { + goRouter.goNamed(RouteName.loginScreen, pathParameters: { + "fromScreen": "registerStep", + }); + }, text: AppText.logoutText, clr: AppColor.primaryColor2, ), ), const Gap(5), ButtonWidget().textBtn( - function: () {}, + function: () { + goRouter.pushNamed(RouteName.deleteAccountScreen); + }, text: TextWidget().text14W700( AppText.deleteAccountText, clr: AppColor.hintTextColor, diff --git a/lib/features/deleteAccount/presentation/bloc/delete_account_bloc.dart b/lib/features/deleteAccount/presentation/bloc/delete_account_bloc.dart new file mode 100644 index 0000000..a8bd515 --- /dev/null +++ b/lib/features/deleteAccount/presentation/bloc/delete_account_bloc.dart @@ -0,0 +1,65 @@ +import 'package:bloc/bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:tanami_app/features/deleteAccount/presentation/bloc/delete_account_event.dart'; +import 'package:tanami_app/features/deleteAccount/presentation/bloc/delete_account_state.dart'; + +class DeleteAccountBloc extends Bloc { + final GlobalKey formKey = GlobalKey(); + final TextEditingController descriptionTextField = TextEditingController(); + + GlobalKey getFormKey() { + return formKey; + } + + DeleteAccountBloc() : super(DeleteAccountInitial()) { + descriptionTextField.addListener(_onFormFieldChanged); + + on(_onLoginFormChanged); + on((event, emit) async { + if (!formKey.currentState!.validate()) { + return; + } + emit(DeleteAccountLoading()); + try { + // Simulate API call + await Future.delayed(const Duration(seconds: 2)); + // Replace the next line with actual API call + final isSuccess = _mockApiCheck(); + if (isSuccess) { + emit(DeleteAccountSuccess()); + } else { + emit(const DeleteAccountFailure("Failed.")); + } + } catch (e) { + emit(DeleteAccountFailure(e.toString())); + } + }); + } + + bool _mockApiCheck() { + return true; + } + + void _onFormFieldChanged() { + add(DeleteAccountFormChanged( + descriptionTextField.text, + )); + } + + void _onLoginFormChanged( + DeleteAccountFormChanged event, Emitter emit) { + final areFieldsFilled = event.description.isNotEmpty; + emit(DeleteAccountFieldsState(areFieldsFilled)); + } + + // Method to reset text fields + void resetFields() { + descriptionTextField.clear(); + } + + @override + Future close() { + descriptionTextField.dispose(); + return super.close(); + } +} diff --git a/lib/features/deleteAccount/presentation/bloc/delete_account_event.dart b/lib/features/deleteAccount/presentation/bloc/delete_account_event.dart new file mode 100644 index 0000000..0db45bf --- /dev/null +++ b/lib/features/deleteAccount/presentation/bloc/delete_account_event.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; + +abstract class DeleteAccountEvent extends Equatable { + const DeleteAccountEvent(); + + @override + List get props => []; +} + +class DeleteAccountSubmitted extends DeleteAccountEvent { + final String description; + + const DeleteAccountSubmitted( + this.description, + ); + + @override + List get props => [description]; +} + +class DeleteAccountFormChanged extends DeleteAccountEvent { + final String description; + + const DeleteAccountFormChanged( + this.description, + ); + + @override + List get props => [description]; +} diff --git a/lib/features/deleteAccount/presentation/bloc/delete_account_state.dart b/lib/features/deleteAccount/presentation/bloc/delete_account_state.dart new file mode 100644 index 0000000..e4700a3 --- /dev/null +++ b/lib/features/deleteAccount/presentation/bloc/delete_account_state.dart @@ -0,0 +1,32 @@ +import 'package:equatable/equatable.dart'; + +abstract class DeleteAccountState extends Equatable { + const DeleteAccountState(); + + @override + List get props => []; +} + +class DeleteAccountInitial extends DeleteAccountState {} + +class DeleteAccountLoading extends DeleteAccountState {} + +class DeleteAccountSuccess extends DeleteAccountState {} + +class DeleteAccountFailure extends DeleteAccountState { + final String error; + + const DeleteAccountFailure(this.error); + + @override + List get props => [error]; +} + +class DeleteAccountFieldsState extends DeleteAccountState { + final bool areFieldsFilled; + + const DeleteAccountFieldsState(this.areFieldsFilled); + + @override + List get props => [areFieldsFilled]; +} diff --git a/lib/features/deleteAccount/presentation/bloc/text_bloc.dart b/lib/features/deleteAccount/presentation/bloc/text_bloc.dart new file mode 100644 index 0000000..ff759eb --- /dev/null +++ b/lib/features/deleteAccount/presentation/bloc/text_bloc.dart @@ -0,0 +1,16 @@ +import 'package:bloc/bloc.dart'; +import 'text_event.dart'; +import 'text_state.dart'; + +class TextBloc extends Bloc { + final int maxCharacters; + + TextBloc(this.maxCharacters) : super(TextInitial()) { + on(_onTextChanged); + } + + void _onTextChanged(TextChanged event, Emitter emit) { + final charactersLeft = maxCharacters - event.text.length; + emit(TextUpdated(event.text, charactersLeft)); + } +} diff --git a/lib/features/deleteAccount/presentation/bloc/text_event.dart b/lib/features/deleteAccount/presentation/bloc/text_event.dart new file mode 100644 index 0000000..0c8f637 --- /dev/null +++ b/lib/features/deleteAccount/presentation/bloc/text_event.dart @@ -0,0 +1,15 @@ +import 'package:equatable/equatable.dart'; + +abstract class TextEvent extends Equatable { + @override + List get props => []; +} + +class TextChanged extends TextEvent { + final String text; + + TextChanged(this.text); + + @override + List get props => [text]; +} diff --git a/lib/features/deleteAccount/presentation/bloc/text_state.dart b/lib/features/deleteAccount/presentation/bloc/text_state.dart new file mode 100644 index 0000000..1a18af2 --- /dev/null +++ b/lib/features/deleteAccount/presentation/bloc/text_state.dart @@ -0,0 +1,18 @@ +import 'package:equatable/equatable.dart'; + +abstract class TextState extends Equatable { + @override + List get props => []; +} + +class TextInitial extends TextState {} + +class TextUpdated extends TextState { + final String text; + final int charactersLeft; + + TextUpdated(this.text, this.charactersLeft); + + @override + List get props => [text, charactersLeft]; +} diff --git a/lib/features/deleteAccount/presentation/pages/delete_account_layout.dart b/lib/features/deleteAccount/presentation/pages/delete_account_layout.dart new file mode 100644 index 0000000..991896d --- /dev/null +++ b/lib/features/deleteAccount/presentation/pages/delete_account_layout.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/features/deleteAccount/presentation/widgets/top_section.dart'; + +import '../widgets/bottom_section.dart'; +import '../widgets/form_section.dart'; + +class DeleteAccountLayout extends StatelessWidget { + const DeleteAccountLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: true, + body: SingleChildScrollView( + child: Column( + children: [ + const TopSection(), + const FormSection(), + const Gap(30), + bottomSection(context), + ], + ), + )); + } +} diff --git a/lib/features/deleteAccount/presentation/pages/delete_account_screen.dart b/lib/features/deleteAccount/presentation/pages/delete_account_screen.dart new file mode 100644 index 0000000..2975ff0 --- /dev/null +++ b/lib/features/deleteAccount/presentation/pages/delete_account_screen.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/features/deleteAccount/presentation/bloc/text_bloc.dart'; +import 'package:tanami_app/features/deleteAccount/presentation/pages/delete_account_layout.dart'; + +import '../../../../core/styles/app_text.dart'; +import '../../../../shared/components/appbar_widget.dart'; +import '../../../../shared/components/bloc/checkbox/checkbox_bloc.dart'; +import '../bloc/delete_account_bloc.dart'; + +class DeleteAccountScreen extends StatelessWidget { + const DeleteAccountScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const AppBarWidget( + height: 75, + titleTxt: AppText.deleteAccountText, + ), + body: MultiBlocProvider( + providers: [ + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => DeleteAccountBloc(), + ), + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => CheckboxBloc(), + ), + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => TextBloc(350), + ), + ], + child: const DeleteAccountLayout(), + ), + ); + } +} diff --git a/lib/features/deleteAccount/presentation/widgets/bottom_section.dart b/lib/features/deleteAccount/presentation/widgets/bottom_section.dart new file mode 100644 index 0000000..2462071 --- /dev/null +++ b/lib/features/deleteAccount/presentation/widgets/bottom_section.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gap/gap.dart'; + +import '../../../../core/routes/routes.dart'; +import '../../../../core/styles/app_color.dart'; +import '../../../../core/styles/app_text.dart'; +import '../../../../shared/components/bloc/checkbox/checkbox_bloc.dart'; +import '../../../../shared/components/bloc/checkbox/checkbox_event.dart'; +import '../../../../shared/components/bloc/checkbox/checkbox_state.dart'; +import '../../../../shared/components/button_widget.dart'; +import '../../../../shared/components/loader.dart'; +import '../../../../shared/components/text_widget.dart'; +import '../../../../shared/components/toast_message.dart'; +import '../bloc/delete_account_bloc.dart'; +import '../bloc/delete_account_event.dart'; +import '../bloc/delete_account_state.dart'; + +Widget bottomSection(BuildContext context) { + final deleteAccountBloc = context.read(); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 10, + ), + width: 1.sw, + height: 56.h, + child: ButtonWidget().elevatedBtn( + txtClr: AppColor.plainWhite, + function: () { + goRouter.pop(); + }, + text: AppText.noIWantToStay, + clr: AppColor.primaryColor2, + ), + ), + BlocConsumer( + listener: (context, state) { + if (state is DeleteAccountLoading) { + Loader.loader(context); + } else if (state is DeleteAccountSuccess) { + goRouter.pop(); + } else if (state is DeleteAccountFailure) { + goRouter.pop(); + errorToastMessage( + context, + state.error, + ); + } + }, + builder: (context, state) { + return ButtonWidget().textBtn( + function: () { + if (deleteAccountBloc.formKey.currentState!.validate()) { + context.read().add(ValidateCheckbox()); + if (context.read().state is CheckboxChecked) { + context.read().add( + DeleteAccountSubmitted( + context + .read() + .descriptionTextField + .text, + ), + ); + } + } + }, + text: TextWidget().text14W700(AppText.deleteAccountText, + clr: AppColor.hintTextColor, + textDecoration: TextDecoration.underline)); + }, + ), + const Gap(20), + ], + ); +} diff --git a/lib/features/deleteAccount/presentation/widgets/form_section.dart b/lib/features/deleteAccount/presentation/widgets/form_section.dart new file mode 100644 index 0000000..f803543 --- /dev/null +++ b/lib/features/deleteAccount/presentation/widgets/form_section.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +import '../../../../shared/components/bloc/checkbox/checkbox_bloc.dart'; +import '../../../../shared/components/bloc/checkbox/checkbox_state.dart'; +import '../../../../shared/components/checkbox_widget.dart'; +import '../../../../shared/components/form_label_textfield.dart'; +import '../bloc/delete_account_bloc.dart'; +import '../bloc/text_bloc.dart'; +import '../bloc/text_event.dart'; +import '../bloc/text_state.dart'; + +class FormSection extends StatelessWidget { + const FormSection({super.key}); + + @override + Widget build(BuildContext context) { + final deleteAccountBloc = context.read(); + return Form( + key: deleteAccountBloc.formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 16.0, + right: 16, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(40), + BlocBuilder( + builder: (context, state) { + return FormLabelTextField( + onChangeFun: (p0) { + context.read().add(TextChanged( + deleteAccountBloc.descriptionTextField.text, + )); + }, + hintText: AppText.enterAdescription, + textEditingController: + deleteAccountBloc.descriptionTextField, + title: AppText + .toHelpUsImprovePleaseLetusKnowWhyYouWantToDeleteAccount, + type: "description", + ); + }, + ), + const Gap(8), + BlocBuilder( + builder: (context, state) { + if (state is TextUpdated) { + return TextWidget().text14W400( + "${state.charactersLeft} ${AppText.charactersLeft}", + clr: AppColor.descriptionText); + } + return const Text('350 characters left'); + }, + ), + ], + ), + ), + Stack( + children: [ + SizedBox( + width: 1.sw, + height: 70.h, + ), + const CheckBoxWidget(), + Positioned( + left: 45, + top: 14, + child: SizedBox( + width: 0.75.sw, + child: TextWidget().text14W500( + AppText.iUnderstandAndAgreeThatmyDataWillBeDeleted, + clr: AppColor.charcoalColor, + txtAlign: TextAlign.start, + ), + ), + ), + BlocBuilder( + builder: (context, state) { + if (state is CheckboxError) { + return Positioned( + left: 45, + top: 55, + child: SizedBox( + width: 0.75.sw, + child: TextWidget().text14W500( + AppText.pleaseCheckThisField, + clr: AppColor.txtErrorColor, + txtAlign: TextAlign.start, + ), + ), + ); + } + return const SizedBox.shrink(); + }, + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/features/deleteAccount/presentation/widgets/top_section.dart b/lib/features/deleteAccount/presentation/widgets/top_section.dart new file mode 100644 index 0000000..e89df2d --- /dev/null +++ b/lib/features/deleteAccount/presentation/widgets/top_section.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +class TopSection extends StatelessWidget { + const TopSection({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Gap(85), + Center( + child: SvgPicture.asset( + AppImages.weclomeLogo, + ), + ), + const Gap(30), + TextWidget().text20W700( + AppText.weAreSadToSeeYouGo, + clr: AppColor.charcoalColor, + ), + ], + ); + } +} diff --git a/lib/features/login/presentation/bloc/login_bloc.dart b/lib/features/login/presentation/bloc/login_bloc.dart index 1ae720f..a753d11 100644 --- a/lib/features/login/presentation/bloc/login_bloc.dart +++ b/lib/features/login/presentation/bloc/login_bloc.dart @@ -21,6 +21,8 @@ class LoginBloc extends Bloc { on(_onLoginFormChanged); on((event, emit) async { if (!formKey.currentState!.validate()) { + emit( + const LoginFailure("Login failed. Please check your credentials.")); return; } emit(LoginLoading()); diff --git a/lib/features/welcome/presentation/widgets/login_signup_button.dart b/lib/features/welcome/presentation/widgets/login_signup_button.dart index 6887eb8..7bddd2b 100644 --- a/lib/features/welcome/presentation/widgets/login_signup_button.dart +++ b/lib/features/welcome/presentation/widgets/login_signup_button.dart @@ -15,7 +15,7 @@ class LoginSignUpButton extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric(vertical: 40.0), + padding: const EdgeInsets.symmetric(vertical: 20.0), child: Column( children: [ Container( diff --git a/lib/shared/components/bloc/checkbox/checkbox_bloc.dart b/lib/shared/components/bloc/checkbox/checkbox_bloc.dart index 90e68d4..1f897e8 100644 --- a/lib/shared/components/bloc/checkbox/checkbox_bloc.dart +++ b/lib/shared/components/bloc/checkbox/checkbox_bloc.dart @@ -1,4 +1,5 @@ import 'package:bloc/bloc.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; import 'checkbox_event.dart'; import 'checkbox_state.dart'; @@ -6,6 +7,7 @@ import 'checkbox_state.dart'; class CheckboxBloc extends Bloc { CheckboxBloc() : super(CheckboxUnchecked()) { on(_onToggleCheckbox); + on(_onValidateCheckbox); } void _onToggleCheckbox(ToggleCheckbox event, Emitter emit) { @@ -15,4 +17,11 @@ class CheckboxBloc extends Bloc { emit(CheckboxUnchecked()); } } + + void _onValidateCheckbox( + ValidateCheckbox event, Emitter emit) { + if (state is! CheckboxChecked) { + emit(const CheckboxError(AppText.pleaseCheckThisField)); + } + } } diff --git a/lib/shared/components/bloc/checkbox/checkbox_event.dart b/lib/shared/components/bloc/checkbox/checkbox_event.dart index 346cbbc..f17b4de 100644 --- a/lib/shared/components/bloc/checkbox/checkbox_event.dart +++ b/lib/shared/components/bloc/checkbox/checkbox_event.dart @@ -8,3 +8,5 @@ abstract class CheckboxEvent extends Equatable { } class ToggleCheckbox extends CheckboxEvent {} + +class ValidateCheckbox extends CheckboxEvent {} diff --git a/lib/shared/components/bloc/checkbox/checkbox_state.dart b/lib/shared/components/bloc/checkbox/checkbox_state.dart index a40b36b..c444957 100644 --- a/lib/shared/components/bloc/checkbox/checkbox_state.dart +++ b/lib/shared/components/bloc/checkbox/checkbox_state.dart @@ -10,3 +10,12 @@ abstract class CheckBoxState extends Equatable { class CheckboxUnchecked extends CheckBoxState {} class CheckboxChecked extends CheckBoxState {} + +class CheckboxError extends CheckBoxState { + final String message; + + const CheckboxError(this.message); + + @override + List get props => [message]; +} diff --git a/lib/shared/components/button_widget.dart b/lib/shared/components/button_widget.dart index 29ee744..c40420a 100644 --- a/lib/shared/components/button_widget.dart +++ b/lib/shared/components/button_widget.dart @@ -1,16 +1,32 @@ import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:tanami_app/core/styles/app_color.dart'; import 'package:tanami_app/shared/components/text_widget.dart'; class ButtonWidget { - //Text Button Widget textBtn({ required Widget text, + Color? clr, required VoidCallback function, }) { - return TextButton( - onPressed: function, - child: text, + return InkWell( + onTap: function, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + side: const BorderSide(width: 1, color: AppColor.txtBorderColor), + borderRadius: BorderRadius.circular(30), + ), + ), + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 10, + ), + width: 1.sw, + height: 56.h, + child: Center(child: text), + ), ); } diff --git a/lib/shared/components/form_label_textfield.dart b/lib/shared/components/form_label_textfield.dart index 0dd53b7..64c42b2 100644 --- a/lib/shared/components/form_label_textfield.dart +++ b/lib/shared/components/form_label_textfield.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:gap/gap.dart'; import 'package:tanami_app/core/styles/app_color.dart'; import 'package:tanami_app/core/styles/app_text.dart'; @@ -17,12 +18,14 @@ class FormLabelTextField extends StatelessWidget { required this.textEditingController, required this.hintText, this.prefixWidget, + this.onChangeFun, }); final String title; final String type; final String hintText; final TextEditingController textEditingController; final Widget? prefixWidget; + final Function(String)? onChangeFun; @override Widget build(BuildContext context) { @@ -32,6 +35,7 @@ class FormLabelTextField extends StatelessWidget { TextWidget().text14W500( title, clr: AppColor.textLabelColor, + txtAlign: type == "description" ? TextAlign.start : TextAlign.center, ), const Gap(10), type == "password" @@ -39,6 +43,7 @@ class FormLabelTextField extends StatelessWidget { controller: textEditingController, ) : textFormField( + onInput: onChangeFun, validator: (value) { if (type == "phone number") { if (value != null && value.isEmpty) { @@ -50,10 +55,19 @@ class FormLabelTextField extends StatelessWidget { return AppText.chooseCountry; } return null; + } else if (type == "description") { + if (textEditingController.text.isEmpty) { + return AppText.pleaseEnteraDescription; + } + return null; } else { return null; } }, + inputFormatters: [ + LengthLimitingTextInputFormatter(350), + ], + maxlines: type == "description" ? 6 : 1, texttype: type == "phone number" ? TextInputType.phone : TextInputType.name, diff --git a/lib/shared/components/text_from_field_widget.dart b/lib/shared/components/text_from_field_widget.dart index d77f243..2045593 100644 --- a/lib/shared/components/text_from_field_widget.dart +++ b/lib/shared/components/text_from_field_widget.dart @@ -24,13 +24,11 @@ Widget textFormField({ return TextFormField( validator: validator, textAlignVertical: TextAlignVertical.center, - // cursorColor: AppColor.txtBorderShadowColor, + cursorColor: AppColor.primaryColor2, initialValue: value, readOnly: readonly!, onTap: onTap, - enabled: enabled, - enableInteractiveSelection: false, maxLines: maxlines, autovalidateMode: AutovalidateMode.onUserInteraction, controller: textEditingController,