Completed By a Pass & Checkout View
This commit is contained in:
@@ -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,)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
199
lib/buy_a_pass/view/buy_pass_view.dart
Normal file
199
lib/buy_a_pass/view/buy_pass_view.dart
Normal 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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
126
lib/buy_a_pass/widget/feature_table.dart
Normal file
126
lib/buy_a_pass/widget/feature_table.dart
Normal 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);
|
||||
}
|
||||
127
lib/buy_a_pass/widget/offer_card_view.dart
Normal file
127
lib/buy_a_pass/widget/offer_card_view.dart
Normal 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,)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
165
lib/buy_a_pass/widget/pass_card_view.dart
Normal file
165
lib/buy_a_pass/widget/pass_card_view.dart
Normal 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
181
lib/buy_a_pass/widget/payment_card_view.dart
Normal file
181
lib/buy_a_pass/widget/payment_card_view.dart
Normal 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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
335
lib/checkout/checkout_view.dart
Normal file
335
lib/checkout/checkout_view.dart
Normal 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,)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
255
lib/common_packages/custom_dashed_line.dart
Normal file
255
lib/common_packages/custom_dashed_line.dart
Normal 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;
|
||||
}
|
||||
@@ -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: (_) =>
|
||||
|
||||
@@ -33,6 +33,7 @@ class RouteConstants {
|
||||
/**************************** By Pass Page Page *****************************************/
|
||||
|
||||
static const String buyPass ='/buyPass';
|
||||
static const String checkout ='/checkout';
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user