Merge remote-tracking branch 'origin/dinesh' into vinayak

This commit is contained in:
Vinayakkadge04
2025-10-16 14:59:43 +05:30
34 changed files with 1152 additions and 14 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

BIN
assets/images/chicago.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
assets/images/clock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
assets/images/koh_rong.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

BIN
assets/images/lady.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

BIN
assets/images/london_bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
assets/images/paris_bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
assets/images/tokyo_bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

View File

@@ -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';

View File

@@ -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});

View File

@@ -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<HomePage> {
switch (state.selectedIndex){
case 0:
body = const FirstTimeUserHomePage();
case 1:
body = const RegisteredUserHomePage();
break;
default:
body = const FirstTimeUserHomePage();

View File

@@ -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<RegisteredUserHomePage> createState() => _RegisteredUserHomePageState();
}
class _RegisteredUserHomePageState extends State<RegisteredUserHomePage> {
final List<Map<String, String>> 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,
),
),
],
),
),
),
],
);
}
}

View File

@@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class AttractionsListView extends StatefulWidget {
final List<Map<String, String>> attractions;
const AttractionsListView({super.key, required this.attractions});
@override
State<AttractionsListView> createState() => _AttractionsListViewState();
}
class _AttractionsListViewState extends State<AttractionsListView> {
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),
),
),
),
),
],
);
}
}

View File

@@ -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,
),
),
),
],
),
),
);
}
}

View File

@@ -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<Path> {
@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<Path> oldClipper) => false;
}

View File

@@ -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<DreamJourneySection> createState() => _DreamJourneySectionState();
}
class _DreamJourneySectionState extends State<DreamJourneySection> {
late PageController _pageController;
double _currentPage = 1.0;
final List<Map<String, dynamic>> 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<Map<String, dynamic>> 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<String, dynamic> 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)),
// ],
// ),
// )
// ],
// ),
);
}
}

View File

@@ -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<ChooseYourPassSection> createState() => _ChooseYourPassSectionState();
}
class _ChooseYourPassSectionState extends State<ChooseYourPassSection> {
final PageController _pageController = PageController(
viewportFraction: 0.92,
);
int _currentPage = 0;
final List<Map<String, dynamic>> 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<String, dynamic> 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,
),
),
),
),
],
),
);
}
}

View File

@@ -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});