diff --git a/assets/icons/arrow_angle_up.png b/assets/icons/arrow_angle_up.png new file mode 100644 index 0000000..d0b60d7 Binary files /dev/null and b/assets/icons/arrow_angle_up.png differ diff --git a/assets/images/chicago.png b/assets/images/chicago.png new file mode 100644 index 0000000..c992189 Binary files /dev/null and b/assets/images/chicago.png differ diff --git a/assets/images/claim_offers_bg.jpg b/assets/images/claim_offers_bg.jpg new file mode 100644 index 0000000..b9728cc Binary files /dev/null and b/assets/images/claim_offers_bg.jpg differ diff --git a/assets/images/clock.png b/assets/images/clock.png new file mode 100644 index 0000000..32a36e4 Binary files /dev/null and b/assets/images/clock.png differ diff --git a/assets/images/get_your_pass_bg.jpg b/assets/images/get_your_pass_bg.jpg new file mode 100644 index 0000000..841ccb4 Binary files /dev/null and b/assets/images/get_your_pass_bg.jpg differ diff --git a/assets/images/koh_rong.png b/assets/images/koh_rong.png new file mode 100644 index 0000000..aba7319 Binary files /dev/null and b/assets/images/koh_rong.png differ diff --git a/assets/images/lady.png b/assets/images/lady.png new file mode 100644 index 0000000..caf7c95 Binary files /dev/null and b/assets/images/lady.png differ diff --git a/assets/images/london_bg.jpg b/assets/images/london_bg.jpg new file mode 100644 index 0000000..132e135 Binary files /dev/null and b/assets/images/london_bg.jpg differ diff --git a/assets/images/magic_itenary_bg.jpg b/assets/images/magic_itenary_bg.jpg new file mode 100644 index 0000000..360b601 Binary files /dev/null and b/assets/images/magic_itenary_bg.jpg differ diff --git a/assets/images/paris_bg.jpg b/assets/images/paris_bg.jpg new file mode 100644 index 0000000..a196cb3 Binary files /dev/null and b/assets/images/paris_bg.jpg differ diff --git a/assets/images/tokyo_bg.jpg b/assets/images/tokyo_bg.jpg new file mode 100644 index 0000000..018e91d Binary files /dev/null and b/assets/images/tokyo_bg.jpg differ diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 6bdf4a8..44ebae3 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -5,8 +5,8 @@ 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/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_start_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_view.dart'; +import 'package:citycards_customer/itinerary_creation/views/itinerary_creation_start_view.dart'; +import 'package:citycards_customer/itinerary_creation/views/itinerary_creation_view.dart'; 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'; diff --git a/lib/home/views/first_time_user_home_page.dart b/lib/home/views/first_time_user_home_page.dart index 286844d..c918ee0 100644 --- a/lib/home/views/first_time_user_home_page.dart +++ b/lib/home/views/first_time_user_home_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../common_packages/app_bar.dart'; -import 'explore_cities_card.dart'; +import '../widgets/explore_cities_card.dart'; class FirstTimeUserHomePage extends StatefulWidget { const FirstTimeUserHomePage({super.key}); diff --git a/lib/home/views/home_page_view.dart b/lib/home/views/home_page_view.dart index e985766..5b3e397 100644 --- a/lib/home/views/home_page_view.dart +++ b/lib/home/views/home_page_view.dart @@ -1,3 +1,4 @@ +import 'package:citycards_customer/home/views/registered_user_home_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -23,6 +24,8 @@ class _HomePageState extends State { switch (state.selectedIndex){ case 0: body = const FirstTimeUserHomePage(); + case 1: + body = const RegisteredUserHomePage(); break; default: body = const FirstTimeUserHomePage(); diff --git a/lib/home/views/registered_user_home_page.dart b/lib/home/views/registered_user_home_page.dart new file mode 100644 index 0000000..005c82e --- /dev/null +++ b/lib/home/views/registered_user_home_page.dart @@ -0,0 +1,272 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import '../../common_packages/app_bar.dart'; +import '../widgets/attractions_list.dart'; +import '../widgets/get_your_pass_card.dart'; +import '../widgets/gradient_container_bg.dart'; +import '../widgets/journey_cards_listview.dart'; +import '../widgets/pass_card_list.dart'; + +class RegisteredUserHomePage extends StatefulWidget { + const RegisteredUserHomePage({super.key}); + + @override + State createState() => _RegisteredUserHomePageState(); +} + +class _RegisteredUserHomePageState extends State { + + final List> attractions = [ + { + 'title': 'Long-Tail Boat Charter', + 'subtitle': 'Lorem ipsum dolor sit amet...', + 'image': + 'assets/images/clock.png', + }, + { + 'title': 'Koh Rong Samloemr', + 'subtitle': 'Lorem ipsum dolor sit amet...', + 'image': 'assets/images/koh_rong.png', + }, + { + 'title': 'Long-Tail Boat Charter', + 'subtitle': 'Lorem ipsum dolor sit amet...', + 'image': + 'assets/images/clock.png', + }, + { + 'title': 'Koh Rong Samloemr', + 'subtitle': 'Lorem ipsum dolor sit amet...', + 'image': 'assets/images/koh_rong.png', + }, + ]; + + + @override + Widget build(BuildContext context) { + return SafeArea( + child: SingleChildScrollView( + child: Stack( + children: [ + Image.asset( + "assets/images/chicago.png", + height: 300, + width: double.infinity, + fit: BoxFit.cover, + ), + + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CommonAppBar(isWhiteLogo: true , isProfilePage: false), + const SizedBox(height: 70), + Text( + "Chicago", + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + fontSize: 44, + ), + ), + const SizedBox(height: 4), + Text( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Cras posuere, nisl id dictum consequat, elit enim tincidunt magna...", + style: TextStyle( + color: Colors.white.withOpacity(0.9), + fontSize: 12, + fontWeight: FontWeight.w400 + ), + ), + const SizedBox(height: 12), + + // Category tags + Wrap( + spacing: 8, + children: [ + _buildTag("Food"), + _buildTag("Drinks"), + _buildTag("Culture"), + _buildTag("Souvenirs"), + ], + ), + + const SizedBox(height: 40), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text.rich( + TextSpan( + children: const [ + TextSpan( + text: "Popular ", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Color(0xffF95F62), + ), + ), + TextSpan( + text: "Attractions", + style: + TextStyle(fontSize: 18, color: Colors.black, fontWeight: FontWeight.w500,), + ), + ], + ), + ), + InkWell( + onTap: (){}, + child: Text("View all", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Color(0xffF95F62), + ), + ), + ) + ], + ), + const SizedBox(height: 12), + AttractionsListView(attractions: attractions), + ], + ), + ), + InwardCurvedContainer( + child: Stack( + children: [ + DreamJourneySection() + ] + ) + ), + const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + _buildFeatureCard( + image: "assets/images/claim_offers_bg.jpg", + title: "Claim offers with your City Cards", + subtitle: "Lorem ipsum dolor sit amet...", + ), + ], + ), + + const SizedBox(height: 24), + + ChooseYourPassSection(), + + const SizedBox(height: 20), + // ===== GET YOUR PASS SECTION ===== + GetYourPassCard(), + const SizedBox(height: 40), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget _buildTag(String label) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: + BoxDecoration(color: Color(0xffF95F62), borderRadius: BorderRadius.circular(20)), + child: Text(label, + style:TextStyle( + color: Colors.white, fontWeight: FontWeight.w500, fontSize: 12)), + ); + } + + Widget _buildFeatureCard({ + required String image, + required String title, + required String subtitle, + }) { + return Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(16), + child: Image.asset( + image, + height: 200, + width: double.infinity, + fit: BoxFit.cover, + ), + ), + Positioned( + left: 16, + right: 16, + bottom: 16, + child: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.9), + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Left side text + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 18, + ), + ), + Text( + subtitle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: GoogleFonts.poppins( + fontSize: 14, + color: Colors.black.withOpacity(0.6), + ), + ), + ], + ), + ), + + const SizedBox(width: 8), + + // Right side arrow button + Container( + decoration: const BoxDecoration( + color: Color(0xffFDCDCE), + shape: BoxShape.circle, + ), + padding: const EdgeInsets.all(8), + child: Image.asset( + "assets/icons/arrow_angle_up.png", + scale: 4, + ), + ), + ], + ), + ), + ), + ], + ); + } + +} diff --git a/lib/home/widgets/attractions_list.dart b/lib/home/widgets/attractions_list.dart new file mode 100644 index 0000000..8c07917 --- /dev/null +++ b/lib/home/widgets/attractions_list.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class AttractionsListView extends StatefulWidget { + final List> attractions; + + const AttractionsListView({super.key, required this.attractions}); + + @override + State createState() => _AttractionsListViewState(); +} + +class _AttractionsListViewState extends State { + final ScrollController _scrollController = ScrollController(); + double _scrollProgress = 0.0; + + @override + void initState() { + super.initState(); + _scrollController.addListener(_updateScrollProgress); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + void _updateScrollProgress() { + if (!_scrollController.hasClients || + _scrollController.position.maxScrollExtent == 0) return; + setState(() { + _scrollProgress = (_scrollController.offset / + _scrollController.position.maxScrollExtent) + .clamp(0.0, 1.0); + }); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SizedBox( + height: 240, + child: ListView.builder( + controller: _scrollController, + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.only(left: 16), + itemCount: widget.attractions.length, + itemBuilder: (context, index) { + final item = widget.attractions[index]; + return Container( + alignment: Alignment.center, + margin: const EdgeInsets.only(right: 16), + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + border: Border.all( + color: const Color(0xFFF95F62).withOpacity(0.24), + ), + borderRadius: BorderRadius.circular(16), + ), + child: Container( + height: 232, + width: 161, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + image: DecorationImage( + image: AssetImage(item['image']!), + fit: BoxFit.cover, + ), + ), + alignment: Alignment.bottomLeft, + padding: const EdgeInsets.all(12), + child: Text( + item['title']!, + style: GoogleFonts.poppins( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + ), + ); + }, + ), + ), + const SizedBox(height: 20), + Align( + alignment: Alignment.center, + child: SizedBox( + width: 200, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: LinearProgressIndicator( + value: _scrollProgress, + minHeight: 6, + backgroundColor: const Color(0xffFEE7E7), + color: const Color(0xffF95F62), + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/home/views/explore_cities_card.dart b/lib/home/widgets/explore_cities_card.dart similarity index 100% rename from lib/home/views/explore_cities_card.dart rename to lib/home/widgets/explore_cities_card.dart diff --git a/lib/home/widgets/get_your_pass_card.dart b/lib/home/widgets/get_your_pass_card.dart new file mode 100644 index 0000000..8d2979d --- /dev/null +++ b/lib/home/widgets/get_your_pass_card.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class GetYourPassCard extends StatelessWidget { + const GetYourPassCard({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + decoration: BoxDecoration( + color: const Color(0xFFFFF1F1), + borderRadius: BorderRadius.circular(40), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // ===== Left Section ===== + Row( + children: [ + // Left texts and overlapping images + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Get your Pass", + style: GoogleFonts.poppins( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + const SizedBox(height: 6), + Row( + children: [ + // Stacked circular attraction images + AttractionsAvatarStack(imagePath: 'assets/images/get_your_pass_bg.jpg',), + const SizedBox(width: 8), + Text( + "Attractions", + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black87, + ), + ), + ], + ), + ], + ), + ], + ), + + // ===== Right Section ===== + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "From", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "\$20", + style: GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w700, + color: Colors.black, + ), + ), + TextSpan( + text: " /Adult", + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.black87, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(width: 12), + // Circular Arrow Button + Container( + padding: const EdgeInsets.all(10), + decoration: const BoxDecoration( + color: Color(0xffF95F62), + shape: BoxShape.circle, + ), + child: const Icon( + Icons.arrow_forward, + color: Colors.white, + size: 18, + ), + ), + ], + ), + ], + ), + ); + } +} + +class AttractionsAvatarStack extends StatelessWidget { + const AttractionsAvatarStack({ + super.key, + required this.imagePath, // from your assets/figma + this.size = 26, // circle diameter + this.count = 4, // total circles including the last “16+” + this.overlap = 8, // how much they overlap + this.moreText = '16+', + }); + + final String imagePath; + final double size; + final int count; + final double overlap; + final String moreText; + + @override + Widget build(BuildContext context) { + final step = size - overlap; // horizontal step between circles + return SizedBox( + width: size + (count - 1) * step, + height: size, + child: Stack( + clipBehavior: Clip.none, + children: List.generate(count, (i) { + final left = i * step; + final isLast = i == count - 1; + + return Positioned( + left: left, + child: _AvatarCircle( + size: size, + imagePath: imagePath, + showOverlayText: isLast, + overlayText: moreText, + ), + ); + }), + ), + ); + } +} + +class _AvatarCircle extends StatelessWidget { + const _AvatarCircle({ + required this.size, + required this.imagePath, + this.showOverlayText = false, + this.overlayText = '16+', + }); + + final double size; + final String imagePath; + final bool showOverlayText; + final String overlayText; + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + padding: const EdgeInsets.all(1.5), // white ring thickness + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Colors.white, // ring color + ), + child: ClipOval( + child: Stack( + fit: StackFit.expand, + children: [ + Image.asset(imagePath, fit: BoxFit.cover), + if (showOverlayText) + Container( + color: Colors.black.withOpacity(0.35), + alignment: Alignment.center, + child: Text( + overlayText, + style: const TextStyle( + color: Colors.white, + fontSize: 11, + fontWeight: FontWeight.w700, + ), + ), + ), + ], + ), + ), + ); + } +} + diff --git a/lib/home/widgets/gradient_container_bg.dart b/lib/home/widgets/gradient_container_bg.dart new file mode 100644 index 0000000..11611bd --- /dev/null +++ b/lib/home/widgets/gradient_container_bg.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +class InwardCurvedContainer extends StatelessWidget { + final Widget child; + const InwardCurvedContainer({super.key, required this.child}); + + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: InwardAndBottomConvexClipper(), + child: Container( + width: double.infinity, + height: 700, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFFFFF5F5), + Color(0xFFFDCDCE), + Color(0xFFFFF5F5), + ], + ), + ), + child: child, + ), + ); + } +} + +class InwardAndBottomConvexClipper extends CustomClipper { + @override + Path getClip(Size size) { + Path path = Path(); + + // 👇 Start at top-left corner + path.moveTo(0, 0); + + // ===== Top inward (concave) curve ===== + path.quadraticBezierTo( + size.width / 2, 60, // Control point (lower Y = deeper dip) + size.width, 0, // End of top curve + ); + + // Right edge down + path.lineTo(size.width, size.height - 80); + + // ===== Bottom outward (convex) curve ===== + path.quadraticBezierTo( + size.width / 2, size.height + 20, // Control point (higher Y = bulge outward) + 0, size.height - 80, // End of bottom curve + ); + + // Close back to start + path.close(); + + return path; + } + + @override + bool shouldReclip(CustomClipper oldClipper) => false; +} diff --git a/lib/home/widgets/journey_cards_listview.dart b/lib/home/widgets/journey_cards_listview.dart new file mode 100644 index 0000000..9220edf --- /dev/null +++ b/lib/home/widgets/journey_cards_listview.dart @@ -0,0 +1,303 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class DreamJourneySection extends StatefulWidget { + const DreamJourneySection({super.key}); + + @override + State createState() => _DreamJourneySectionState(); +} + +class _DreamJourneySectionState extends State { + late PageController _pageController; + double _currentPage = 1.0; + + final List> baseJourneys = [ + { + "city": "Tokyo", + "country": "Japan", + "days": "4 days", + "tags": ["Modern Culture", "Temple"], + "image": "assets/images/tokyo.jpg", + "itinerary": [ + {"title": "Senso-ji Temple", "time": "8:00 AM"}, + {"title": "Tokyo Skytree", "time": "8:00 AM"}, + ] + }, + { + "city": "Paris", + "country": "France", + "days": "3 days", + "tags": ["Art", "Romantic"], + "image": "assets/images/paris.jpg", + "itinerary": [ + {"title": "Eiffel Tower", "time": "9:00 AM"}, + {"title": "Louvre Museum", "time": "11:00 AM"}, + ] + }, + { + "city": "Bangkok", + "country": "Thailand", + "days": "5 days", + "tags": ["Culture", "Food"], + "image": "assets/images/bangkok.jpg", + "itinerary": [ + {"title": "Wat Arun", "time": "9:00 AM"}, + {"title": "Floating Market", "time": "1:00 PM"}, + ] + }, + ]; + + late List> journeys; + + @override + void initState() { + super.initState(); + + // Duplicate list for infinite scroll illusion + journeys = List.generate(9, (i) => baseJourneys[i % baseJourneys.length]); + + _pageController = PageController(initialPage: 1, viewportFraction: 0.75); + _pageController.addListener(() { + setState(() { + _currentPage = _pageController.page ?? 1.0; + }); + + // Infinite loop logic + if (_pageController.page == journeys.length - 2) { + Future.microtask(() { + _pageController.jumpToPage(2); + }); + } else if (_pageController.page == 1) { + Future.microtask(() { + _pageController.jumpToPage(journeys.length - 3); + }); + } + }); + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + // --- build --- + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 60), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // === Title === + Text.rich( + TextSpan( + children: [ + TextSpan( + text: "Plan Your ", + style: GoogleFonts.poppins( + fontSize: 20, fontWeight: FontWeight.w600), + ), + TextSpan( + text: "Dream Journey", + style: GoogleFonts.poppins( + fontSize: 20, + fontWeight: FontWeight.w600, + color: const Color(0xffF95F62), + ), + ), + TextSpan( + text: "\nin Just 3 Seconds", + style: GoogleFonts.poppins( + fontSize: 20, fontWeight: FontWeight.w600), + ), + ], + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 40), + + // === 3D Tilted Carousel === + SizedBox( + height: 440, + child: PageView.builder( + controller: _pageController, + physics: const BouncingScrollPhysics(), + itemCount: journeys.length, + itemBuilder: (context, index) { + final double distance = (_currentPage - index); + final double scale = (1 - (distance.abs() * 0.15)).clamp(0.8, 1.0); + + // 👇 3D tilt transform (top tilts out, bottom tilts in) + final Matrix4 transform = Matrix4.identity() + ..setEntry(3, 2, 0.0015) // perspective + ..rotateX(distance * -0.4); // tilt angle + + return Transform( + alignment: Alignment.center, + transform: transform, + child: Opacity( + opacity: (1 - distance.abs() * 0.3).clamp(0.6, 1), + child: Transform.scale( + scale: scale, + child: _buildJourneyCard(journeys[index]), + ), + ), + ); + }, + ), + ), + ], + ), + ); + } + + // --- Card Widget --- + Widget _buildJourneyCard(Map item) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 15, + offset: const Offset(0, 8), + ) + ], + ), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // // Image + badge + // Stack( + // children: [ + // ClipRRect( + // borderRadius: + // const BorderRadius.vertical(top: Radius.circular(20)), + // child: Image.asset( + // item['image'], + // height: 160, + // width: double.infinity, + // fit: BoxFit.cover, + // ), + // ), + // Positioned( + // right: 10, + // top: 10, + // child: Container( + // padding: + // const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + // decoration: BoxDecoration( + // color: const Color(0xffF95F62), + // borderRadius: BorderRadius.circular(12), + // ), + // child: Text( + // item['days'], + // style: GoogleFonts.poppins( + // color: Colors.white, + // fontSize: 12, + // fontWeight: FontWeight.w500, + // ), + // ), + // ), + // ), + // ], + // ), + // Padding( + // padding: const EdgeInsets.all(16.0), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text(item['city'], + // style: GoogleFonts.poppins( + // fontSize: 16, fontWeight: FontWeight.w600)), + // Text(item['country'], + // style: GoogleFonts.poppins( + // fontSize: 12, color: Colors.grey[600])), + // const SizedBox(height: 8), + // Wrap( + // spacing: 8, + // children: List.generate( + // item['tags'].length, + // (i) => Container( + // padding: const EdgeInsets.symmetric( + // horizontal: 10, vertical: 4), + // decoration: BoxDecoration( + // color: const Color(0xffFEE7E7), + // borderRadius: BorderRadius.circular(20), + // ), + // child: Text( + // item['tags'][i], + // style: GoogleFonts.poppins( + // color: const Color(0xffF95F62), + // fontSize: 11, + // ), + // ), + // ), + // ), + // ), + // const SizedBox(height: 16), + // const Divider(), + // const SizedBox(height: 8), + // Text("Day 1", + // style: GoogleFonts.poppins( + // fontWeight: FontWeight.w600, fontSize: 13)), + // const SizedBox(height: 6), + // ...List.generate( + // item['itinerary'].length, + // (i) { + // final activity = item['itinerary'][i]; + // return Container( + // margin: const EdgeInsets.only(bottom: 8), + // padding: const EdgeInsets.all(10), + // decoration: BoxDecoration( + // color: const Color(0xffFEE7E7), + // borderRadius: BorderRadius.circular(10), + // ), + // child: Row( + // children: [ + // CircleAvatar( + // radius: 12, + // backgroundColor: const Color(0xffF95F62), + // child: Text( + // "${i + 1}", + // style: const TextStyle( + // color: Colors.white, fontSize: 12), + // ), + // ), + // const SizedBox(width: 8), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text(activity['title'], + // style: GoogleFonts.poppins( + // fontSize: 12, + // fontWeight: FontWeight.w500)), + // Text(activity['time'], + // style: GoogleFonts.poppins( + // fontSize: 11, + // color: Colors.grey[700])), + // ], + // ), + // ], + // ), + // ); + // }, + // ), + // const Divider(), + // const SizedBox(height: 4), + // Text("Day 2", + // style: GoogleFonts.poppins( + // fontWeight: FontWeight.w600, fontSize: 13)), + // ], + // ), + // ) + // ], + // ), + ); + } +} diff --git a/lib/home/widgets/pass_card_list.dart b/lib/home/widgets/pass_card_list.dart new file mode 100644 index 0000000..083c81f --- /dev/null +++ b/lib/home/widgets/pass_card_list.dart @@ -0,0 +1,189 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class ChooseYourPassSection extends StatefulWidget { + const ChooseYourPassSection({super.key}); + + @override + State createState() => _ChooseYourPassSectionState(); +} + +class _ChooseYourPassSectionState extends State { + final PageController _pageController = PageController( + viewportFraction: 0.92, + ); + + int _currentPage = 0; + + final List> passes = [ + { + "title": "Chicago-\nFLEXI CARD", + "price": "\$50", + "color": const Color(0xffF95FAF), + "bgColor": const Color(0xFFFDE7F1), + }, + { + "title": "Chicago-\nUnlimited CARD", + "price": "\$120", + "color": const Color(0xffF95F62), + "bgColor": const Color(0xFFFFE8E8), + }, + ]; + + @override + void initState() { + super.initState(); + _pageController.addListener(() { + final page = _pageController.page ?? 0; + setState(() => _currentPage = page.round()); + }); + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // ===== TITLE ===== + Text( + "Choose your Pass", + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + Text( + "Dive into an extensive selection of thrilling destinations, " + "thoughtfully categorized to help you find the perfect getaway.", + style: GoogleFonts.poppins( + fontSize: 13, + color: Colors.grey[700], + ), + ), + + const SizedBox(height: 20), + + // ===== STATIC PAGEVIEW (no animation) ===== + SizedBox( + height: 430, + child: PageView.builder( + controller: _pageController, + itemCount: passes.length, + itemBuilder: (context, index) { + final item = passes[index]; + return _buildPassCard(item); + }, + ), + ), + + const SizedBox(height: 12), + + Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(passes.length, (index) { + bool isActive = index == _currentPage; + return AnimatedContainer( + duration: const Duration(milliseconds: 250), + margin: const EdgeInsets.symmetric(horizontal: 4), + width: isActive ? 40 : 20, + height: 6, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: isActive + ? const Color(0xffF95F62) + : const Color(0xffFEE7E7), + ), + ); + }), + ), + ), + ], + ); + } + + // ===== CARD BUILDER ===== + Widget _buildPassCard(Map item) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: item['bgColor'], + border: Border.all(color: item['color'].withOpacity(0.6)), + borderRadius: BorderRadius.circular(20), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item['title'], + style: GoogleFonts.poppins( + fontSize: 22, + fontWeight: FontWeight.w700, + color: item['color'], + ), + ), + const SizedBox(height: 6), + Text( + "From ${item['price']}", + style: GoogleFonts.poppins( + fontSize: 16, + color: item['color'], + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 12), + Text( + "Dive into an extensive selection of thrilling destinations, " + "thoughtfully categorized to help you find the perfect getaway.", + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.grey[800], + height: 1.4, + ), + ), + const SizedBox(height: 16), + const Text( + "• Fusce tincidunt interdum ex, in tincidunt libero porttitor vel.\n" + "• Pellentesque vel nisl posuere, ullamcorper nibh.\n" + "• Fusce tincidunt interdum ex, in tincidunt libero porttitor vel.", + style: TextStyle( + fontSize: 12, + color: Colors.black54, + height: 1.5, + ), + ), + const Spacer(), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () {}, + style: ElevatedButton.styleFrom( + backgroundColor: item['color'], + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + ), + child: Text( + "Get a Pass", + style: GoogleFonts.poppins( + fontWeight: FontWeight.w600, + fontSize: 14, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/itinerary_creation/itinerary_creation_start_view.dart b/lib/itinerary_creation/views/itinerary_creation_start_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_start_view.dart rename to lib/itinerary_creation/views/itinerary_creation_start_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/art_gallery_selection_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/art_gallery_selection_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/art_gallery_selection_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/art_gallery_selection_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/city_selection_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/city_selection_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/city_selection_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/city_selection_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/date_selection_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/date_selection_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/date_selection_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/date_selection_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/dietary_selection_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/dietary_selection_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/dietary_selection_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/dietary_selection_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/energy_selection_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/energy_selection_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/energy_selection_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/energy_selection_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/historical_site_rating_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/historical_site_rating_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/historical_site_rating_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/historical_site_rating_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/itinerary_completion_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/itinerary_completion_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/itinerary_completion_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/itinerary_completion_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/kids_selection_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/kids_selection_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/kids_selection_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/kids_selection_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/scenic_viewpoints_rating_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/scenic_viewpoints_rating_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/scenic_viewpoints_rating_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/scenic_viewpoints_rating_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/shopping_rating_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/shopping_rating_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/shopping_rating_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/shopping_rating_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_steps/wildlife_rating_view.dart b/lib/itinerary_creation/views/itinerary_creation_steps/wildlife_rating_view.dart similarity index 100% rename from lib/itinerary_creation/itinerary_creation_steps/wildlife_rating_view.dart rename to lib/itinerary_creation/views/itinerary_creation_steps/wildlife_rating_view.dart diff --git a/lib/itinerary_creation/itinerary_creation_view.dart b/lib/itinerary_creation/views/itinerary_creation_view.dart similarity index 77% rename from lib/itinerary_creation/itinerary_creation_view.dart rename to lib/itinerary_creation/views/itinerary_creation_view.dart index ee4e00e..b0d67cd 100644 --- a/lib/itinerary_creation/itinerary_creation_view.dart +++ b/lib/itinerary_creation/views/itinerary_creation_view.dart @@ -1,19 +1,20 @@ import 'package:citycards_customer/itinerary_creation/bloc/itinerary_steps_selection_bloc.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/art_gallery_selection_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/city_selection_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/date_selection_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/dietary_selection_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/energy_selection_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/historical_site_rating_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/itinerary_completion_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/kids_selection_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/scenic_viewpoints_rating_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/shopping_rating_view.dart'; -import 'package:citycards_customer/itinerary_creation/itinerary_creation_steps/wildlife_rating_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'itinerary_creation_steps/art_gallery_selection_view.dart'; +import 'itinerary_creation_steps/city_selection_view.dart'; +import 'itinerary_creation_steps/date_selection_view.dart'; +import 'itinerary_creation_steps/dietary_selection_view.dart'; +import 'itinerary_creation_steps/energy_selection_view.dart'; +import 'itinerary_creation_steps/historical_site_rating_view.dart'; +import 'itinerary_creation_steps/itinerary_completion_view.dart'; +import 'itinerary_creation_steps/kids_selection_view.dart'; +import 'itinerary_creation_steps/scenic_viewpoints_rating_view.dart'; +import 'itinerary_creation_steps/shopping_rating_view.dart'; +import 'itinerary_creation_steps/wildlife_rating_view.dart'; + class ItineraryCreationPage extends StatefulWidget { const ItineraryCreationPage({super.key});