diff --git a/assets/icons/discount_clock.png b/assets/icons/discount_clock.png new file mode 100644 index 0000000..a9fdcb5 Binary files /dev/null and b/assets/icons/discount_clock.png differ diff --git a/assets/icons/discount_crown.png b/assets/icons/discount_crown.png new file mode 100644 index 0000000..1b80eff Binary files /dev/null and b/assets/icons/discount_crown.png differ diff --git a/assets/icons/discount_percent.png b/assets/icons/discount_percent.png new file mode 100644 index 0000000..e8dc2a6 Binary files /dev/null and b/assets/icons/discount_percent.png differ diff --git a/assets/icons/esim_camera.png b/assets/icons/esim_camera.png new file mode 100644 index 0000000..93503d9 Binary files /dev/null and b/assets/icons/esim_camera.png differ diff --git a/assets/icons/esim_location.png b/assets/icons/esim_location.png new file mode 100644 index 0000000..6eb1339 Binary files /dev/null and b/assets/icons/esim_location.png differ diff --git a/assets/icons/esim_people.png b/assets/icons/esim_people.png new file mode 100644 index 0000000..b484881 Binary files /dev/null and b/assets/icons/esim_people.png differ diff --git a/assets/icons/esim_phone.png b/assets/icons/esim_phone.png new file mode 100644 index 0000000..801c614 Binary files /dev/null and b/assets/icons/esim_phone.png differ diff --git a/assets/icons/process_phone.png b/assets/icons/process_phone.png new file mode 100644 index 0000000..dab9da4 Binary files /dev/null and b/assets/icons/process_phone.png differ diff --git a/assets/icons/process_qr.png b/assets/icons/process_qr.png new file mode 100644 index 0000000..c7538e2 Binary files /dev/null and b/assets/icons/process_qr.png differ diff --git a/assets/icons/process_wifi.png b/assets/icons/process_wifi.png new file mode 100644 index 0000000..c61270f Binary files /dev/null and b/assets/icons/process_wifi.png differ diff --git a/assets/images/esim_bottom_banner.png b/assets/images/esim_bottom_banner.png new file mode 100644 index 0000000..9f9cbb3 Binary files /dev/null and b/assets/images/esim_bottom_banner.png differ diff --git a/assets/images/esim_top_bg.png b/assets/images/esim_top_bg.png new file mode 100644 index 0000000..12139ff Binary files /dev/null and b/assets/images/esim_top_bg.png differ diff --git a/assets/images/marriot_hotel.jpg b/assets/images/marriot_hotel.jpg new file mode 100644 index 0000000..aebd90c Binary files /dev/null and b/assets/images/marriot_hotel.jpg differ diff --git a/lib/common_packages/custom_filled_button.dart b/lib/common_packages/custom_filled_button.dart index 6a6e706..29a849a 100644 --- a/lib/common_packages/custom_filled_button.dart +++ b/lib/common_packages/custom_filled_button.dart @@ -7,6 +7,7 @@ class CustomFilledButton extends StatelessWidget { final String label; final bool? showArrow; final GestureTapCallback onTap; + final double? height; CustomFilledButton({ super.key, @@ -14,6 +15,7 @@ class CustomFilledButton extends StatelessWidget { required this.onTap, required this.label, this.showArrow = false, + this.height = 42 }); @override @@ -21,7 +23,7 @@ class CustomFilledButton extends StatelessWidget { return GestureDetector( onTap: onTap, child: Container( - height: 42.h, + height: height, width: width, decoration: BoxDecoration( color: Color(0xFFF95F62), diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 806c430..89f1da1 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -2,7 +2,10 @@ import 'package:citycards_customer/Profile/profile_page_view.dart'; import 'package:citycards_customer/common_bloc/language_selection_bloc.dart'; import 'package:citycards_customer/contact_us/contact_us_view.dart'; import 'package:citycards_customer/edit_profile/edit_profile_view.dart'; +import 'package:citycards_customer/esim_offer/esim_offer_view.dart'; import 'package:citycards_customer/faq/faq_view.dart'; +import 'package:citycards_customer/hotel_offer/hotel_offer_view.dart'; +import 'package:citycards_customer/itinerary_creation/bloc/date_selection_bloc.dart'; import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart'; import 'package:citycards_customer/itinerary_creation/views/itinerary_creation_start_view.dart'; import 'package:citycards_customer/itinerary_creation/views/itinerary_creation_view.dart'; @@ -82,11 +85,32 @@ class AppRouter { case RouteConstants.itineraryCreation: return MaterialPageRoute( builder: (_) { - return BlocProvider(create: (_) => ItineraryStepNavigationBloc(), - child: ItineraryCreationPage(), + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => UpdateSelectedDateBloc(), + ), + BlocProvider( + create: (_) => ItineraryStepNavigationBloc(), + ), + ], + child: const ItineraryCreationPage(), ); }, ); + + case RouteConstants.hotelOffer: + return MaterialPageRoute(builder: (_){ + return HotelOfferView(); + }); + + + case RouteConstants.esimOffer: + return MaterialPageRoute( + builder: (_) { + return EsimOfferPage(); + }, + ); default: return MaterialPageRoute( builder: (_) => diff --git a/lib/core/route_constants.dart b/lib/core/route_constants.dart index efddbd4..3a42f4f 100644 --- a/lib/core/route_constants.dart +++ b/lib/core/route_constants.dart @@ -19,4 +19,9 @@ class RouteConstants { static const String itineraryCreationStart = '/itineraryCreationStart'; static const String itineraryCreation = '/itineraryCreation'; + + /**************************** ESIM Page *****************************************/ + + static const String esimOffer = '/esim_offer'; + static const String hotelOffer = '/hotelOffer'; } diff --git a/lib/esim_offer/esim_offer_view.dart b/lib/esim_offer/esim_offer_view.dart new file mode 100644 index 0000000..71317f6 --- /dev/null +++ b/lib/esim_offer/esim_offer_view.dart @@ -0,0 +1,405 @@ +import 'package:citycards_customer/common_packages/app_bar.dart'; +import 'package:citycards_customer/common_packages/custom_filled_button.dart'; +import 'package:citycards_customer/common_packages/custom_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class EsimOfferPage extends StatelessWidget { + const EsimOfferPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: SafeArea( + child: SingleChildScrollView( + child: Column( + children: [ + Container( + color: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: CommonAppBar(isWhiteLogo: false, isProfilePage: false), + ), + + /************************* Top Banner ***********************/ + Stack( + children: [ + Image.asset( + "assets/images/esim_top_bg.png", + width: double.infinity, + ), + Positioned( + top: 32.h, + left: 24.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 48.h, + width: 48.w, + decoration: BoxDecoration( + color: Color(0xFFFFFFFF).withOpacity(.2), + borderRadius: BorderRadius.circular(20.r), + ), + child: Icon(Icons.wifi, color: Colors.white), + ), + + SizedBox(height: 24.h), + SizedBox( + width: 350.w, + child: CustomText( + text: + "Stay Connected Instantly with Your Complimentary eSIM", + size: 22.sp, + color: Color(0xFFFFFFFF), + ), + ), + SizedBox(height: 12.h), + SizedBox( + width: 350, + child: CustomText( + text: + "Because every unforgettable trip starts with seamless connectivity.", + size: 14.sp, + color: Colors.white, + ), + ), + SizedBox(height: 22.h), + Container( + height: 48.h, + width: 165.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(38.r), + boxShadow: [ + BoxShadow( + color: Colors.black12, + offset: Offset(4, 4), + blurRadius: 5, + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomText( + text: "View Plans", + size: 16.sp, + color: Color(0xFFF95F62), + ), + SizedBox(width: 6.w), + Icon( + Icons.arrow_forward, + color: Color(0xFFF95F62), + size: 18, + ), + ], + ), + ), + ], + ), + ), + ], + ), + + SizedBox(height: 32.h), + + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "With your ", + style: TextStyle( + fontSize: 26.sp, + fontWeight: FontWeight.w300, + ), + ), + TextSpan( + text: "eSIM", + style: TextStyle( + color: Color(0xFFF95F62), + fontSize: 26.sp, + fontWeight: FontWeight.w700, + ), + ), + TextSpan( + text: ", you can:", + style: TextStyle( + fontSize: 26.sp, + fontWeight: FontWeight.w300, + ), + ), + ], + ), + ), + SizedBox(height: 37.h), + ServiceCard( + "assets/icons/esim_location.png", + "Navigate the city with ease", + "Access real-time maps and directions wherever you go", + ), + SizedBox(height: 28.h), + ServiceCard( + "assets/icons/esim_phone.png", + "Book rides, access maps, and find attractions in real time", + "Stay connected to all essential travel services", + ), + SizedBox(height: 28.h), + ServiceCard( + "assets/icons/esim_camera.png", + "Share photos and memories instantly", + "Upload and share your travel moments without delay", + ), + SizedBox(height: 28.h), + ServiceCard( + "assets/icons/esim_people.png", + "Stay connected with friends, family, and travel plans", + "Never miss important updates or messages while traveling", + ), + + SizedBox(height: 75.h), + + Container( + width: double.infinity, + padding: EdgeInsets.only( + left: 33.w, + right: 33.w, + top: 70.h, + bottom: 37.h, + ), + color: Color(0xFFFFF5F5), + child: Column( + children: [ + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "Simple ", + style: TextStyle(fontSize: 26.sp), + ), + TextSpan( + text: "3-Step Process", + style: TextStyle( + color: Color(0xFFF95F62), + fontSize: 26.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + SizedBox(height: 16.h), + CustomText( + text: "Get connected in seconds", + size: 17.5, + color: Color(0xFF4B5563), + ), + SizedBox(height: 56.h), + ProcessCard( + "Receive QR Code", + "Get your unique eSIM QR code with your CityCard", + "1", + "assets/icons/process_qr.png", + ), + SizedBox(height: 28.h), + ProcessCard( + "Scan Code", + "Open your phone camera and scan the QR code", + "2", + "assets/icons/process_phone.png", + ), + SizedBox(height: 28.h), + ProcessCard( + "Connected", + "You're online instantly - start exploring!", + "3", + "assets/icons/process_wifi.png", + ), + ], + ), + ), + + Stack( + children: [ + Image.asset( + 'assets/images/esim_bottom_banner.png', + fit: BoxFit.contain, + ), + Positioned.fill( + child: Container( + height: double.infinity, + width: double.infinity, + color: Colors.black.withOpacity(.68), + ), + ), + Positioned.fill( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "It's one more way", + style: TextStyle( + color: Colors.white, + fontSize: 21.sp, + fontWeight: FontWeight.w400, + ), + ), + TextSpan( + text: " CityCard", + style: TextStyle( + color: Color(0xFFF95F62), + fontSize: 21.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + SizedBox(height: 4.h,), + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "makes your journey", + style: TextStyle( + color: Colors.white, + fontSize: 21.sp, + fontWeight: FontWeight.w400, + ), + ), + TextSpan( + text: " smarter", + style: TextStyle( + color: Color(0xFFF95F62), + fontSize: 21.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + SizedBox(height: 4.h,), + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "and more", + style: TextStyle( + color: Colors.white, + fontSize: 21.sp, + fontWeight: FontWeight.w400, + ), + ), + TextSpan( + text: " effortless", + style: TextStyle( + color: Color(0xFFF95F62), + fontSize: 21.sp, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + SizedBox(height: 28.h,), + CustomFilledButton(onTap: (){}, label: "Start Your Journey Today", height: 60.h, width: 300.w,showArrow: true,), + ], + ), + ), + ], + ), + SizedBox(height: 150.h,) + ], + ), + ), + ), + ); + } + + Widget ServiceCard(String icon, String title, String subTitle) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 28.w), + child: Container( + padding: EdgeInsets.symmetric(vertical: 21.h, horizontal: 21.w), + decoration: BoxDecoration( + color: Color(0xFFFFF5F5), + borderRadius: BorderRadius.circular(14.r), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset(icon, scale: 4), + SizedBox(width: 14.w), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + ), + softWrap: true, + ), + SizedBox(height: 6.h), + Text( + subTitle, + style: TextStyle(fontSize: 14.sp, color: Color(0xFF4B5563)), + softWrap: true, + ), + ], + ), + ), + ], + ), + ), + ); + } + + Widget ProcessCard(String title, String subTitle, String count, String icon) { + return Container( + padding: EdgeInsets.symmetric(vertical: 28.h, horizontal: 28.w), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow(color: Colors.black12, offset: Offset(0, 5), blurRadius: 5), + ], + borderRadius: BorderRadius.circular(14.sp), + ), + child: Column( + children: [ + Container( + height: 56.h, + width: 56.w, + decoration: BoxDecoration( + color: Color(0xFFF95F62), + borderRadius: BorderRadius.circular(30.r), + ), + child: Center( + child: CustomText( + text: count, + size: 21.sp, + weight: FontWeight.w700, + color: Colors.white, + ), + ), + ), + SizedBox(height: 21.h), + Image.asset(icon, scale: 4), + SizedBox(height: 14.h), + CustomText(text: title, size: 18.sp, weight: FontWeight.w700), + SizedBox(height: 10.h), + Text( + subTitle, + style: TextStyle(fontSize: 14.sp, color: Color(0xFF4B5563)), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} diff --git a/lib/hotel_offer/hotel_offer_view.dart b/lib/hotel_offer/hotel_offer_view.dart new file mode 100644 index 0000000..d57f014 --- /dev/null +++ b/lib/hotel_offer/hotel_offer_view.dart @@ -0,0 +1,384 @@ +import 'package:citycards_customer/common_packages/app_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:citycards_customer/common_packages/custom_filled_button.dart'; +import 'package:citycards_customer/common_packages/custom_text.dart'; + +class HotelOfferView extends StatelessWidget { + const HotelOfferView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: SafeArea( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: CommonAppBar(isWhiteLogo: false, isProfilePage: false), + ), + // Banner Section + Stack( + children: [ + Image.asset( + "assets/images/marriot_hotel.jpg", + height: 529.h, + width: double.infinity, + fit: BoxFit.cover, + ), + Positioned.fill( + child: Container( + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Color(0xFF000000).withOpacity(.4), + Color(0xFF000000).withOpacity(.4), + Color(0xFFFFFFFF), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ), + Positioned.fill( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 30.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Enjoy 20% Off at\nMarriott Hotels\nExclusively with CityCard", + style: TextStyle( + fontSize: 32.sp, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 21.h), + Text( + "Make every stay as unforgettable as the city you're exploring.", + style: TextStyle( + fontSize: 18.sp, + color: Colors.white, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + ], + ), + + SizedBox(height: 65.h), + + Padding( + padding: EdgeInsets.symmetric(horizontal: 22.w), + child: Column( + children: [ + Text( + "Your CityCard unlocks more than just attractions — it also opens doors to exceptional stays.", + style: TextStyle( + fontSize: 21.sp, + color: Color(0xFF1F2937), + ), + textAlign: TextAlign.center, + ), + + SizedBox(height: 31.h), + + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: TextStyle( + color: const Color(0xFF364153), + fontSize: 18.sp, + height: 1.6, + ), + children: [ + const TextSpan( + text: "Thanks to our exclusive partnership with ", + ), + TextSpan( + text: "Marriott Hotels", + style: TextStyle(color: const Color(0xFFF95F62)), + ), + const TextSpan(text: ", CityCard holders enjoy "), + TextSpan( + text: "20% off best available rates", + style: TextStyle(color: const Color(0xFFF95F62)), + ), + const TextSpan( + text: + " across a curated selection of properties in the city.", + ), + ], + ), + ), + ], + ), + ), + + SizedBox(height: 69.h), + + Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 32.h, horizontal: 16.w), + decoration: BoxDecoration(color: const Color(0xFFFFF5F5)), + child: Column( + children: [ + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: TextStyle( + color: const Color(0xFF101828), + fontSize: 26.25.sp, + ), + children: [ + const TextSpan(text: "Choose from a "), + TextSpan( + text: "Wide \nVariety", + style: TextStyle( + color: const Color(0xFFF95F62), + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ), + SizedBox(height: 24.h), + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: TextStyle( + color: const Color(0xFF364153), + fontSize: 18.sp, + height: 1.5, + ), + children: [ + const TextSpan( + text: + "Choose from a wide variety of Marriott hotels — from elegant urban hideaways and premium city-centre locations to luxurious five-star experiences — all designed to make your trip ", + ), + TextSpan( + text: "effortless, comfortable and memorable", + style: TextStyle( + color: const Color(0xFFF95F62), + fontWeight: FontWeight.w600, + ), + ), + TextSpan(text: " and "), + TextSpan( + text: "memorable", + style: TextStyle( + color: const Color(0xFFF95F62), + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ], + ), + ), + SizedBox(height: 70.h), + + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "Simply use your CityCard", + style: TextStyle( + fontSize: 26.25.sp, + color: Color(0xFF1F2937), + ), + ), + + TextSpan( + text: " booking link to:", + style: TextStyle( + fontSize: 26.25.sp, + color: Color(0xFFF95F62), + fontWeight: FontWeight.w700, + ), + ), + ], + ), + textAlign: TextAlign.center, + ), + + SizedBox(height: 56.h), + + _featureCard( + icon: "assets/icons/discount_percent.png", + title: "Access 20% off best available rates", + subtitle: "Save on your stay at premium Marriott properties", + ), + SizedBox(height: 28.h), + _featureCard( + icon: "assets/icons/discount_clock.png", + title: "Enjoy priority check-in and late checkout", + subtitle: "Subject to availability for your convenience", + ), + SizedBox(height: 28.h), + _featureCard( + icon: "assets/icons/discount_crown.png", + title: "Receive exclusive seasonal offers", + subtitle: "Designed specially for CityCard travellers", + ), + + SizedBox(height: 56.h), + + // Bottom CTA + Padding( + padding: EdgeInsets.symmetric(horizontal: 30.w), + child: Text.rich( + textAlign : TextAlign.center, + TextSpan( + children: [ + TextSpan( + text: "It's just one more way", + style: TextStyle( + fontSize: 21.sp, + color: Color(0xFF1F2937), + ), + ), + TextSpan( + text: " CityCard", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w700, + color: Color(0xFFF95F62), + ), + ), + TextSpan( + text: " makes exploring", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w400, + color: Color(0xFF1F2937), + ), + ), + TextSpan( + text: " smarter", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w600, + color: Color(0xFFF95F62), + ), + ), + TextSpan( + text: ",", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w400, + color: Color(0xFF1F2937), + ), + ), + TextSpan( + text: " simpler", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w600, + color: Color(0xFFF95F62), + ), + ), + TextSpan( + text: ", and", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w400, + color: Color(0xFF1F2937), + ), + ), + TextSpan( + text: " more rewarding", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w600, + color: Color(0xFFF95F62), + ), + ), + TextSpan( + text: ".", + style: TextStyle( + fontSize: 21.sp, + fontWeight: FontWeight.w400, + color: Color(0xFF1F2937), + ), + ), + ], + ), + ), + ), + SizedBox(height: 28.h), + Align( + alignment: Alignment.center, + child: CustomFilledButton( + onTap: () {}, + label: "Get your CityCard today", + showArrow: true, + height: 59.h, + width: 291.w, + ), + ), + SizedBox(height: 70.h), + ], + ), + ), + ), + ); + } + + Widget _featureCard({ + required String icon, + required String title, + required String subtitle, + }) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 22.w), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 21.w, vertical: 21.h), + decoration: BoxDecoration( + color: const Color(0xFFFFF5F5), + borderRadius: BorderRadius.circular(14.r), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset(icon, scale: 4), + SizedBox(height: 21.h), + + Text( + title, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: Color(0xFF1F2937), + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 21.h), + Text( + subtitle, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w400, + color: Color(0xFF4B5563), + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } +} diff --git a/lib/itinerary_creation/bloc/date_selection_bloc.dart b/lib/itinerary_creation/bloc/date_selection_bloc.dart new file mode 100644 index 0000000..db1f13f --- /dev/null +++ b/lib/itinerary_creation/bloc/date_selection_bloc.dart @@ -0,0 +1,28 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; + +abstract class SelectDateEvent {} + +class SelectItineraryDateEvent extends SelectDateEvent { + final String date; + SelectItineraryDateEvent(this.date); +} + +class SelectItineraryDateState { + final String selectedDate; + const SelectItineraryDateState(this.selectedDate); +} + +class UpdateSelectedDateBloc + extends Bloc { + UpdateSelectedDateBloc() + : super( + SelectItineraryDateState( + DateFormat('EEEE, MMMM d, yyyy').format(DateTime.now()), + ), + ) { + on((event, emit) { + emit(SelectItineraryDateState(event.date)); + }); + } +} diff --git a/lib/itinerary_creation/views/itinerary_creation_start_view.dart b/lib/itinerary_creation/views/itinerary_creation_start_view.dart index 9f8905f..389f223 100644 --- a/lib/itinerary_creation/views/itinerary_creation_start_view.dart +++ b/lib/itinerary_creation/views/itinerary_creation_start_view.dart @@ -11,78 +11,80 @@ class ItineraryCreationStartPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( backgroundColor: Color(0xFFFFF5F5), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - height: 103.h, - width: 103.w, - - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(24.r), - boxShadow: [ - BoxShadow( - color: Colors.black12, - offset: Offset(0, 4), - blurRadius: 5, - ), - ], - ), - child: Center( - child: Image.asset("assets/icons/magic_creation.png", scale: 4), - ), - ), - - SizedBox(height: 34.h), - Text.rich( - TextSpan( - children: [ - TextSpan( - text: "Create your", - style: TextStyle( - color: Color(0xFF101828), - fontSize: 24.sp, - fontWeight: FontWeight.w500, + body: SafeArea( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + height: 103.h, + width: 103.w, + + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(24.r), + boxShadow: [ + BoxShadow( + color: Colors.black12, + offset: Offset(0, 4), + blurRadius: 5, ), - ), - TextSpan( - text: " magic itinerary", - style: TextStyle( - color: Color(0xFFF95F62), - fontSize: 24.sp, - fontWeight: FontWeight.w500, + ], + ), + child: Center( + child: Image.asset("assets/icons/magic_creation.png", scale: 4), + ), + ), + + SizedBox(height: 34.h), + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "Create your", + style: TextStyle( + color: Color(0xFF101828), + fontSize: 24.sp, + fontWeight: FontWeight.w500, + ), ), - ), - ], + TextSpan( + text: " magic itinerary", + style: TextStyle( + color: Color(0xFFF95F62), + fontSize: 24.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), ), - ), - - SizedBox(height: 13.h), - Padding( - padding: EdgeInsets.symmetric(horizontal: 25.w), - child: Text( - "Answer a few quick questions and we'll craft a personalized travel experience just for you ✨", - style: TextStyle(fontSize: 14, color: Color(0xFF4A5565)), - textAlign: TextAlign.center, + + SizedBox(height: 13.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 25.w), + child: Text( + "Answer a few quick questions and we'll craft a personalized travel experience just for you ✨", + style: TextStyle(fontSize: 14, color: Color(0xFF4A5565)), + textAlign: TextAlign.center, + ), ), - ), - SizedBox(height: 47.h), - CustomFilledButton( - onTap: () { - Navigator.pushNamed(context, RouteConstants.itineraryCreation); - }, - showArrow: true, - label: "Let’s Get Started", - ), - SizedBox(height: 38.h), - CustomText( - text: "Takes only 2 minutes ⏱️", - color: Color(0xFF6A7282), - size: 14.sp, - ), - ], + SizedBox(height: 47.h), + CustomFilledButton( + onTap: () { + Navigator.pushNamed(context, RouteConstants.itineraryCreation); + }, + showArrow: true, + label: "Let’s Get Started", + ), + SizedBox(height: 38.h), + CustomText( + text: "Takes only 2 minutes ⏱️", + color: Color(0xFF6A7282), + size: 14.sp, + ), + ], + ), ), ), ); diff --git a/lib/itinerary_creation/views/itinerary_creation_steps/date_selection_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/date_selection_view.dart index 2a65ca7..5554ce2 100644 --- a/lib/itinerary_creation/views/itinerary_creation_steps/date_selection_view.dart +++ b/lib/itinerary_creation/views/itinerary_creation_steps/date_selection_view.dart @@ -1,9 +1,11 @@ import 'package:citycards_customer/common_packages/custom_filled_button.dart'; import 'package:citycards_customer/common_packages/custom_text.dart'; +import 'package:citycards_customer/itinerary_creation/bloc/date_selection_bloc.dart'; import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:intl/intl.dart'; class DateSelectionView extends StatelessWidget { const DateSelectionView({super.key}); @@ -34,16 +36,24 @@ class DateSelectionView extends StatelessWidget { border: Border.all(color: Color(0xFFF95F62), width: 1.1.w), ), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Image.asset("assets/icons/calender.png", scale: 4), - - CustomText( - text: "Wednesday, October 15, 2025", - size: 14.sp, - color: Color(0xFF101828), + GestureDetector( + onTap: () { + _pickDate(context); + }, + child: Image.asset("assets/icons/calender.png", scale: 4), ), - + SizedBox(width: 16.w,), + BlocBuilder( + builder: (context, state) { + return CustomText( + text: state.selectedDate, + size: 14.sp, + color: Color(0xFF101828), + ); + }, + ), + const Spacer(), Icon(Icons.check_circle, color: Color(0xFFF95F62)), ], ), @@ -62,4 +72,36 @@ class DateSelectionView extends StatelessWidget { ), ); } + + Future _pickDate(BuildContext context) async { + final DateTime? picked = await showDatePicker( + context: context, + // initialDate: , + firstDate: DateTime.now().subtract(const Duration(days: 0)), + lastDate: DateTime.now().add(const Duration(days: 365 * 3)), + builder: (context, child) { + return Theme( + data: Theme.of(context).copyWith( + colorScheme: const ColorScheme.light( + primary: Color(0xFFF95F62), + onPrimary: Colors.white, + onSurface: Color(0xFF101828), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: const Color(0xFFF95F62), + ), + ), + ), + child: child!, + ); + }, + ); + if (picked != null) { + final formattedDate = DateFormat('EEEE, MMMM d, y').format(picked); + context.read().add( + SelectItineraryDateEvent(formattedDate), + ); + } + } } diff --git a/lib/itinerary_creation/views/itinerary_creation_view.dart b/lib/itinerary_creation/views/itinerary_creation_view.dart index 6488942..490e019 100644 --- a/lib/itinerary_creation/views/itinerary_creation_view.dart +++ b/lib/itinerary_creation/views/itinerary_creation_view.dart @@ -1,3 +1,4 @@ +import 'package:citycards_customer/itinerary_creation/bloc/date_selection_bloc.dart'; import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -65,57 +66,63 @@ class _ItineraryCreationPageState extends State { curve: Curves.easeInOut, ); }, - child: Column( - children: [ - Padding( - padding: EdgeInsets.only( - left: 20.w, - right: 20.w, - bottom: 20.h, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: - BlocBuilder< - ItineraryStepNavigationBloc, - ItineraryStepNavigationState - >( - builder: (context, state) { - return LinearProgressIndicator( - value: state.selectedIndex / 10, - borderRadius: BorderRadius.circular(10), - backgroundColor: Colors.white, - color: const Color(0xFFF95F62), - minHeight: 6.h, - ); - }, - ), - ), - ), - - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 20.w), - child: PageView( - controller: _pageController, - physics: const NeverScrollableScrollPhysics(), - children: [ - DateSelectionView(), - CitySelectionView(), - EnergySelectionView(), - KidsSelectionView(), - DietarySelectionView(), - ArtGallerySelectionView(), - ScenicViewpointsRatingView(), - HistoricalSiteRatingView(), - WildlifeRatingView(), - ShoppingRatingView(), - ItineraryCompletionView(), - ], + child: SafeArea( + child: Column( + children: [ + Padding( + padding: EdgeInsets.only( + left: 20.w, + right: 20.w, + bottom: 20.h, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: + BlocBuilder< + ItineraryStepNavigationBloc, + ItineraryStepNavigationState + >( + builder: (context, state) { + return LinearProgressIndicator( + value: state.selectedIndex / 10, + borderRadius: BorderRadius.circular(10), + backgroundColor: Colors.white, + color: const Color(0xFFF95F62), + minHeight: 6.h, + ); + }, + ), ), ), - ), - ], + + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 20.w), + child: PageView( + controller: _pageController, + physics: const NeverScrollableScrollPhysics(), + children: [ + BlocProvider(create: (_){ + return UpdateSelectedDateBloc(); + }, + child: DateSelectionView(), + ), + CitySelectionView(), + EnergySelectionView(), + KidsSelectionView(), + DietarySelectionView(), + ArtGallerySelectionView(), + ScenicViewpointsRatingView(), + HistoricalSiteRatingView(), + WildlifeRatingView(), + ShoppingRatingView(), + ItineraryCompletionView(), + ], + ), + ), + ), + ], + ), ), ), ); diff --git a/pubspec.lock b/pubspec.lock index d1431d7..dca9f12 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -139,6 +139,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" leak_tracker: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b6042db..b66a61a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 flutter_screenutil: ^5.9.3 + intl: ^0.20.2 dev_dependencies: flutter_test: