From f6aaf121ca3a11c84ac201f8e55f202fe6dbe6c1 Mon Sep 17 00:00:00 2001 From: "dinesh.patil" Date: Tue, 14 Oct 2025 19:02:13 +0530 Subject: [PATCH] Added bloc navigation for bottom navbar and added bottom navbar to the home page --- assets/icons/explore.png | Bin 0 -> 2209 bytes assets/icons/magic.png | Bin 0 -> 1291 bytes assets/icons/pass_icon.png | Bin 0 -> 2021 bytes assets/icons/postcard_icon.png | Bin 0 -> 1287 bytes lib/common_bloc/bottom_navigation_bloc.dart | 21 ++ lib/common_packages/custom_bottom_navbar.dart | 111 +++++++ lib/core/app_router.dart | 7 +- lib/home/views/first_time_user_home_page.dart | 276 +++++++++++++++++ lib/home/views/home_page_view.dart | 293 ++---------------- 9 files changed, 431 insertions(+), 277 deletions(-) create mode 100644 assets/icons/explore.png create mode 100644 assets/icons/magic.png create mode 100644 assets/icons/pass_icon.png create mode 100644 assets/icons/postcard_icon.png create mode 100644 lib/common_bloc/bottom_navigation_bloc.dart create mode 100644 lib/common_packages/custom_bottom_navbar.dart create mode 100644 lib/home/views/first_time_user_home_page.dart diff --git a/assets/icons/explore.png b/assets/icons/explore.png new file mode 100644 index 0000000000000000000000000000000000000000..9c77fbc411aac3de5386b361df8f89cf2fbb5a50 GIT binary patch literal 2209 zcmV;S2wwMzP)@~0drDELIAGL9O(c600d`2O+f$vv5yP>kAFH6*M{)wm2|`Y=?6X+| z0lKzohCx22XQpQc%)WnAx|!*Ir@HH{>KYLigv(MXp8+TU;VS?63<2{9lq3E*-j6?P z@n>z4pM;+QsJW!5xn8TaU`{}oBd$xO0#9UD0qjgqHU^qJ*e0Pq+o;tVkRnni;igc? z^_L#+BT_kqiTi>F-C~QXWYpkO8Wxfo38Tpa9uQIv762iG2&!47&ytXl3JD*JTl+w& zvZ%>V!?TQ6SAU#A3?w4qhBv!D;VECH06zhQj11~^TdRmoLhPqc&;~JMmy7(|prt zw4;94^DzADj=0sR-(#{X8&?-cQMy`_@UggkN&w|3ec9KavzItY%cVJlXe=Y*UX=bk zx$3L>#oGw|ECx~Bx`|?UUdn5;%_V(pZT`1~oS7m+L`2B9jos0z~EUlYg7G zm@#3vR8GT|Nti__h=4ygw+|x_aPn{W``V%i5XilL=`OGFBu^l4Y*1z^es5|IwFL13 zto%ZDT6(u(u^Z0=*QM z(||7REb6ss9TB)32BdEOF9ZlSYqcr|SV7((=Zd>Y7)iL=*yaET@}_o;aP%_~;6aFx znb*8CfiuAVJrUrAGx+uCy&GOB?c_tO>G$jM|Mw;;K95Zp!`lmVHTI`E1@6Rui(Fl_qa&zDjgN0zb1C{$CFoj6ZG)yEzEZ z0}81EA>d6qXj$YgEp@|sSO7;a3O&W^@Zu2G8ZQ z-{4*el#E4`a(XDh=0>9Iv%o>CsNVB;2=va?S8Rin2 zV^kWgZzk){!4MFg1CzkSTfVji4RJcuF2VJ0GuFihJ-MIBSjE# z#nbpb*ypeN5i!&Q>u(aCOv@O93O1yq(A#T#L+K!E7@h1i{tjRyC}a*h$f$ zUT9gbxWTZ4U$&65&Dv$z)E=1x=cdsMysH^rM)P`uZJ3dMAFcKw#R6{+Q7mHY;?*?Y zUULh&)jmcn{0OrE57s2Og9)!Ap1KK(6Z#-W{5FI*>s_*1TD`P=yUwQ8G8PGfYY|Iw z-XH7bFOsUVw!Hjlc>U)Y4{Vjw{rOn07qJ)EnD^^H9-Nmi2yd)j%b6>vA<3JqMZE|% z4yi8Z1$g*iWW;#-<4A=Bfuk-#m5FF?VPweU6`XwQ+#M&0I#VOT!`O@AbF&QZ?Yg9s zzrWrbF?xGVN(0T6agl&d_zTt^CfciH(%>XUl00000NkvXXu0mjfY>)mt literal 0 HcmV?d00001 diff --git a/assets/icons/magic.png b/assets/icons/magic.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0777ddda357d67f1121906bdd0f671d158cb23 GIT binary patch literal 1291 zcmV+m1@!ufP)@~0drDELIAGL9O(c600d`2O+f$vv5yPudys^U;Oj4Z(5n?iYuwELz0T3mN3uj7Wjwld%UxOh(PNC(xMZ(SukojM_x4WXuIH2jDOpRfdW$R~h}P#R z0|Kt!xN)Cz+JOYg<(=yT|Nf($9XBqGv4q3z?KNWLlEYF}u-EB&rq`kjMr<{083BO+ z#&|P)Udd=CiD0@+-J3U?VjwnIjct&mQ6^GS-{JI9e*O9?DQtV&+b!~?xAkOyS67Dg z{T6ZFo-SkP`)_{j_1=<(I00c)p6|};&hB*6h>ZkTa~nTxsQBDr4x3aCAk2iIJF-^u z{=a=Ywoop_2Cj|!1;S+z<6cx5oUwLN8KZm*1dQ{HjEKZY2v8l>s$5y|NJvi#`H|Q7 zgU;i}eO7X>tHxe0qpWVifXBwb#L*pJry5Rlua4-R?alVskcKMli=` zl6&F#LlzJrstG*v_V=GAT{o`)Ro_j{oqN$~=K#YR)$6M5a}{cGJyl{!s!53F%yCY1 zBY{l;(iiV?X$~~5sM~04sKYg^F-BADnaZ-)82;3u(GVEqYg544rw_ZiNeF{s=ZAs+ zS6GC}glQ(91qeVVooa|Q1>-%F@+5qY%fU&SJQgKjR5!8(uT8=o)8Ivp0%-({s!EnP zt^&=`tP?6G^)qo)$5WfKh&A8BC~>I>jva0|G|t5nmpwS?lkSpBo4O)+Hn{ zj{qH}0>Gt7D2f1JwNHrSvIR_U@GM+H5h7r`!Lu5?U#NtlGy!bzkh9Vb-Y-l-Q3M1x zfCc-$^j}EFc-aDU8$78tWV29&2sq#15pA`>v#tb87;o@2twutThCvXw!DFpYD4GBe zx4~mYLJ&XU_`{3eMll>jig$B6y8sHZ*WrRV~H-G?+v zAOK7x;4e18Ge&t(76ITwB%IZqe^g(o{lAfpG6?{)LnNL?9g08ISIP7z&9Vsq(UYZ& zBFZQL#7~=3iYTi9ki3906j5dYK);mMh$y=N@C}&MLf}gBJO=;(002ovPDHLkV1m6N BNj(4n literal 0 HcmV?d00001 diff --git a/assets/icons/pass_icon.png b/assets/icons/pass_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f604255ed3fe8c9ef4cbbf5123fed10ac8314e34 GIT binary patch literal 2021 zcmV@~0drDELIAGL9O(c600d`2O+f$vv5yPRuvL6|2{VoEj-2QLCP?F+U}UG-_{2IG6UGpL0>}DebkuEm8|22!pXX zMXf}XYlCu*BjpgOG8OpuO=lMGdiU+x*v8l>3V2C zd4LKs6UohkQ_lkpAT@b&6rmiYlg>e#Zm=mJJ|0gdR?KUem|j4Dg(mjS5JCG&m6qbW zyKnuzP?v<~l2*}B2kb1X|KXc0ulnjaZ8Nk~r4Lgho+_MeWNg?3)xnECFEwB21=prQY$`vJgx6EFwH4R7~zq@UZ|CxKIDmpo-~Q z^Q#?5#i!=D-Z50o@D<8De`rc7X`c5lbf4Wl`P+ zp27~$QhF;nK0fRs0Z`592SA}7`oJ<9`Go&5X;R8b%VNV;1*d!<{kLWub=kq<|2h5d$LUlMJ zq)_|%n7umCQWD_>)B>-i9rp#+wG{9z2Hj)k(ukg~W}3am{=UA0<9x|AW?RBZfa1Io zd<9(sJ2AmA$!QSKw7=rGfcA3{=O96ZUB^dx2wj9I7?1Re)sPwJ3oJnkklIpN^$B`} zD6lJ2!2(-@EXaBEwFpdG(H^t_1@3#RMvNyqK=mU+S1p0TM9|B8dTb^Fq4H%O3k8EO zTD^G-sKkH5kuW&J=AOtF{ZJv}+$=LtYXcFrKmrEDcD7oYckRT+wQGZeL!W*DMfLvZ z-*<0jU%oto7Vvy_Hk`|s?$RRTii~ZeNKH=tUPCOIV7)6E-L45K`0ApHr6imH7eTh`d>v2>HvtCPU0@%*+g) zb{y76$PwPJv6-!W-sKaR2zuE|l9)+1eh0Mt`-tWf_+?_}ff9Uf*(n81Ph;CVfAjm@ z_|zbcU!^BA7|Iqd(NJCgkdtK=WpxY4mJxrQtcbVLZ%4|umi+Q>!d>VFit@enmaM_P zX(JdIsJEMG1%S7-Vb(`(TLdsXIP(N+0QCN@CX|$#$bI38e8~@!25@7X*g%=0CYY z!81i_l3+R&DfDtz<{}BMoM-4`h;os|Rp*$81dau`-e=YxG`${Cb&LRER968d)MxfJ zfH*Zau)v9yk1i1rGPukElr`Xr6F9UyE|j>q;ajZ8mkJ%rBS=Yl!>KHT1EW-YdH|%l zL?c(t?F!$IrxUMFI}Ym-7*v0N7V}U5wYba0T~)kiTLPEf%+S)mT9f7cp_6i3Y-{T^ zad2{z6d0kzn|1`rkd3KIrbijB$>uP+V(65OM{`n%2`E>vmzCZUQ(fu=@)YE-o0)$m1c3K98e3C zEc&O?Itg5@iT8k(a2`m;rx%?>I2|Le|2?TLC26^qqIg|N;}FXESM9EdS4rUBA9Rp_ zDgs?bwNs{a9a#@Y#kE67O-!xTYri=SY((Cm{iO3Qv~7gv*ct#6mU z;gH^vl1`8$y)YF72Dn zR(Pd%&Wv3ouwc?i{(auSa(g3JZV315%_J*W=ginsi>#M_Q>ku_T=IIkY#cPwJ>E^2 z-S|R9Da;&I2)^Y-OCRxV>rGEhjWyCkY_{UFGwotqFE?#FKAimQpnj0W$U5bLnhoA2 z5hxxsaKMgWAQZTfFBV77qrtm&@r7%X+$n-R(Z-kI)yq#fZ3gTExWh#kh#LCQhb=}( z8105-aI|Rs`lV7QZRSTG+>O6ZS?78!b*v#0oZu3Dz%Yi(W=(?TuGL;|Inwr#ltt6$ z7_0H^O_!8rHf&3w7mP^>!$bbr0sAJ2$Y#IaNV@$$7u|+azS{7>00000NkvXXu0mjf D2=2I| literal 0 HcmV?d00001 diff --git a/assets/icons/postcard_icon.png b/assets/icons/postcard_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..eb81f636a08e3e6b87aa081f3805481da30c8fcf GIT binary patch literal 1287 zcmV+i1^D`jP)@~0drDELIAGL9O(c600d`2O+f$vv5yPUnPGUFdo^qFBmt0yx0X7%>Hw{HPbQlh|d2CZUoNl-3N7Ttmv zBV5rV(LXM=+ubO;P(fbZzrXm)^70?9fZq|J00}}$_(UjMrAFf@?EVEoTI=ftA;ePz zT!I86L3X7OTWihcv41=uNV8HYvJg+;1|bC0*P8pg=hq`aqQv0wkNjlhw>S3?E%B_l z7j{bscZX-nq58Kch;p1Vn|;pnXfa?ZvEBQ6Qh)sJ-FHX;>OCQosY$>;12-gm$H9*c z<#26pfQ@u95af30VE+gb#Bhb#X;ms67Uk*a2EwASv+8;iz1fk;zVJWDQ|o29(P%@0 z33CzX;3i2sR}2iYzeu_m3iXb>1a|_178Gor4f){OD`N4Pc>Vx#p`n3m4SM==8U~?t zD8U`YSpwxfo5i>ruyULZ%rV|nN32QD=gCkT;$Jn}bqQeivH;A(>2)BJ9bGCd@O7p7 zpWo(TWB(tRLsayEhS)699t;h9x=t`--KS4}bymRNT>!cCtnT5f?IM|GZ zAS#^*jU9La2>YY%`AvCjwN+d<64Qn_AjGOa%aB5L$#pg_%UsVwiQF@`%#WV2;Wz$k ztbdHf43R*2|24U^9hJtbdr0syFo?ovhQve=Ev10|_`Iqgw7K|ygcBR5IxSK`G*Uq{ zQb9CgDoAd8c{3NZ;YHi1rV|T6NQjUAYhlYrQT8g#mr@3@c+T96Dt_?@%xpRWAxDv_ z3dcEldjW2EpoKR6su#R0a1X)cM?nlI?d^4%Yt@cFm+^K5ELaVFKudyEIRBv8`~U{T z#)r`0$F>UD;fi?AIQ<<{``8Gg!tv9ry}`9!qp9yDcw8hu?S9!9w_E2Y9zJBqu%@xY zPV@hTm6ZaC@aj+T& z8|Bw~6Y&dI!AOvUYV{GX=QZB(fJuQCSHR2F_Ya%Yg*jND!`1 zPGu^474sQm^vQ58QRf}=E{MssSD}T+-#(wbg&AY)REJxFiE57sBVBeiQb9CQK{Skg z$U(WBw_JOo&AX-c-ppGui~}>0P?IEKk{pz)ffmVGFjs*YAv}N?V>m { + NavigationBloc() : super(const NavigationState(0)) { + on((event, emit) { + emit(NavigationState(event.index)); + }); + } +} diff --git a/lib/common_packages/custom_bottom_navbar.dart b/lib/common_packages/custom_bottom_navbar.dart new file mode 100644 index 0000000..6dc6f1d --- /dev/null +++ b/lib/common_packages/custom_bottom_navbar.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../common_bloc/bottom_navigation_bloc.dart'; + + +class CustomBottomNavBar extends StatelessWidget { + const CustomBottomNavBar({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Container( + decoration: BoxDecoration( + color: const Color(0xffFFF5F5), + border: Border.all(color: Color(0xffFDCDCE)), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(24), + topRight: Radius.circular(24), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 8, + offset: const Offset(0, -2), + ), + ], + ), + padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildNavItem( + context, + index: 0, + iconPath: 'assets/icons/explore.png', + label: 'Explore', + isActive: state.selectedIndex == 0, + ), + _buildNavItem( + context, + index: 1, + iconPath: 'assets/icons/magic.png', + label: 'Magic Itinerary', + isActive: state.selectedIndex == 1, + ), + _buildNavItem( + context, + index: 2, + iconPath: 'assets/icons/pass_icon.png', + label: 'My Passes', + isActive: state.selectedIndex == 2, + ), + _buildNavItem( + context, + index: 3, + iconPath: 'assets/icons/postcard_icon.png', + label: 'Postcard', + isActive: state.selectedIndex == 3, + ), + ], + ), + ); + }, + ); + } + + Widget _buildNavItem(BuildContext context, { + required int index, + required String iconPath, + required String label, + required bool isActive, + }) { + final color = isActive ? const Color(0xFFBB474A) : Color(0xFFBB474A).withOpacity(0.6); + + return GestureDetector( + onTap: () => + context.read().add(NavigationTabChanged(index)), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (isActive) + Container( + margin: const EdgeInsets.only(bottom: 4), + height: 4, + width: 50, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(2), + ), + ) + else + const SizedBox(height: 7), + + Image.asset(iconPath, scale: 4, color: color), + const SizedBox(height: 4), + Text( + label, + style: TextStyle( + color: color, + fontSize: 12, + fontWeight: isActive ? FontWeight.w500 : FontWeight.w500, + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 7b336ed..04bfcd0 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -6,12 +6,10 @@ import 'package:citycards_customer/privacy/privacy_view.dart'; import 'package:citycards_customer/terms_and_condition/terms_and_condition_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../common_bloc/bottom_navigation_bloc.dart'; import '../home/views/home_page_view.dart'; import 'route_constants.dart'; -// Uncomment these when blocs are ready -// import '../login/blocs/login_bloc.dart'; -// import '../home/blocs/home_bloc.dart'; class AppRouter { Route onGenerateRoute(RouteSettings settings) { @@ -20,8 +18,7 @@ class AppRouter { case RouteConstants.home: return MaterialPageRoute( builder: (_) { - // return BlocProvider(create: (_) => HomeBloc(), child: const HomePage()); - return const HomePage(); + return BlocProvider(create: (_) => NavigationBloc(), child: const HomePage()); }, ); diff --git a/lib/home/views/first_time_user_home_page.dart b/lib/home/views/first_time_user_home_page.dart new file mode 100644 index 0000000..454e807 --- /dev/null +++ b/lib/home/views/first_time_user_home_page.dart @@ -0,0 +1,276 @@ +import 'package:flutter/material.dart'; + +import '../../common_packages/app_bar.dart'; +import 'explore_cities_card.dart'; + +class FirstTimeUserHomePage extends StatefulWidget { + const FirstTimeUserHomePage({super.key}); + + @override + State createState() => _FirstTimeUserHomePageState(); +} + +class _FirstTimeUserHomePageState extends State { + + final ScrollController _scrollController = ScrollController(); + + double _scrollProgress = 0.0; + + final List> featuredCities = [ + { + "name": "Melbourne", + "description": "Australia's cultural capital famous for vibrant...", + "individualTicket": "\$350+", + "cityCard": "\$199", + "savings": "Save \$151+", + "image": + "assets/images/city_sydney.png" + }, + { + "name": "Sydney", + "description": "Australia's cultural capital famous for vibrant...", + "individualTicket": "\$400+", + "cityCard": "\$249", + "savings": "Save \$151+", + "image": + "assets/images/city_sydney.png" + }, + { + "name": "Sydney", + "description": "Australia's cultural capital famous for vibrant...", + "individualTicket": "\$400+", + "cityCard": "\$249", + "savings": "Save \$151+", + "image": + "assets/images/city_sydney.png" + } + ]; + + final List> upcomingCities = [ + { + "image": "assets/images/city_turkey.jpg", + "name": "Turkey", + }, + { + "image":"assets/images/city_germany.jpg", + "name": "Germany" + }, + { + "image":"assets/images/city_switz.jpg", + "name" : "Switzerland" + }, + { + "image":"assets/images/city_maldives.jpg", + "name": "Maldives" + }, + { + "image" : "assets/images/city_turkey.jpg", + "name": "Turkey" + }, + { + "image":"assets/images/city_germany.jpg", + "name": "Germany" + }, + { + "image":"assets/images/city_switz.jpg", + "name" : "Switzerland" + }, + { + "image":"assets/images/city_maldives.jpg", + "name": "Maldives" + }, + ]; + + @override + void initState() { + super.initState(); + _scrollController.addListener(_updateScrollProgress); + } + + void _updateScrollProgress() { + if (!_scrollController.hasClients || + _scrollController.position.maxScrollExtent == 0) return; + setState(() { + _scrollProgress = (_scrollController.offset / + _scrollController.position.maxScrollExtent) + .clamp(0.0, 1.0); + }); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: SingleChildScrollView( + child: Stack( + children: [ + Image.asset("assets/images/home_bg.png"), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CommonAppBar(isWhiteLogo: false , isProfilePage: false), + const SizedBox(height: 140), + const Text( + "CityCards.\nSee More,\nSpend Less.", + style: TextStyle( + fontSize: 44, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 8), + const Text( + "Instant QR access to 40+ attractions,\nexclusive perks, and savings up to 30%", + style: TextStyle(color: Colors.white), + ), + const SizedBox(height: 20), + ElevatedButton( + style: ElevatedButton.styleFrom( + fixedSize: const Size(200, 50), + padding: const EdgeInsets.all(15), + backgroundColor: const Color(0xffF95F62), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25), + ), + ), + onPressed: () {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Get You'r CityCard", + style: TextStyle(color: Colors.white)), + const SizedBox(width: 10), + Image.asset("assets/icons/arrow.png", height: 13), + ], + ), + ), + const SizedBox(height: 80), + Text.rich( + TextSpan( + children: const [ + TextSpan( + text: "Explore ", + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500, + color: Color(0xffF95F62), + ), + ), + TextSpan( + text: "Cities", + style: + TextStyle(fontSize: 24, color: Colors.black, fontWeight: FontWeight.w500,), + ), + ], + ), + ), + const SizedBox(height: 8), + const Text( + "Explore your dream destination and experience various attractions.", + style: TextStyle(color: Color(0xff676D75)), + ), + const SizedBox(height: 16), + + // Horizontal cards + SizedBox( + height: 270, + child: ListView.builder( + controller: _scrollController, + scrollDirection: Axis.horizontal, + itemCount: featuredCities.length, + itemBuilder: (context, index) { + final city = featuredCities[index]; + return ExploreCitiesCard( + name: city['name']!, + description: city['description']!, + imageUrl: city['image']!, + individualPrice: city['individualTicket']!, + cityCardPrice: city['cityCard']!, + savingsText: city['savings']!, + ); + }, + ), + ), + + const SizedBox(height: 10), + const SizedBox(height: 10), + Align( + alignment: Alignment.center, + child: SizedBox( + width: 200, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: LinearProgressIndicator( + value: _scrollProgress, + minHeight: 6, + backgroundColor: Color(0xffFEE7E7), + color: const Color(0xffF95F62), + ), + ), + ), + ), + + + const SizedBox(height: 40), + Text.rich( + TextSpan( + children: const [ + TextSpan( + text: "Upcoming ", + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500, + color: Color(0xffF95F62), + ), + ), + TextSpan( + text: "Cities", + style: + TextStyle(fontSize: 24, color: Colors.black, fontWeight: FontWeight.w500), + ), + ], + ), + ), + const SizedBox(height: 8), + Text( + "Explore your dream destination and experience various attractions.", + style: TextStyle(color: Colors.grey[600]), + ), + const SizedBox(height: 16), + SizedBox( + height: 80, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: upcomingCities.length, + separatorBuilder: (_, __) => const SizedBox(width: 16), + itemBuilder: (context, index) { + return Column( + children: [ + CircleAvatar( + radius: 28, + backgroundImage: AssetImage(upcomingCities[index]["image"] ?? ""), + ), + const SizedBox(height: 4), + Text(upcomingCities[index]["name"] ?? "", + style: const TextStyle(fontSize: 12)), + ], + ); + }, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/home/views/home_page_view.dart b/lib/home/views/home_page_view.dart index 24d65c4..e985766 100644 --- a/lib/home/views/home_page_view.dart +++ b/lib/home/views/home_page_view.dart @@ -1,7 +1,9 @@ -import 'package:citycards_customer/home/views/explore_cities_card.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../common_packages/app_bar.dart'; +import '../../common_bloc/bottom_navigation_bloc.dart'; +import '../../common_packages/custom_bottom_navbar.dart'; +import 'first_time_user_home_page.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -11,281 +13,28 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { - final ScrollController _scrollController = ScrollController(); - double _scrollProgress = 0.0; - - final List> featuredCities = [ - { - "name": "Melbourne", - "description": "Australia's cultural capital famous for vibrant...", - "individualTicket": "\$350+", - "cityCard": "\$199", - "savings": "Save \$151+", - "image": - "assets/images/city_sydney.png" - }, - { - "name": "Sydney", - "description": "Australia's cultural capital famous for vibrant...", - "individualTicket": "\$400+", - "cityCard": "\$249", - "savings": "Save \$151+", - "image": - "assets/images/city_sydney.png" - }, - { - "name": "Sydney", - "description": "Australia's cultural capital famous for vibrant...", - "individualTicket": "\$400+", - "cityCard": "\$249", - "savings": "Save \$151+", - "image": - "assets/images/city_sydney.png" - } - ]; - - final List> upcomingCities = [ - { - "image": "assets/images/city_turkey.jpg", - "name": "Turkey", - }, - { - "image":"assets/images/city_germany.jpg", - "name": "Germany" - }, - { - "image":"assets/images/city_switz.jpg", - "name" : "Switzerland" - }, - { - "image":"assets/images/city_maldives.jpg", - "name": "Maldives" - }, - { - "image" : "assets/images/city_turkey.jpg", - "name": "Turkey" - }, - { - "image":"assets/images/city_germany.jpg", - "name": "Germany" - }, - { - "image":"assets/images/city_switz.jpg", - "name" : "Switzerland" - }, - { - "image":"assets/images/city_maldives.jpg", - "name": "Maldives" - }, - ]; - - @override - void initState() { - super.initState(); - _scrollController.addListener(_updateScrollProgress); - } - - void _updateScrollProgress() { - if (!_scrollController.hasClients || - _scrollController.position.maxScrollExtent == 0) return; - setState(() { - _scrollProgress = (_scrollController.offset / - _scrollController.position.maxScrollExtent) - .clamp(0.0, 1.0); - }); - } - - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } @override Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: SingleChildScrollView( - child: Stack( - children: [ - Image.asset("assets/images/home_bg.png"), - Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CommonAppBar(isWhiteLogo: false , isProfilePage: false), - const SizedBox(height: 140), - const Text( - "CityCards.\nSee More,\nSpend Less.", - style: TextStyle( - fontSize: 44, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - const SizedBox(height: 8), - const Text( - "Instant QR access to 40+ attractions,\nexclusive perks, and savings up to 30%", - style: TextStyle(color: Colors.white), - ), - const SizedBox(height: 20), - ElevatedButton( - style: ElevatedButton.styleFrom( - fixedSize: const Size(200, 50), - padding: const EdgeInsets.all(15), - backgroundColor: const Color(0xffF95F62), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(25), - ), - ), - onPressed: () {}, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text("Get You'r CityCard", - style: TextStyle(color: Colors.white)), - const SizedBox(width: 10), - Image.asset("assets/icons/arrow.png", height: 13), - ], - ), - ), - const SizedBox(height: 80), - Text.rich( - TextSpan( - children: const [ - TextSpan( - text: "Explore ", - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500, - color: Color(0xffF95F62), - ), - ), - TextSpan( - text: "Cities", - style: - TextStyle(fontSize: 24, color: Colors.black, fontWeight: FontWeight.w500,), - ), - ], - ), - ), - const SizedBox(height: 8), - const Text( - "Explore your dream destination and experience various attractions.", - style: TextStyle(color: Color(0xff676D75)), - ), - const SizedBox(height: 16), - - // Horizontal cards - SizedBox( - height: 270, - child: ListView.builder( - controller: _scrollController, - scrollDirection: Axis.horizontal, - itemCount: featuredCities.length, - itemBuilder: (context, index) { - final city = featuredCities[index]; - return ExploreCitiesCard( - name: city['name']!, - description: city['description']!, - imageUrl: city['image']!, - individualPrice: city['individualTicket']!, - cityCardPrice: city['cityCard']!, - savingsText: city['savings']!, - ); - }, - ), - ), - - const SizedBox(height: 10), - const SizedBox(height: 10), - Align( - alignment: Alignment.center, - child: SizedBox( - width: 200, - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: LinearProgressIndicator( - value: _scrollProgress, - minHeight: 6, - backgroundColor: Color(0xffFEE7E7), - color: const Color(0xffF95F62), - ), - ), - ), - ), - - - const SizedBox(height: 40), - Text.rich( - TextSpan( - children: const [ - TextSpan( - text: "Upcoming ", - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500, - color: Color(0xffF95F62), - ), - ), - TextSpan( - text: "Cities", - style: - TextStyle(fontSize: 24, color: Colors.black, fontWeight: FontWeight.w500), - ), - ], - ), - ), - const SizedBox(height: 8), - Text( - "Explore your dream destination and experience various attractions.", - style: TextStyle(color: Colors.grey[600]), - ), - const SizedBox(height: 16), - SizedBox( - height: 80, - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: upcomingCities.length, - separatorBuilder: (_, __) => const SizedBox(width: 16), - itemBuilder: (context, index) { - return Column( - children: [ - CircleAvatar( - radius: 28, - backgroundImage: AssetImage(upcomingCities[index]["image"] ?? ""), - ), - const SizedBox(height: 4), - Text(upcomingCities[index]["name"] ?? "", - style: const TextStyle(fontSize: 12)), - ], - ); - }, - ), - ), - ], - ), - ), - ], + return BlocBuilder( + builder: (context, state) { + Widget body; + switch (state.selectedIndex){ + case 0: + body = const FirstTimeUserHomePage(); + break; + default: + body = const FirstTimeUserHomePage(); + } + return SafeArea( + top: false, + child: Scaffold( + body: body, + bottomNavigationBar: CustomBottomNavBar(), ), - ), - ), - bottomNavigationBar: BottomNavigationBar( - currentIndex: 0, - selectedItemColor: Colors.red, - unselectedItemColor: Colors.grey, - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.explore), label: "Explore"), - BottomNavigationBarItem( - icon: Icon(Icons.auto_fix_high), label: "Magic Itinerary"), - BottomNavigationBarItem( - icon: Icon(Icons.card_giftcard), label: "My Passes"), - BottomNavigationBarItem( - icon: Icon(Icons.post_add), label: "Postcard"), - ], - ), + ); + } ); } }