From 9c89e6c0d4acb0ce9cf02a2baf9da7e875d912c3 Mon Sep 17 00:00:00 2001 From: jayesh Date: Wed, 29 May 2024 13:21:55 +0530 Subject: [PATCH] login screen, choose country screen --- .../images/country_flag/png/bahrain_flag.png | Bin 0 -> 1091 bytes .../images/country_flag/png/kuwait_flag.png | Bin 0 -> 854 bytes assets/images/country_flag/png/oman_flag.png | Bin 0 -> 959 bytes assets/images/country_flag/png/qatar_flag.png | Bin 0 -> 1341 bytes .../country_flag/png/saudi_arabia_flag.png | Bin 0 -> 1578 bytes assets/images/country_flag/png/uae_flag.png | Bin 0 -> 678 bytes lib/core/routes/route_name.dart | 3 + lib/core/routes/routes.dart | 8 ++ lib/core/styles/app_color.dart | 8 ++ lib/core/styles/app_images.dart | 12 +-- lib/core/styles/app_text.dart | 10 ++ .../utils/constant/country_flag_data.dart | 20 ++++ .../bloc/choose_country_bloc.dart | 21 ++++ .../bloc/choose_country_event.dart | 17 +++ .../bloc/choose_country_state.dart | 19 ++++ .../pages/choose_country_layout.dart | 41 +++++++ .../pages/choose_country_screen.dart | 14 +++ .../widgets/country_selection_list.dart | 57 ++++++++++ .../login/presentation/bloc/login_bloc.dart | 25 +++-- .../login/presentation/bloc/login_event.dart | 11 +- .../login/presentation/bloc/login_state.dart | 3 + .../presentation/pages/login_layout.dart | 8 +- .../presentation/pages/login_screen.dart | 4 +- .../presentation/widgets/bottom_section.dart | 8 +- .../presentation/widgets/login_form.dart | 102 +++++++++++------- lib/main.dart | 25 +++-- lib/shared/components/appbar_widget.dart | 73 +++++++++++++ .../components/form_label_textfield.dart | 12 ++- pubspec.yaml | 1 + 29 files changed, 429 insertions(+), 73 deletions(-) create mode 100644 assets/images/country_flag/png/bahrain_flag.png create mode 100644 assets/images/country_flag/png/kuwait_flag.png create mode 100644 assets/images/country_flag/png/oman_flag.png create mode 100644 assets/images/country_flag/png/qatar_flag.png create mode 100644 assets/images/country_flag/png/saudi_arabia_flag.png create mode 100644 assets/images/country_flag/png/uae_flag.png create mode 100644 lib/core/utils/constant/country_flag_data.dart create mode 100644 lib/features/countrySelection/presentation/bloc/choose_country_bloc.dart create mode 100644 lib/features/countrySelection/presentation/bloc/choose_country_event.dart create mode 100644 lib/features/countrySelection/presentation/bloc/choose_country_state.dart create mode 100644 lib/features/countrySelection/presentation/pages/choose_country_layout.dart create mode 100644 lib/features/countrySelection/presentation/widgets/country_selection_list.dart create mode 100644 lib/shared/components/appbar_widget.dart diff --git a/assets/images/country_flag/png/bahrain_flag.png b/assets/images/country_flag/png/bahrain_flag.png new file mode 100644 index 0000000000000000000000000000000000000000..6b650a19a8f4061207ed6689e22ce0a0428846a8 GIT binary patch literal 1091 zcmV-J1ibr+P)0VqO3**17ApXbwEAPHFnx8mpdNPNHg@sux?8tV)L=z!83~3%i`dF15 z+<*HG6eiALchIooI!F2}DG^o>i!phc;L+>lR$+fN1^MYoly5ERlh`f)-7W7xz5Egl z#LT%@elYXrKY=|n1==X1)ie^dMbLtoOt!!J=7K$Q(JcRQ3sIvJzT`Rws>5egJM14l zmo8oYJ{B<=0nxj6&))P_TLli)08gX(bNS{Xa;L}O%ud6rpL;FEBXH>M&esaM&68Q} zs9YDtsn58w&SLG_0-{3a)6?1Ij!i+;cc&5S*&E0m`w+$HNkoB1G^p38Acqb$b9S%( z1bOl(>~Cif75*;3h)S8?u60nnDi^0O@cMHC<==lp9C(!INbsE6wD6+mE?n_*#{PO5 z$}S>K1Z||Wp#H!hxw2N@e-B?oRVymdGZ?743sPjF>jzlJ!_TSu_l&jF3woAsq%Ay4u15exKI z9v~tzo!Ps6LD?T~=J{x6Hy%6qJofZGgUBE;qNnB$Xt7nCusXAM{0Fo|m35HE94Y1J zj2u7e=Ma@jh#YPpH>FkEi?d%2uYNFmXniT$lKv|U3N+G{T^NGxxmqy|1yw}p{p~O+4FI_%+)KZX;ujOx4LWCB_mRVWmKFYG zLyfS3d+FEGT>9qjDo*I9!NL8NhOnU*LUOaKeE)3i&R7`d(1HT= zE4_qJDZcgmI*&~5&!4J<7yNopDpGkt#yo${B*>WTsFNqn_!oUnB|wHGvHXy`eeCXXq23C9AC)EO+~EjSjGGpJ7N7ZjBd$e4 zVr|5%b)BMfic%ZgH0=L+gR3zeAr(?7jn069n~CSxGJm%dG>5N;6L|jxh+O~%BajM1 z904B2=EqmHM>Vo!OaHwv+z;Fz1|lIy#paZ{&3vPv4`h0ib8iqxCxGN-NJG@q+(ts& zR#TR(STiR!u=^eOdGLwnf#RHzCPsfwTZ(*6;!{jC%+H6De^7!(gp zRaDL1*jo`P(@{>BV~Pg{h(x?EcvVpnBK{e;gkCI}1Tx}R4fuzo5OfXx!4QIU_yI7DV5m`LXZXjUYdF(NuM)%*G;-z2+4Hl{HZvynI+hx-v_a(5ph zRX42N`ReD!eVO(k)VKDuXJ90*NN81?F&RHjr7Y8GCkXGf>pB-3MfqKT`G+5;Z&{{U z|FCIsOIG$;W&$~}4QNDr(?dC7CI gf&#WG(hnE>1>s2Vb{58+VE_OC07*qoM6N<$f}(YDApigX literal 0 HcmV?d00001 diff --git a/assets/images/country_flag/png/oman_flag.png b/assets/images/country_flag/png/oman_flag.png new file mode 100644 index 0000000000000000000000000000000000000000..5bd32a0b9a92a62fa7570e9adbb3d803549aff36 GIT binary patch literal 959 zcmV;w13>(VP)f@EPZ==56WqXVkhA~Vz0OV+~}GxO-4x?3RwylCmvS=>ntb&Bjby2ameaD$o38bbu)(%e#YbVo@ zkDw_b_|}H{MA3ojSn<&#n0lJPIuax6Cs7$kRREhyf?OHLF6Gt;o{wZpf27iLFC^H`60p#r{ztoc7dzn;B_2K2HRd_JUx1W-~6 zZD$!~UcYs%fW&C(p3o>6>S5u3i^^(Ie7J^}HJyibzXQ(1Bz9iEg7MZnm>v0;v)5Hl z**qo*3aT47s8!M{Jr?F$PuJu7)f<>jzClWu7>~C>k2Rs>c!WH2rmVHV=W~=hLRRO) zegpy`>-Jqp+g{?SZQBO@@kcI}^>48Vkq|@VWuA-tUvC4{Kf#A6c ztdS&I`)cNvy@p5FY^2ixq|Zaa(ZO!aRhyfo#>B1X-N53i!(olsETp_Sf4d+kI5uE7 z3?0~L9QtN@be0Hm9|Crq|9JTv221 zO*nx_fkh?f#Jn{BZOFLWyP}Hhnu5fXiW$wHQH&yIJu3E8MJcDN>XI+#mpd`4DWa7e z?=zP3T5*A_gwb9TW0D<0eu8=_>2)t&N$VnLK^T=ys#0-_1132Gt5m1Xv>*%WIK!kt h*Cc#)PqwMa#vfUHKGz%$blLy_002ovPDHLkV1kbPvbX>M literal 0 HcmV?d00001 diff --git a/assets/images/country_flag/png/qatar_flag.png b/assets/images/country_flag/png/qatar_flag.png new file mode 100644 index 0000000000000000000000000000000000000000..c3fc0ada449cd0251f32e79df64d24eecbc1fe6c GIT binary patch literal 1341 zcmV-D1;YA?P)w zfeBY2!VNG7AUIB~EDneYW12z-i<&C@F%kDEZCoBgtB#DZ(eUG)k*!~Tb1VO>A$1gP z@t=aftg-2dsBg9vZ?TyJ5pmPDpAJW6lEFi_BuXgd^JpDxP!cio<43OjsSjPF&Ai_k zp_%b<$7jwjCiBtE_<`4t*lqxYR2G8Ra!~8j1N%8w>biL&?upm4OLhOmnDg^5zq(yk z)!|pqf`|n|$b=)@K#y=fLIMPrEUDjoZT;ldd_EL^+g^tZ4GXk6YtA7Xi6gkQ4#l&E z-8(DD#S%CV=+P9dgdwBoMFSa!k&Mooq9R%(A{9e68s}lhc|nnIN*3Yw8xFUl_PlKH zo2-&yWeo>wekk{R2&_Itc;hKF`U&TJWp9EFc3E=#p4Y2q&LYIqnuhV}aW0ucF_}g# zkw7VxgH~2MOy(4%sqv|Pm;3ecYeM@lysBDcqA~2xuOi@ifWXoQi9CuNEQ;A2)O-P*1lLowMpZTRtlw?X5_az{RNj)_0rIq{Z2rR6jNJKjgjj>xiLjGos0`<`X zMMv(kLbh39>F$OZAZdDp%G9xMG!`;h1Sz9-@2w!T;zu$RhDwFe%+xPS7!c_O3hjH0 zMo|=DyU>g7{(gYEMXn`t>k#u#9Qzdg4wd#}rjdJ0Ii!BbtIuTz-c1ye8FUz9-=Fwq z^*TgPK1+kr$lKQ216yB@K3cl$=qLmzoDAQsEi7X=ZSZxdg`z%}9j4(nL>@e zNp*(jw zf1T6&>d^95o6G5UfL>L$Xun#!ihW})qbPC1H5l~7=ifpijh;&m^z>hZ?NT2)L8dHX z$hf4go5#F16*Jj|(AuVR?~V__)lDRnBYl4BOzP7Y5s6*nX>PRCo$YyTvi5XeJ%03b zn)gDo)k+Opu_Kc_*G#r#8>&Y}zjw0>RBF^@K9O_=mLEcy_u=0^=V=kyW*wqeBxo4> zF5>eva%*u{ZQxA{4H?+0iDi{M4?rEj1a8U21}6H_0%oU+P>8GX*9 z&w5UZ;bm6&q$f~6!vrBgIpCr7m0x|2PkR3YH^)?$I-}4U00000NkvXXu0mjf1$}aW literal 0 HcmV?d00001 diff --git a/assets/images/country_flag/png/saudi_arabia_flag.png b/assets/images/country_flag/png/saudi_arabia_flag.png new file mode 100644 index 0000000000000000000000000000000000000000..654d7acab37919f17354d8da3e51098d0cb4ee71 GIT binary patch literal 1578 zcmV+_2G#kAP)PT%LexAVS7 zfvKX}WP}RU@S_|8D(N?wMw3UsO7xhapvR9!cu|X5(TpiTG3lAxP4APC_voz>Q-cT} zzQu3i;mJ&%n9w#;CCxfSWQ=$o__@~hPeknLgsM%+WSYJ7_6epJ-;gQylPM)kn$QmN z6EAA$&4if%)0T{?X#S;$N=8QgbDQWHNre+)W>VwojFWi+kt|UaYCa+CGxM3*Bg0@C z_0ust(NG|)58=h23Ma${_M!?I9AmmLrwAjy5fo?UqPxEjrh*a-4mlyYoanY$k*m+d zi0nmDoDTWvS-5h$6%`9ixZ2r1+-%f63R`@Kt-tuHm3s`bqrR`UyAihS3#vzA~z)ympWRo zdD%ML>bZ+pwHgl{64DZqQMF_R4%Pn-gFX$`fd?o^H=yR?X~b)^XzMX!4gKe!+l5w3 zC;A5^B*!NN1S&}OR^l*5$dBDYGHg=U!yd%NXmF3%du17^2}!8Cei0doDOg)lj>UP! za1Rebt%{*BJtueH_5iKjx3PQkTaZXYj3m=&Nmc=@Qa>l)eB|PqDay=2_dp-wNrPh$ z_$dG-4|5+51jiqZc(7~3HgxoMRT@1vp3$wZL5W}7el6V!kC_o?38rA(U%v#1drDXyW4?cLmsZ(y%iK~ zM2X>uKw(#n2rv?{wELYu?1P%jWHOf0pNwG7wq2Y|UTP+aGjh>qw-M0_WRb98`o6kk zDZYC1WBgQCi!Cc(LDz#G@*A0(`ogX2Z_y)Foj zCC0@gi=58m^*~3qXS*!S$U(EY4JVtf>Y=>y+>Og`fo;IlBRw$!D@sN`@o7+P*VP!s45K7WLJj|o3 z>CR0a9W0WVTAA&ckDO_3LM91WId3r(lr-ANvA3>Vk0h-QU%$N@e|>Qff645IQ3JeCT&dTuUw89r&@XhO7M^MNwXw0q5G9AqccA zuMo%o`-h^?i^kR~Se#Q#VV=dEHHQdZXunRrSAt%Oumsvb&E|vt%Tc(f zQWyu;yCJ+{?2J~vvICHvZxc(8PVmDGTXDYa8b23bTfPoo9{rwY{fLQX{3;fP6JiY^ zsM~lE@Zp1C1F0MvY_*!P(4Z%Zt1+AK^EP|z_zLejWbDIirh!0jwl8!joDl1eRM^6A zFJ^~KRb)#ojGM^PfRZN8EGhA6t+X=g^cJhc>RTeEopzsMJ|mM8*NWC?pC`VK+0NUO zz0-0XqaY%q4dK%;wF@gBUCk)y=4@(cq;-d<@4_k)fvXzUsZ?^UDR@mDdZlNl@dg?f cpXEyLF(e#UFL)<2ZvX%Q07*qoM6N<$g47b@{{R30 literal 0 HcmV?d00001 diff --git a/assets/images/country_flag/png/uae_flag.png b/assets/images/country_flag/png/uae_flag.png new file mode 100644 index 0000000000000000000000000000000000000000..ebc9a383d0654e1f27d04316d675acd66dab16b7 GIT binary patch literal 678 zcmV;X0$KfuP)15Ab-OSU2qYGm13=s*tPCdzCulhU5Os-)nyx{Vhyx^?fZU+r1hv!y#7NDi zo2HvAYAMrsnQ4-`Y2#_fi9hMJ6K5QMng1^gh6up_{Qi0Mcks9ZhEQJebP#V@WrTZgo}&KtS=L)4h+zXik6KwO^LZ7dNGPK#i%pV&IRO}N zV!P7C_N%RVmH90qo{W82JdokzzA*V2BeB=}s0AKOgoF|xks55nSPfhpN+};%gr;Ny z6tH^;WjiC_5p+Z70P#i)M@=+W-67oNeEbIWtL;AQ=o`8`pK)qcljUQ(~C)#_z6+AM5L|iQ_m?uh-kS z-0lCE2n}TGoY-%n#+6r;d&v-4qCTo_HO8ln>4otlmdD;@VJyVd^lxDC&@)>z-_hCX z;R3siy6WaMh^cMl1Jo}Vc@xWcht$?}ZF6OliZSvdy6G7+cY7_hfqceP1NZsDlp>{< zg!i96^0!ugd#C6%`g8%>k M07*qoM6N<$g1BQY2mk;8 literal 0 HcmV?d00001 diff --git a/lib/core/routes/route_name.dart b/lib/core/routes/route_name.dart index ed31c5b..b7f6820 100644 --- a/lib/core/routes/route_name.dart +++ b/lib/core/routes/route_name.dart @@ -14,4 +14,7 @@ class RouteName { //Welcome static const String welcomeScreen = 'welcome'; + + //choose country + static const String chooseCountryScreen = 'chooseCountryScreen'; } diff --git a/lib/core/routes/routes.dart b/lib/core/routes/routes.dart index eca3876..f3e3ff6 100644 --- a/lib/core/routes/routes.dart +++ b/lib/core/routes/routes.dart @@ -2,6 +2,7 @@ import 'package:go_router/go_router.dart'; import 'package:tanami_app/core/routes/route_name.dart'; +import 'package:tanami_app/features/countrySelection/presentation/pages/choose_country_screen.dart'; import 'package:tanami_app/features/welcome/presentation/pages/weclome_screen.dart'; import '../../features/login/presentation/pages/login_screen.dart'; @@ -43,6 +44,13 @@ final goRouter = GoRouter( return const WelcomeScreen(); }, ), + GoRoute( + name: RouteName.chooseCountryScreen, + path: RouteName.chooseCountryScreen, + builder: (context, state) { + return const ChooseCountryScreen(); + }, + ), ]), // GoRoute( diff --git a/lib/core/styles/app_color.dart b/lib/core/styles/app_color.dart index b9b6a59..7fc536b 100644 --- a/lib/core/styles/app_color.dart +++ b/lib/core/styles/app_color.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; class AppColor { //Primary Color static const Color primaryColor = Color(0xFF002F0F); + static const Color primaryColor2 = Color(0xFF004717); //Welcome Color static const Color indicatorActiveColor = Color(0xFF002F0F); @@ -11,6 +12,7 @@ class AppColor { //Common Color static const Color plainWhite = Color(0xFFFFFFFF); static const Color darkGreyColor = Color(0xFF343434); + static const Color plainBlack = Color(0xFF000000); //Auth Color static const Color charcoalColor = Color(0xFF272727); @@ -28,4 +30,10 @@ class AppColor { //Button Color static const Color inactiveBtnColor = Color(0xFFD8D8D8); static const Color inactiveBtnTxtColor = Color(0xFF8D8D8D); + + //AppBar Color + static const Color appBarIconColor = Color(0xFF363636); + + //Radio Color + static const Color radioActiveColor = Color(0xFF0B8933); } diff --git a/lib/core/styles/app_images.dart b/lib/core/styles/app_images.dart index f1e8ea6..893c708 100644 --- a/lib/core/styles/app_images.dart +++ b/lib/core/styles/app_images.dart @@ -25,14 +25,14 @@ class AppImages { //Country Flag static const String bahrainFlag = - "assets/images/country_flag/svg/bahrain_flag.svg"; + "assets/images/country_flag/png/bahrain_flag.png"; static const String kuwaitFlag = - "assets/images/auth_screen/svg/kuwait_flag.svg"; - static const String omanFlag = "assets/images/auth_screen/svg/oman_flag.svg"; + "assets/images/country_flag/png/kuwait_flag.png"; + static const String omanFlag = "assets/images/country_flag/png/oman_flag.png"; static const String qatarFlag = - "assets/images/country_flag/svg/qatar_flag.svg"; + "assets/images/country_flag/png/qatar_flag.png"; static const String saudiArabiaflag = - "assets/images/auth_screen/svg/saudi_arabia_flag.svg"; + "assets/images/country_flag/png/saudi_arabia_flag.png"; static const String unitedArabEmiratesFlag = - "assets/images/auth_screen/svg/united_arab_emirates_flag.svg"; + "assets/images/country_flag/png/uae_flag.png"; } diff --git a/lib/core/styles/app_text.dart b/lib/core/styles/app_text.dart index c9387f3..d13575e 100644 --- a/lib/core/styles/app_text.dart +++ b/lib/core/styles/app_text.dart @@ -28,4 +28,14 @@ class AppText { static const String enterPhoneNo = "Enter phone number"; static const String invalidPassword = "Invalid Password"; static const String forgorPassword = "Forgot Password"; + + //Country Name + static const String bahrainCountryText = "Bahrain"; + static const String kuwaitCountryText = "Kuwait"; + static const String omanCountryText = "Oman"; + static const String qatarCountryText = "Qatar"; + static const String saudiArabiaCountryText = "Saudi Arabia"; + static const String uaeCountryText = "United Arab Emirates"; + + static const String confirmSelectionText = "Confirm selection"; } diff --git a/lib/core/utils/constant/country_flag_data.dart b/lib/core/utils/constant/country_flag_data.dart new file mode 100644 index 0000000..5ecfc7a --- /dev/null +++ b/lib/core/utils/constant/country_flag_data.dart @@ -0,0 +1,20 @@ +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; + +List countryName = [ + AppText.bahrainCountryText, + AppText.kuwaitCountryText, + AppText.omanCountryText, + AppText.qatarCountryText, + AppText.saudiArabiaCountryText, + AppText.uaeCountryText, +]; + +List countryFlag = [ + AppImages.bahrainFlag, + AppImages.kuwaitFlag, + AppImages.omanFlag, + AppImages.qatarFlag, + AppImages.saudiArabiaflag, + AppImages.unitedArabEmiratesFlag, +]; diff --git a/lib/features/countrySelection/presentation/bloc/choose_country_bloc.dart b/lib/features/countrySelection/presentation/bloc/choose_country_bloc.dart new file mode 100644 index 0000000..8712902 --- /dev/null +++ b/lib/features/countrySelection/presentation/bloc/choose_country_bloc.dart @@ -0,0 +1,21 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'choose_country_event.dart'; +import 'choose_country_state.dart'; + +class RadioBloc extends Bloc { + RadioBloc() : super(RadioInitial()) { + on(_onRadioSelected); + } + + void _onRadioSelected(RadioSelected event, Emitter emit) { + emit(RadioSelectionChanged(event.selectedIndex)); + } + + int get selectedCountry { + if (state is RadioSelectionChanged) { + return (state as RadioSelectionChanged).selectedIndex; + } + return 0; + } +} diff --git a/lib/features/countrySelection/presentation/bloc/choose_country_event.dart b/lib/features/countrySelection/presentation/bloc/choose_country_event.dart new file mode 100644 index 0000000..5cf9a32 --- /dev/null +++ b/lib/features/countrySelection/presentation/bloc/choose_country_event.dart @@ -0,0 +1,17 @@ +import 'package:equatable/equatable.dart'; + +abstract class RadioEvent extends Equatable { + const RadioEvent(); + + @override + List get props => []; +} + +class RadioSelected extends RadioEvent { + final int selectedIndex; + + const RadioSelected(this.selectedIndex); + + @override + List get props => [selectedIndex]; +} diff --git a/lib/features/countrySelection/presentation/bloc/choose_country_state.dart b/lib/features/countrySelection/presentation/bloc/choose_country_state.dart new file mode 100644 index 0000000..9a56165 --- /dev/null +++ b/lib/features/countrySelection/presentation/bloc/choose_country_state.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; + +abstract class RadioState extends Equatable { + const RadioState(); + + @override + List get props => []; +} + +class RadioInitial extends RadioState {} + +class RadioSelectionChanged extends RadioState { + final int selectedIndex; + + const RadioSelectionChanged(this.selectedIndex); + + @override + List get props => [selectedIndex]; +} diff --git a/lib/features/countrySelection/presentation/pages/choose_country_layout.dart b/lib/features/countrySelection/presentation/pages/choose_country_layout.dart new file mode 100644 index 0000000..7c4ab28 --- /dev/null +++ b/lib/features/countrySelection/presentation/pages/choose_country_layout.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:tanami_app/core/routes/routes.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/shared/components/appbar_widget.dart'; + +import '../../../../core/styles/app_color.dart'; +import '../../../../shared/components/button_widget.dart'; +import '../widgets/country_selection_list.dart'; + +class ChooseCountryLayout extends StatelessWidget { + const ChooseCountryLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + bottomNavigationBar: Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 35, + ), + width: 1.sw, + height: 56.h, + child: ButtonWidget().elevatedBtn( + txtClr: AppColor.plainWhite, + function: () { + // radioBloc.add(const BackPressed(true)); + goRouter.pop(); + }, + text: AppText.confirmSelectionText, + clr: AppColor.primaryColor2, + ), + ), + appBar: const AppBarWidget( + height: 75, + titleTxt: AppText.chooseCountry, + ), + body: const CountrySelectionList(), + ); + } +} diff --git a/lib/features/countrySelection/presentation/pages/choose_country_screen.dart b/lib/features/countrySelection/presentation/pages/choose_country_screen.dart index e69de29..71bfdf8 100644 --- a/lib/features/countrySelection/presentation/pages/choose_country_screen.dart +++ b/lib/features/countrySelection/presentation/pages/choose_country_screen.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'choose_country_layout.dart'; + +class ChooseCountryScreen extends StatelessWidget { + const ChooseCountryScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + resizeToAvoidBottomInset: true, + body: ChooseCountryLayout(), + ); + } +} diff --git a/lib/features/countrySelection/presentation/widgets/country_selection_list.dart b/lib/features/countrySelection/presentation/widgets/country_selection_list.dart new file mode 100644 index 0000000..0466980 --- /dev/null +++ b/lib/features/countrySelection/presentation/widgets/country_selection_list.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/utils/constant/country_flag_data.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +import '../bloc/choose_country_bloc.dart'; +import '../bloc/choose_country_event.dart'; +import '../bloc/choose_country_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 RadioSelectionChanged) { + selectedIndex = state.selectedIndex; + } + + return Column( + children: List.generate(countryFlag.length, (int index) { + return ListTile( + title: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + countryFlag[index], + width: 24, + height: 24, + ), + const Gap(10), + TextWidget().tex14W500(countryName[index], + clr: AppColor.charcoalColor), + ], + ), + leading: Radio( + activeColor: AppColor.radioActiveColor, + value: index, + groupValue: selectedIndex, + onChanged: (int? value) { + if (value != null) { + radioBloc.add(RadioSelected(value)); + } + }, + ), + ); + }), + ); + }, + ); + } +} diff --git a/lib/features/login/presentation/bloc/login_bloc.dart b/lib/features/login/presentation/bloc/login_bloc.dart index e6ade5b..5a3a9e0 100644 --- a/lib/features/login/presentation/bloc/login_bloc.dart +++ b/lib/features/login/presentation/bloc/login_bloc.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; import 'login_event.dart'; @@ -20,6 +18,7 @@ class LoginBloc extends Bloc { phoneNumberTextField.addListener(_onFormFieldChanged); passwordTextField.addListener(_onFormFieldChanged); countrySelectionTextField.addListener(_onFormFieldChanged); + on(_onLoginFormChanged); on((event, emit) async { if (!formKey.currentState!.validate()) { return; @@ -43,19 +42,18 @@ class LoginBloc extends Bloc { }); } void _onFormFieldChanged() { - add(LoginFormChanged()); + add(LoginFormChanged( + phoneNumberTextField.text, + passwordTextField.text, + countrySelectionTextField.text, + )); } - bool areFieldsFilled() { - return phoneNumberTextField.text.isNotEmpty && - passwordTextField.text.isNotEmpty && - countrySelectionTextField.text.isNotEmpty; - } - - Stream mapEventToState(LoginEvent event) async* { - if (event is LoginFormChanged) { - yield LoginFieldsState(areFieldsFilled()); - } + void _onLoginFormChanged(LoginFormChanged event, Emitter emit) { + final areFieldsFilled = event.phoneNumber.isNotEmpty && + event.password.isNotEmpty && + event.country.isNotEmpty; + emit(LoginFieldsState(areFieldsFilled)); } // Mock API function, replace with actual API call @@ -71,6 +69,7 @@ class LoginBloc extends Bloc { Future close() { phoneNumberTextField.dispose(); passwordTextField.dispose(); + countrySelectionTextField.dispose(); return super.close(); } } diff --git a/lib/features/login/presentation/bloc/login_event.dart b/lib/features/login/presentation/bloc/login_event.dart index aa0ac58..232ec43 100644 --- a/lib/features/login/presentation/bloc/login_event.dart +++ b/lib/features/login/presentation/bloc/login_event.dart @@ -22,4 +22,13 @@ class LoginSubmitted extends LoginEvent { List get props => [phoneNumber, password, countryResidence]; } -class LoginFormChanged extends LoginEvent {} +class LoginFormChanged extends LoginEvent { + final String phoneNumber; + final String password; + final String country; + + const LoginFormChanged(this.phoneNumber, this.password, this.country); + + @override + List get props => [phoneNumber, password, country]; +} diff --git a/lib/features/login/presentation/bloc/login_state.dart b/lib/features/login/presentation/bloc/login_state.dart index 5dcb209..5429d7e 100644 --- a/lib/features/login/presentation/bloc/login_state.dart +++ b/lib/features/login/presentation/bloc/login_state.dart @@ -26,4 +26,7 @@ class LoginFieldsState extends LoginState { final bool areFieldsFilled; const LoginFieldsState(this.areFieldsFilled); + + @override + List get props => [areFieldsFilled]; } diff --git a/lib/features/login/presentation/pages/login_layout.dart b/lib/features/login/presentation/pages/login_layout.dart index 91c442f..ea53361 100644 --- a/lib/features/login/presentation/pages/login_layout.dart +++ b/lib/features/login/presentation/pages/login_layout.dart @@ -5,17 +5,17 @@ import '../widgets/bottom_section.dart'; import '../widgets/top_section.dart'; class LoginLayout extends StatelessWidget { - LoginLayout({super.key}); + const LoginLayout({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView( // - children: [ - const TopSection(), + children: const [ + TopSection(), LoginForm(), - const BottomSection(), + BottomSection(), ], )); } diff --git a/lib/features/login/presentation/pages/login_screen.dart b/lib/features/login/presentation/pages/login_screen.dart index f1d6b37..1d23678 100644 --- a/lib/features/login/presentation/pages/login_screen.dart +++ b/lib/features/login/presentation/pages/login_screen.dart @@ -21,9 +21,9 @@ class LoginScreen extends StatelessWidget { ), BlocProvider( create: (context) => PasswordVisibilityBloc(), - ) + ), ], - child: LoginLayout(), + child: const LoginLayout(), ), ); } diff --git a/lib/features/login/presentation/widgets/bottom_section.dart b/lib/features/login/presentation/widgets/bottom_section.dart index 891d449..9cd4dc0 100644 --- a/lib/features/login/presentation/widgets/bottom_section.dart +++ b/lib/features/login/presentation/widgets/bottom_section.dart @@ -53,6 +53,8 @@ class BottomSection extends StatelessWidget { bool isButtonEnabled = false; if (state is LoginFieldsState) { isButtonEnabled = state.areFieldsFilled; + } else if (state is LoginSuccess || state is LoginFailure) { + isButtonEnabled = true; } return Container( margin: const EdgeInsets.symmetric( @@ -61,7 +63,9 @@ class BottomSection extends StatelessWidget { width: 1.sw, height: 56.h, child: ButtonWidget().elevatedBtn( - txtClr: AppColor.inactiveBtnTxtColor, + txtClr: isButtonEnabled + ? AppColor.plainWhite + : AppColor.inactiveBtnTxtColor, function: () { isButtonEnabled ? context.read().add( @@ -80,7 +84,7 @@ class BottomSection extends StatelessWidget { }, text: AppText.loginText, clr: isButtonEnabled - ? AppColor.primaryColor + ? AppColor.primaryColor2 : AppColor.inactiveBtnColor, ), ); diff --git a/lib/features/login/presentation/widgets/login_form.dart b/lib/features/login/presentation/widgets/login_form.dart index 1d1f687..1957354 100644 --- a/lib/features/login/presentation/widgets/login_form.dart +++ b/lib/features/login/presentation/widgets/login_form.dart @@ -2,52 +2,82 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/core/utils/constant/country_flag_data.dart'; import '../../../../shared/components/form_label_textfield.dart'; +import '../../../countrySelection/presentation/bloc/choose_country_bloc.dart'; +import '../../../countrySelection/presentation/bloc/choose_country_state.dart'; import '../bloc/login_bloc.dart'; class LoginForm extends StatelessWidget { - LoginForm({super.key}); + const LoginForm({super.key}); @override Widget build(BuildContext context) { final loginBloc = context.read(); - return Form( - key: loginBloc.formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 14, - ), - child: Align( - alignment: Alignment.topLeft, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Gap(50), - FormLabelTextField( - hintText: AppText.chooseCountry, - title: AppText.countryOfResidence, - type: "country selection", - textEditingController: loginBloc.countrySelectionTextField, - ), - const Gap(20), - FormLabelTextField( - hintText: "+0 (000) 000 00 00", - title: AppText.phoneNumber, - type: "phone number", - textEditingController: loginBloc.phoneNumberTextField, - ), - const Gap(20), - FormLabelTextField( - hintText: AppText.enterPassword, - title: AppText.password, - type: "password", - textEditingController: loginBloc.passwordTextField, - ), - ], + + return BlocConsumer(listener: (context, state) { + int selectedCountry = -1; + if (state is RadioSelectionChanged) { + selectedCountry = state.selectedIndex; + loginBloc.countrySelectionTextField.text = countryName[selectedCountry]; + } + }, builder: (context, state) { + int selectedCountry = -1; + if (state is RadioSelectionChanged) { + selectedCountry = state.selectedIndex; + } + return Form( + key: loginBloc.formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 14, + ), + child: Align( + alignment: Alignment.topLeft, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(50), + FormLabelTextField( + prefixWidget: selectedCountry == -1 + ? null + : Image.asset( + countryFlag[selectedCountry], + width: 20, + height: 20, + ), + hintText: AppText.chooseCountry, + title: AppText.countryOfResidence, + type: "country selection", + textEditingController: loginBloc.countrySelectionTextField, + ), + const Gap(20), + FormLabelTextField( + prefixWidget: selectedCountry == -1 + ? null + : Image.asset( + countryFlag[selectedCountry], + width: 20, + height: 20, + ), + hintText: "+0 (000) 000 00 00", + title: AppText.phoneNumber, + type: "phone number", + textEditingController: loginBloc.phoneNumberTextField, + ), + const Gap(20), + FormLabelTextField( + hintText: AppText.enterPassword, + title: AppText.password, + type: "password", + textEditingController: loginBloc.passwordTextField, + ), + ], + ), ), ), - ), - ); + ); + }); } } diff --git a/lib/main.dart b/lib/main.dart index a759a60..dda583a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'core/routes/routes.dart'; import 'core/utils/connectivity/network_connectivity.dart'; +import 'features/countrySelection/presentation/bloc/choose_country_bloc.dart'; /* CREATED BY - JAYESH JAIN DATE - 24-05-2024 @@ -47,16 +49,23 @@ class _MyAppState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { - return ScreenUtilInit( - builder: (BuildContext context, Widget? child) => MaterialApp.router( - title: 'Tanami Capital', - theme: ThemeData( - useMaterial3: true, + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => RadioBloc(), + ) + ], + child: ScreenUtilInit( + builder: (BuildContext context, Widget? child) => MaterialApp.router( + title: 'Tanami Capital', + theme: ThemeData( + useMaterial3: true, + ), + debugShowCheckedModeBanner: false, + routerConfig: goRouter, ), - debugShowCheckedModeBanner: false, - routerConfig: goRouter, + designSize: const Size(390, 844), ), - designSize: const Size(390, 844), ); } } diff --git a/lib/shared/components/appbar_widget.dart b/lib/shared/components/appbar_widget.dart new file mode 100644 index 0000000..bee8ea5 --- /dev/null +++ b/lib/shared/components/appbar_widget.dart @@ -0,0 +1,73 @@ +// ignore_for_file: non_constant_identifier_names, file_names, prefer_const_constructors + +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:tanami_app/core/routes/routes.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +class AppBarWidget extends StatelessWidget implements PreferredSizeWidget { + @override + Size get preferredSize => Size.fromHeight(height!); + const AppBarWidget( + {super.key, + required this.titleTxt, + this.suffixIcon, + this.showLeading = true, + this.customBack, + this.backPageName = '', + this.customActionWidget, + this.onCustomActionPressed, + this.height = 105}); + + final String titleTxt; + final String? suffixIcon; + final bool? showLeading; + final bool? customBack; + final String? backPageName; + final Widget? customActionWidget; + final VoidCallback? onCustomActionPressed; + final double? height; + @override + Widget build(BuildContext context) { + return PreferredSize( + preferredSize: Size.fromHeight(height ?? 130), + child: AppBar( + scrolledUnderElevation: 0.0, + elevation: 0, + centerTitle: true, + title: TextWidget().tex20W700(titleTxt, clr: AppColor.charcoalColor), + leading: Padding( + padding: EdgeInsets.only( + left: 16.w, + ), + child: GestureDetector( + onTap: () { + customBack ?? false + ? goRouter.goNamed(backPageName!) + : goRouter.pop(); + }, + child: Padding( + padding: EdgeInsets.only(left: 8.w), + child: Icon( + Icons.arrow_back_rounded, + color: AppColor.appBarIconColor, + size: 25.r, + ), + ), + ), + ), + actions: [ + if (customActionWidget != null) + InkWell( + onTap: onCustomActionPressed, + child: Padding( + padding: EdgeInsets.only(right: 14.w), + child: customActionWidget, + ), + ), + ], + ), + ); + } +} diff --git a/lib/shared/components/form_label_textfield.dart b/lib/shared/components/form_label_textfield.dart index 892e471..9beadd5 100644 --- a/lib/shared/components/form_label_textfield.dart +++ b/lib/shared/components/form_label_textfield.dart @@ -5,6 +5,8 @@ import 'package:tanami_app/core/styles/app_text.dart'; import 'package:tanami_app/shared/components/password_text_form_field.dart'; import 'package:tanami_app/shared/components/text_widget.dart'; +import '../../core/routes/route_name.dart'; +import '../../core/routes/routes.dart'; import 'text_from_field_widget.dart'; class FormLabelTextField extends StatelessWidget { @@ -14,11 +16,13 @@ class FormLabelTextField extends StatelessWidget { required this.type, required this.textEditingController, required this.hintText, + this.prefixWidget, }); final String title; final String type; final String hintText; final TextEditingController textEditingController; + final Widget? prefixWidget; @override Widget build(BuildContext context) { @@ -56,12 +60,18 @@ class FormLabelTextField extends StatelessWidget { textEditingController: textEditingController, readonly: type == "country selection" ? true : false, hintText: hintText, + leadingIcon: prefixWidget, suffixIcon: type == "country selection" ? const Icon( Icons.arrow_forward_ios_rounded, color: Color(0xFFB4B4B4), ) - : const SizedBox()) + : const SizedBox(), + onTap: () { + if (type == "country selection") { + goRouter.pushNamed(RouteName.chooseCountryScreen); + } + }) ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index 6f6bfee..335c34f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,3 +93,4 @@ flutter: - assets/images/auth_screen/png/ - assets/images/country_flag/ - assets/images/country_flag/svg/ + - assets/images/country_flag/png/