275 lines
9.5 KiB
Dart
275 lines
9.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import '../models/my_passes_model.dart';
|
|
|
|
class PassTicketCard extends StatelessWidget {
|
|
final MyPassData pass;
|
|
|
|
const PassTicketCard({super.key, required this.pass});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final double cardWidth = MediaQuery.of(context).size.width - 32.w;
|
|
final double topSectionHeight = 105.h;
|
|
final double bottomSectionHeight = 50.h;
|
|
final double cardHeight = topSectionHeight + bottomSectionHeight;
|
|
|
|
return SizedBox(
|
|
width: cardWidth,
|
|
child: CustomPaint(
|
|
painter: _TicketBackgroundPainter(
|
|
cornerRadius: 16.r,
|
|
notchRadius: 9.r,
|
|
dividerY: topSectionHeight,
|
|
borderColor: Colors.white,
|
|
shadowColor: Colors.black.withOpacity(0.08),
|
|
),
|
|
child: ClipPath(
|
|
clipper: _TicketClipper(
|
|
cornerRadius: 16.r,
|
|
notchRadius: 9.r,
|
|
dividerY: topSectionHeight,
|
|
),
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h),
|
|
child: Column(
|
|
children: [
|
|
SizedBox(
|
|
height: topSectionHeight - 12.h,
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(10.r),
|
|
child: Image.network(
|
|
pass.city?.bannerImage ?? '',
|
|
height: 80.h,
|
|
width: 80.w,
|
|
fit: BoxFit.cover,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return Container(
|
|
height: 80.h,
|
|
width: 80.w,
|
|
color: Colors.grey[300],
|
|
child: Icon(Icons.image, size: 40),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
SizedBox(width: 10.w),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
if (pass.bookingStatus == "active")
|
|
Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 8.w, vertical: 3.h),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xff439F6E),
|
|
borderRadius: BorderRadius.circular(30.r),
|
|
),
|
|
child: Text(
|
|
"Active",
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.white,
|
|
fontSize: 10.sp,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
),
|
|
SizedBox(width: 8.w),
|
|
Text(
|
|
"${pass.noOfDays ?? 0} Days",
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.black87,
|
|
fontSize: 12.sp,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 10.h),
|
|
Text(
|
|
"${(pass.cardMode?.isNotEmpty ?? false)
|
|
? pass.cardMode![0].toUpperCase() + pass.cardMode!.substring(1)
|
|
: ''} Card",
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w600,
|
|
fontSize: 18.sp,
|
|
height: 1.1,
|
|
),
|
|
),
|
|
SizedBox(height: 4.h),
|
|
Text(
|
|
"Adults-${pass.totalAdult ?? 0} • Kids-${pass.totalChild ?? 0}",
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.black54,
|
|
fontSize: 11.sp,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
CircleAvatar(
|
|
radius: 20.r,
|
|
backgroundColor: Color(0xffFEE7E7),
|
|
child: Image.asset(
|
|
"assets/images/qr_image.png",
|
|
scale: 6,
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 15.h),
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 4.w),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
"Valid Till: ${pass.validUpto ?? ''}",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 11.sp,
|
|
color: Colors.black,
|
|
fontWeight: FontWeight.w400),
|
|
),
|
|
Text(
|
|
pass.city?.name ?? '',
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w500,
|
|
fontSize: 13.sp,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TicketClipper extends CustomClipper<Path> {
|
|
final double cornerRadius;
|
|
final double notchRadius;
|
|
final double dividerY;
|
|
|
|
_TicketClipper({
|
|
required this.cornerRadius,
|
|
required this.notchRadius,
|
|
required this.dividerY,
|
|
});
|
|
|
|
@override
|
|
Path getClip(Size size) {
|
|
final rrectPath = Path()
|
|
..addRRect(RRect.fromRectAndRadius(
|
|
Rect.fromLTWH(0, 0, size.width, size.height),
|
|
Radius.circular(cornerRadius),
|
|
));
|
|
|
|
final cuts = Path()
|
|
..addOval(Rect.fromCircle(
|
|
center: Offset(0, dividerY), radius: notchRadius))
|
|
..addOval(Rect.fromCircle(
|
|
center: Offset(size.width, dividerY), radius: notchRadius));
|
|
|
|
return Path.combine(PathOperation.difference, rrectPath, cuts);
|
|
}
|
|
|
|
@override
|
|
bool shouldReclip(covariant _TicketClipper old) =>
|
|
cornerRadius != old.cornerRadius ||
|
|
notchRadius != old.notchRadius ||
|
|
dividerY != old.dividerY;
|
|
}
|
|
|
|
class _TicketBackgroundPainter extends CustomPainter {
|
|
final double cornerRadius;
|
|
final double notchRadius;
|
|
final double dividerY;
|
|
final Color borderColor;
|
|
final Color shadowColor;
|
|
|
|
_TicketBackgroundPainter({
|
|
required this.cornerRadius,
|
|
required this.notchRadius,
|
|
required this.dividerY,
|
|
required this.borderColor,
|
|
required this.shadowColor,
|
|
});
|
|
|
|
Path _ticketPath(Size size) {
|
|
final clipper = _TicketClipper(
|
|
cornerRadius: cornerRadius,
|
|
notchRadius: notchRadius,
|
|
dividerY: dividerY,
|
|
);
|
|
return clipper.getClip(size);
|
|
}
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final path = _ticketPath(size);
|
|
|
|
canvas.save();
|
|
canvas.translate(0, 2);
|
|
final shadowPaint = Paint()
|
|
..color = Colors.black.withOpacity(0.10)
|
|
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6);
|
|
canvas.drawPath(path, shadowPaint);
|
|
canvas.restore();
|
|
|
|
final ambientShadowPaint = Paint()
|
|
..color = Colors.black.withOpacity(0.04)
|
|
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 12);
|
|
canvas.drawPath(path, ambientShadowPaint);
|
|
|
|
final fillPaint = Paint()
|
|
..style = PaintingStyle.fill
|
|
..color = const Color(0xffFFFBFB);
|
|
canvas.drawPath(path, fillPaint);
|
|
|
|
final strokePaint = Paint()
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 0.8
|
|
..color = const Color(0xffE5E5E5);
|
|
canvas.drawPath(path, strokePaint);
|
|
|
|
final dashPaint = Paint()
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 1
|
|
..color = const Color(0xff787878);
|
|
|
|
const double dashWidth = 4;
|
|
const double dashSpace = 4;
|
|
double startX = 12;
|
|
final double endX = size.width - 12;
|
|
|
|
while (startX < endX) {
|
|
final double currentEnd = (startX + dashWidth).clamp(0, endX);
|
|
canvas.drawLine(
|
|
Offset(startX, dividerY),
|
|
Offset(currentEnd, dividerY),
|
|
dashPaint,
|
|
);
|
|
startX += dashWidth + dashSpace;
|
|
}
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant _TicketBackgroundPainter oldDelegate) {
|
|
return cornerRadius != oldDelegate.cornerRadius ||
|
|
notchRadius != oldDelegate.notchRadius ||
|
|
dividerY != oldDelegate.dividerY ||
|
|
borderColor != oldDelegate.borderColor ||
|
|
shadowColor != oldDelegate.shadowColor;
|
|
}
|
|
} |