Completed By a Pass & Checkout View

This commit is contained in:
Vinayakkadge04
2025-10-25 14:17:56 +05:30
parent 94c91abf32
commit 54cf642732
24 changed files with 1443 additions and 66 deletions

View File

@@ -1,32 +0,0 @@
import 'package:citycards_customer/common_packages/app_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class BuyPassView extends StatelessWidget {
const BuyPassView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0.w),
child: Column(
children: [
CommonAppBar(isWhiteLogo: false, isProfilePage: true),
SizedBox(height:24.h),
Row(
children: [
Icon(Icons.arrow_back,)
],
)
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,199 @@
import 'package:citycards_customer/buy_a_pass/widget/feature_table.dart';
import 'package:citycards_customer/buy_a_pass/widget/offer_card_view.dart';
import 'package:citycards_customer/buy_a_pass/widget/pass_card_view.dart';
import 'package:citycards_customer/buy_a_pass/widget/payment_card_view.dart';
import 'package:citycards_customer/common_packages/app_bar.dart';
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class BuyPassView extends StatelessWidget {
BuyPassView({super.key});
final availableAttraction = [
{"image": "assets/images/aa1.png", "name": "Mystic Falls"},
{"image": "assets/images/aa2.png", "name": "Whispering Pines"},
{"image": "assets/images/aa3.png", "name": "Enchanted Oasis"},
{"image": "assets/images/aa4.png", "name": "Serenity Cove"},
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0.w),
child: CommonAppBar(isWhiteLogo: false, isProfilePage: true),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0.w),
child: Row(
children: [
Icon(Icons.arrow_back),
SizedBox(width: 8.w),
CustomText(text: "Buy a Pass", size: 12.sp),
],
),
),
SizedBox(height: 22.h),
Padding(
padding: EdgeInsets.only(left: 20.0.w),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
PassCardView(themeColor: Color(0xFFF95FAF)),
SizedBox(width: 12.w),
PassCardView(themeColor: Color(0xFF1E8AF6)),
],
),
),
),
SizedBox(height: 40.h),
FeatureTable(),
SizedBox(height: 30.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Divider(color: Colors.black.withOpacity(0.1)),
),
SizedBox(height: 30.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0.w),
child: CustomText(text: "Available Attractions", size: 18.sp),
),
SizedBox(height: 12.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
...availableAttraction.map((item) {
return Padding(
padding: EdgeInsets.only(right: 12.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 104.h,
width: 104.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.r),
child: Image.asset(
item["image"]!,
fit: BoxFit.cover,
),
),
),
CustomText(text: item["name"]!, size: 12.sp),
],
),
);
}),
],
),
),
),
SizedBox(height: 20.h),
Align(
alignment: Alignment.center,
child: CustomText(
text: "View All",
size: 12.sp,
color: Color(0xFFF95F62),
),
),
SizedBox(height: 30.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Divider(color: Colors.black.withOpacity(0.1)),
),
SizedBox(height: 40.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomText(text: "Card Benefits", size: 18.sp),
CustomText(
text: "View All",
size: 14.sp,
color: Color(0xFFFF5757),
),
],
),
),
SizedBox(height: 16.h),
Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: const [
OfferCard(
title: '50% OFF',
subtitle: 'on eSIM Data Plans',
tag: 'SPECIAL OFFER',
buttonText: 'Get Offer',
badgeText: 'NEW',
backgroundImage: 'assets/images/blue_card_bg.png',
tagIcon: "assets/icons/process_wifi.png",
themeColor: Color(0xFF4F46E5),
),
SizedBox(width: 16),
OfferCard(
title: '60% OFF',
subtitle: 'Hotel Bookings',
tag: 'EXCLUSIVE DEAL',
buttonText: 'Book Now',
badgeText: 'Limited',
backgroundImage: 'assets/images/red_card_bg.png',
tagIcon: "assets/icons/exclusive.png",
themeColor: Color(0xFFFF5757),
),
SizedBox(width: 16),
OfferCard(
title: 'Airport Transfer',
subtitle: 'With hotel booking',
tag: 'FREE PERK',
buttonText: 'Learn More',
backgroundImage: 'assets/images/green_card_bg.png',
tagIcon: "assets/icons/star.png",
opacity: 0.3,
),
],
),
),
),
SizedBox(height: 41.h),
Center(
child: PaymentCard(
city: 'Melbourne',
tag: 'Flexi Card',
oldPrice: 120,
newPrice: 90,
),
),
SizedBox(height: 20.h),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,126 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class FeatureTable extends StatelessWidget {
const FeatureTable({super.key});
@override
Widget build(BuildContext context) {
// Static data using a simple model
final features = [
FeatureModel('Access to attractions', true, true),
FeatureModel('Entry to attractions', true, true),
FeatureModel('Access to experiences', true, true),
FeatureModel('Entry to sites', false, true),
FeatureModel('Access to venues', true, true),
FeatureModel('Entry to events', true, true),
FeatureModel('Access to experiences', true, true),
];
return Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h),
decoration: BoxDecoration(
color: Color(0xFFF3F3F3),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 1,
offset: const Offset(0, 2),
),
],
),
child: Table(
columnWidths: const {
0: FlexColumnWidth(2.5),
1: FlexColumnWidth(1.2),
2: FlexColumnWidth(1.2),
},
children: [
_buildHeaderRow(),
...features.map((f) => _buildFeatureRow(f)).toList(),
],
),
),
),
);
}
// Header Row
TableRow _buildHeaderRow() {
return TableRow(
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 6.h),
child: Text(
'Features',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.sp),
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 6.h),
child: Center(
child: Text(
'Flexi',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.sp),
),
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 6.h),
child: Center(
child: Text(
'Unlimited',
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16.sp),
),
),
),
],
);
}
// Each Feature Row
TableRow _buildFeatureRow(FeatureModel feature) {
return TableRow(
children: [
_buildCell(feature.name),
_buildIconCell(feature.flexi),
_buildIconCell(feature.unlimited),
],
);
}
// Text cell
Widget _buildCell(String text) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 6.h),
child: Text(text, style: TextStyle(fontSize: 12.sp, color: Colors.black.withOpacity(.8)),),
);
}
// Icon cell
Widget _buildIconCell(bool isAvailable) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 6.h),
child: Center(
child: isAvailable
? Icon(Icons.check_circle, color: Colors.redAccent,size: 16.sp,)
: const Text('', style: TextStyle(color: Colors.black54)),
),
);
}
}
// Model for feature row
class FeatureModel {
final String name;
final bool flexi;
final bool unlimited;
FeatureModel(this.name, this.flexi, this.unlimited);
}

View File

@@ -0,0 +1,127 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class OfferCard extends StatelessWidget {
final String title;
final String subtitle;
final String tag;
final String tagIcon;
final String buttonText;
final String? badgeText;
final String backgroundImage;
final Color? themeColor;
final Color? buttonColor;
final double? opacity;
const OfferCard({
super.key,
required this.title,
required this.subtitle,
required this.tag,
required this.buttonText,
this.badgeText,
required this.backgroundImage,
required this.tagIcon,
this.themeColor = Colors.white,
this.buttonColor= Colors.white,
this.opacity = 1.0,
});
@override
Widget build(BuildContext context) {
return Container(
width: 240.w,
height: 156.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
image: DecorationImage(
image: AssetImage(backgroundImage),
fit: BoxFit.cover,
),
),
child: Stack(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 15.w,vertical: 15.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
tag.toUpperCase(),
style: TextStyle(
color: Colors.white.withOpacity(.9),
fontWeight: FontWeight.w400,
fontSize: 10.sp,
),
),
if (badgeText != null)
Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w,
vertical: 4.h,
),
decoration: BoxDecoration(
color: Colors.yellowAccent,
borderRadius: BorderRadius.circular(12),
),
child: Text(
badgeText!,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 10,
),
),
),
],
),
SizedBox(height: 9.h,),
Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.bold,
),
),
Text(
subtitle,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 12.sp,
),
),
SizedBox(height: 8.h),
IntrinsicWidth(
child: Container(
height: 27.h,
decoration: BoxDecoration(
color: buttonColor!.withOpacity(opacity!),
borderRadius: BorderRadius.circular(35.r),
),
padding: EdgeInsets.symmetric(
vertical: 5.h,
horizontal: 12.w,
),
child: Row(
children: [
CustomText(text: buttonText, color: themeColor,size: 12.sp,),
SizedBox(width: 6.w,),
Icon(Icons.arrow_forward_outlined, size: 18.sp,color: themeColor,)
],
),
),
),
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,165 @@
import 'package:citycards_customer/common_packages/custom_text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class PassCardView extends StatelessWidget {
final Color? themeColor;
final String? city;
final int? adultCount;
final int? childCount;
final String? cardType;
const PassCardView({
super.key,
this.themeColor,
this.city,
this.adultCount,
this.childCount,
this.cardType,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: themeColor ?? Color(0xFFF95FAF)),
borderRadius: BorderRadius.circular(8.r),
),
child: Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.r),
bottomLeft: Radius.circular(8.r)
),
child: Image.asset(
"assets/images/card_banner.png",
scale: 4,
width: 103.w,
height:140.h,
fit: BoxFit.cover,
),
),
SizedBox(width: 6.66.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
text: "Melbourne",
weight: FontWeight.w500,
size: 16.sp,
),
Row(
children: [
Text(
"From ",
style: TextStyle(
color: Colors.black.withOpacity(0.6),
fontSize: 11.sp,
fontWeight: FontWeight.w400,
),
),
Text(
"\$80",
style: TextStyle(
color:themeColor,
fontWeight: FontWeight.w500,
fontSize: 24.sp,
),
),
Text(
" /Adult",
style: TextStyle(
color: Colors.black.withOpacity(0.8),
fontSize: 11.sp,
fontWeight: FontWeight.w400,
),
),
],
),
Row(
children: [
Text(
"and ",
style: TextStyle(
color: Colors.black.withOpacity(0.6),
fontSize: 11.sp,
fontWeight: FontWeight.w400,
),
),
Text(
"\$10",
style: TextStyle(
color: themeColor,
fontWeight: FontWeight.w500,
fontSize: 24.sp,
),
),
Text(
" /child",
style: TextStyle(
color: Colors.black.withOpacity(0.8),
fontSize: 11.sp,
fontWeight: FontWeight.w400,
),
),
],
),
SizedBox(
width: 193.w,
child: CustomText(
text:
"Dive into an extensive selection of thrilling destinations!",
color: Color(0xFF000000).withOpacity(0.6),
size: 11.sp,
),
),
],
),
],
),
Container(
width: 35.w,
height: 140.h,
decoration: BoxDecoration(
color: themeColor,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(8.r),
topRight: Radius.circular(8.r),
),
),
child: RotatedBox(
quarterTurns: -1,
child: Center(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: "Flexi ",
style: TextStyle(color: Colors.white, fontSize: 16.sp),
),
TextSpan(
text: "Card",
style: TextStyle(color: Colors.white, fontSize: 12.sp),
),
],
),
),
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,181 @@
import 'package:citycards_customer/common_packages/custom_filled_button.dart';
import 'package:citycards_customer/core/route_constants.dart';
import 'package:flutter/material.dart';
class PaymentCard extends StatefulWidget {
final String city;
final String tag;
final double oldPrice;
final double newPrice;
const PaymentCard({
super.key,
required this.city,
required this.tag,
required this.oldPrice,
required this.newPrice,
});
@override
State<PaymentCard> createState() => _PaymentCardState();
}
class _PaymentCardState extends State<PaymentCard> {
int adults = 1;
int children = 1;
@override
Widget build(BuildContext context) {
return Container(
width: 320,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.pinkAccent, width: 1.2),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.pinkAccent.withOpacity(0.1),
blurRadius: 10,
spreadRadius: 2,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Title
Text(
widget.city,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 6),
// Tag
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
decoration: BoxDecoration(
color: Color(0xFFF95FAF),
borderRadius: BorderRadius.circular(20),
),
child: Text(
widget.tag,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 16),
// Adult Counter
_buildCounterRow("No. of Adults", adults, (val) {
setState(() => adults = val);
}),
const SizedBox(height: 10),
// Children Counter
_buildCounterRow("No. of Children", children, (val) {
setState(() => children = val);
}),
const Divider(height: 30, thickness: 1),
// Price section
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"You Pay",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
Row(
children: [
Text(
"\$${widget.oldPrice.toStringAsFixed(0)}",
style: const TextStyle(
color: Colors.grey,
fontSize: 14,
decoration: TextDecoration.lineThrough,
),
),
const SizedBox(width: 8),
Text(
"\$${widget.newPrice.toStringAsFixed(0)}",
style: const TextStyle(
color: Color(0xFFF95F62),
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
],
),
],
),
const SizedBox(height: 20),
// Proceed Button
CustomFilledButton(
onTap: () {
Navigator.of(
context,
rootNavigator: true,
).pushNamed(RouteConstants.checkout);
},
label: "Proceed to Pay",
),
],
),
);
}
Widget _buildCounterRow(String label, int value, Function(int) onChanged) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontSize: 15)),
Row(
children: [
_circleButton(Icons.remove, () {
if (value > 0) onChanged(value - 1);
}),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Text(
"$value",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
_circleButton(Icons.add, () {
onChanged(value + 1);
}),
],
),
],
);
}
Widget _circleButton(IconData icon, VoidCallback onTap) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
child: Container(
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFFF95F62),
),
padding: const EdgeInsets.all(4),
child: Icon(icon, color: Colors.white, size: 18),
),
);
}
}

View File

@@ -0,0 +1,335 @@
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:citycards_customer/common_packages/custom_dashed_line.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CheckoutView extends StatelessWidget {
const CheckoutView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w),
child: Column(
children: [
CommonAppBar(
isWhiteLogo: false,
isProfilePage: false,
showCart: false,
),
Row(
children: [
Icon(Icons.arrow_back),
SizedBox(width: 8.w),
CustomText(text: "Checkout", size: 12.sp),
],
),
SizedBox(height: 22.h),
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Color(0xFFF95FAF).withOpacity(0.2),
),
borderRadius: BorderRadius.circular(8.r),
),
child: Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.r),
bottomLeft: Radius.circular(8.r),
),
child: Image.asset(
"assets/images/card_banner.png",
scale: 4,
width: 103.w,
height: 160.h,
fit: BoxFit.cover,
),
),
SizedBox(width: 6.66.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
text: "Melbourne",
weight: FontWeight.w500,
size: 16.sp,
),
SizedBox(height: 8.h),
CustomText(
text: "2 Days",
color: Color(0xFF8E8E8E),
size: 12.sp,
),
SizedBox(height: 12.h),
SizedBox(
width: MediaQuery.of(context).size.width * .5,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
'assets/icons/adult.png',
scale: 4,
),
SizedBox(width: 4.w),
CustomText(
text: "3 adults",
color: Color(0xFF8E8E8E),
size: 12.sp,
),
],
),
Row(
children: [
Image.asset(
'assets/icons/qty.png',
scale: 4,
),
SizedBox(width: 4.w),
CustomText(
text: "Qty : 2",
color: Color(0xFF8E8E8E),
size: 12.sp,
),
],
),
],
),
),
SizedBox(height: 7.h),
Row(
children: [
Image.asset(
"assets/icons/kid.png",
scale: 4,
),
SizedBox(width: 4.w),
CustomText(
text: "3 Kids",
color: Color(0xFF8E8E8E),
size: 12.sp,
),
SizedBox(width: 53.w),
CustomText(
text: "\$49.50",
size: 24.sp,
weight: FontWeight.w500,
color: Color(0xFFF95F62),
),
],
),
SizedBox(
width: 193.w,
child: CustomText(
text:
"Dive into an extensive selection of thrilling destinations!",
color: Color(0xFF000000).withOpacity(0.6),
size: 11.sp,
),
),
],
),
],
),
Container(
width: 35.w,
height: 160.h,
decoration: BoxDecoration(
color: Color(0xFFF95FAF),
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(8.r),
topRight: Radius.circular(8.r),
),
),
child: RotatedBox(
quarterTurns: -1,
child: Center(
child: RichText(
text: TextSpan(
children: [
TextSpan(
text: "Flexi ",
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
),
),
TextSpan(
text: "Card",
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
),
],
),
),
),
),
),
],
),
),
),
SizedBox(height: 15.h),
Container(
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 12.h,
),
decoration: BoxDecoration(
color: Color(0xFFFFF5F5),
borderRadius: BorderRadius.circular(8.r),
border: Border.all(
color: Color(0xFFBB474A).withOpacity(0.4),
width: 0.8,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(
text: "Get 10% off on your first trip",
color: Color(0xFF262626),
size: 14.sp,
),
SizedBox(height: 7.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
CustomText(
text: "View all coupons",
color: Color(0xFFF95F62),
size: 12,
),
SizedBox(width: 3.w),
Icon(Icons.arrow_right, color: Color(0xFFF95F62)),
],
),
],
),
const Spacer(),
Container(
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 10.h,
),
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFF95F62)),
borderRadius: BorderRadius.circular(8.r),
),
child: CustomText(
text: "Apply",
color: Color(0xFFF95F62),
size: 14.sp,
),
),
],
),
),
SizedBox(height: 15.h),
DashedDivider(
color: Color(0xFFACACAC),
thickness: 1.h,
dashLength: 4,
dashSpace: 4,
),
SizedBox(height: 10.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomText(text: "Subtotal", size: 14.sp),
CustomText(
text: "\$49.50",
size: 14.sp,
weight: FontWeight.w500,
),
],
),
SizedBox(height: 14.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomText(text: "Discount", size: 14.sp),
CustomText(
text: "-7.20%",
size: 14.sp,
weight: FontWeight.w500,
),
],
),
SizedBox(height: 10.h),
DashedDivider(
color: Color(0xFFACACAC),
thickness: 1.h,
dashLength: 4,
dashSpace: 4,
),
SizedBox(height: 10.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomText(text: 'Total', size: 14.sp),
SizedBox(height: 4.h),
CustomText(
text: "Including \$2.24 in taxes",
size: 12.sp,
color: Colors.black.withOpacity(0.6),
),
],
),
),
CustomText(
text: "\$42.60",
size: 24.sp,
weight: FontWeight.w500,
),
],
),
const Spacer(),
CustomFilledButton(
onTap: () {},
label: "Login to Checkout",
),
SizedBox(height: 25.h,)
],
),
),
),
);
}
}

View File

@@ -4,10 +4,16 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../core/route_constants.dart';
class CommonAppBar extends StatelessWidget {
const CommonAppBar({super.key, required this.isWhiteLogo, required this.isProfilePage});
const CommonAppBar({
super.key,
required this.isWhiteLogo,
required this.isProfilePage,
this.showCart = true,
});
final bool isWhiteLogo;
final bool isProfilePage;
final bool? showCart;
@override
Widget build(BuildContext context) {
@@ -17,45 +23,53 @@ class CommonAppBar extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Image.asset(
isWhiteLogo ? "assets/logo/logo_city_cards_white.png" :"assets/logo/logo_city_cards.png",
scale: 4,),
isWhiteLogo
? "assets/logo/logo_city_cards_white.png"
: "assets/logo/logo_city_cards.png",
scale: 4,
),
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: Image.asset(
"assets/icons/shopping_cart.png",
height: 20.h,
),
),
SizedBox(width: 8.w),
if(!isProfilePage)
GestureDetector(
onTap: (){
Navigator.of(context, rootNavigator: true)
.pushNamed(RouteConstants.profile);
},
child: CircleAvatar(
backgroundColor: Color(0xffFFDFDF),
backgroundImage:
AssetImage("assets/images/profile_img.png")),
if(showCart!)
Container(
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: Image.asset(
"assets/icons/shopping_cart.png",
height: 20.h,
),
),
SizedBox(width: 8.w),
if (!isProfilePage)
GestureDetector(
onTap: () {
Navigator.of(
context,
rootNavigator: true,
).pushNamed(RouteConstants.profile);
},
child: CircleAvatar(
backgroundColor: Color(0xffFFDFDF),
backgroundImage: AssetImage(
"assets/images/profile_img.png",
),
),
),
],
),
],
),
if(!isWhiteLogo)
Column(
children: [
SizedBox(height: 12.h),
Divider(height: 1.h, color: Color(0xFFD9D9D9)),
SizedBox(height: 22.h),
],
)
if (!isWhiteLogo)
Column(
children: [
SizedBox(height: 12.h),
Divider(height: 1.h, color: Color(0xFFD9D9D9)),
SizedBox(height: 22.h),
],
),
],
);
}

View File

@@ -0,0 +1,255 @@
import 'package:flutter/material.dart';
class DashedDivider extends StatelessWidget {
/// The divider's height extent.
///
/// The divider itself is always drawn as a horizontal line that is centered
/// within the height specified by this value.
///
/// If this is null, then the [DividerThemeData.space] is used. If that is
/// also null, then this defaults to 16.0.
final double? height;
/// The thickness of the line drawn within the divider.
///
/// A divider with a [thickness] of 0.0 is always drawn as a line with a
/// height of exactly one device pixel.
///
/// If this is null, then the [DividerThemeData.thickness] is used. If
/// that is also null, then this defaults to 0.0.
final double? thickness;
/// The amount of empty space to the leading edge of the divider.
///
/// If this is null, then the [DividerThemeData.indent] is used. If that is
/// also null, then this defaults to 0.0.
final double? indent;
/// The amount of empty space to the trailing edge of the divider.
///
/// If this is null, then the [DividerThemeData.endIndent] is used. If that is
/// also null, then this defaults to 0.0.
final double? endIndent;
/// The color to use when painting the line.
///
/// If this is null, then the [DividerThemeData.color] is used. If that is
/// also null, then [ThemeData.dividerColor] is used.
final Color? color;
/// The length of each dash in the dashed line.
final double dashLength;
/// The space between each dash in the dashed line.
final double dashSpace;
/// The offset along the main axis for the starting position of the dashes.
///
/// This value determines how far from the start the first dash will be drawn,
/// allowing for fine-tuning the positioning of the dashed line. A positive value
/// shifts the dashes forward, while a negative value moves them backward along
/// the main axis.
///
/// The default value is 0.0, meaning the dashes start at the beginning of the line.
final double mainAxisOffset;
const DashedDivider({
super.key,
this.height,
this.thickness,
this.color,
this.indent,
this.endIndent,
this.dashLength = 5,
this.dashSpace = 5,
this.mainAxisOffset = 0.0,
}) : assert(height == null || height >= 0.0),
assert(thickness == null || thickness >= 0.0),
assert(indent == null || indent >= 0.0),
assert(endIndent == null || endIndent >= 0.0);
@override
Widget build(BuildContext context) {
final theme = DividerThemeProvider.of(context).withDefaults(
height: height,
thickness: thickness,
indent: indent,
endIndent: endIndent,
color: color,
);
return Container(
margin: EdgeInsets.only(left: theme.indent, right: theme.endIndent),
height: theme.height,
width: double.infinity,
child: CustomPaint(
painter: DashedLinePainter(
color: theme.color,
thickness: theme.thickness,
dashLength: dashLength,
dashSpace: dashSpace,
mainAxisOffset: mainAxisOffset,
),
),
);
}
}
class DashedLinePainter extends CustomPainter {
final Color color;
final double thickness;
final double dashLength;
final double dashSpace;
final bool isVertical;
final double mainAxisOffset;
DashedLinePainter({
required this.color,
required this.thickness,
required this.dashLength,
required this.dashSpace,
this.isVertical = false,
this.mainAxisOffset = 0.0,
});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..strokeWidth = thickness <= 0 ? 1 : thickness;
final mainAxisSize = _getMainAxisSize(size);
final crossAxisPosition = _getCrossAxisPosition(size);
canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height));
double currentPosition = mainAxisOffset;
while (currentPosition < mainAxisSize) {
double nextDashEnd = currentPosition + dashLength;
if (nextDashEnd > mainAxisSize) {
nextDashEnd = mainAxisSize;
}
final start = _calculateStartOffset(crossAxisPosition, currentPosition);
final end = _calculateEndOffset(crossAxisPosition, nextDashEnd);
canvas.drawLine(start, end, paint);
currentPosition += dashLength + dashSpace;
}
}
double _getMainAxisSize(Size size) {
return isVertical ? size.height : size.width;
}
double _getCrossAxisPosition(Size size) {
return isVertical ? size.width / 2 : size.height / 2;
}
Offset _calculateStartOffset(
double crossAxisPosition, double currentPosition) {
return isVertical
? Offset(crossAxisPosition, currentPosition)
: Offset(currentPosition, crossAxisPosition);
}
Offset _calculateEndOffset(double crossAxisPosition, double nextDashEnd) {
return isVertical
? Offset(crossAxisPosition, nextDashEnd)
: Offset(nextDashEnd, crossAxisPosition);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
class DividerThemeProvider {
final DividerThemeData _dividerTheme;
final ThemeData _theme;
final DividerThemeData _defaults;
double? _width;
double? _height;
double? _thickness;
double? _indent;
double? _endIndent;
Color? _color;
DividerThemeProvider._(BuildContext context)
: _dividerTheme = DividerTheme.of(context),
_theme = Theme.of(context),
_defaults = Theme.of(context).useMaterial3
? _DividerDefaultsM3(context)
: _DividerDefaultsM2(context);
static DividerThemeProvider of(BuildContext context) {
return DividerThemeProvider._(context);
}
DividerThemeProvider withDefaults({
double? width,
double? height,
double? thickness,
double? indent,
double? endIndent,
Color? color,
}) {
_width = width ?? _width;
_height = height ?? _height;
_thickness = thickness ?? _thickness;
_indent = indent ?? _indent;
_endIndent = endIndent ?? _endIndent;
_color = color ?? _color;
return this;
}
double get width => _width ?? _dividerTheme.space ?? _defaults.space!;
double get height => _height ?? _dividerTheme.space ?? _defaults.space!;
double get thickness =>
_thickness ?? _dividerTheme.thickness ?? _defaults.thickness!;
double get indent => _indent ?? _dividerTheme.indent ?? _defaults.indent!;
double get endIndent =>
_endIndent ?? _dividerTheme.endIndent ?? _defaults.endIndent!;
Color get color =>
_color ?? _dividerTheme.color ?? _defaults.color ?? _theme.dividerColor;
}
class _DividerDefaultsM3 extends DividerThemeData {
const _DividerDefaultsM3(this.context)
: super(
space: 16,
thickness: 1.0,
indent: 0,
endIndent: 0,
);
final BuildContext context;
@override
Color? get color => Theme.of(context).colorScheme.outlineVariant;
}
class _DividerDefaultsM2 extends DividerThemeData {
const _DividerDefaultsM2(this.context)
: super(
space: 16,
thickness: 0,
indent: 0,
endIndent: 0,
);
final BuildContext context;
@override
Color? get color => Theme.of(context).dividerColor;
}

View File

@@ -1,6 +1,7 @@
import 'package:citycards_customer/Profile/profile_page_view.dart';
import 'package:citycards_customer/attraction_details/attraction_details_view.dart';
import 'package:citycards_customer/buy_a_pass/buy_pass_view.dart';
import 'package:citycards_customer/buy_a_pass/view/buy_pass_view.dart';
import 'package:citycards_customer/checkout/checkout_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';
@@ -125,6 +126,11 @@ class AppRouter {
return MaterialPageRoute(builder: (_) {
return BuyPassView();
});
case RouteConstants.checkout:
return MaterialPageRoute(builder: (_){
return CheckoutView();
});
default:
return MaterialPageRoute(
builder: (_) =>

View File

@@ -33,6 +33,7 @@ class RouteConstants {
/**************************** By Pass Page Page *****************************************/
static const String buyPass ='/buyPass';
static const String checkout ='/checkout';