169 lines
4.3 KiB
Dart
169 lines
4.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../../../common/theme/theme.dart';
|
|
|
|
/// Kartu berbentuk tiket: sobekan kiri ([stub]) dan isi kanan ([body]),
|
|
/// dipisah garis putus-putus.
|
|
class TicketCard extends StatelessWidget {
|
|
const TicketCard({
|
|
super.key,
|
|
required this.stub,
|
|
required this.body,
|
|
required this.onTap,
|
|
this.active = true,
|
|
});
|
|
|
|
final Widget stub;
|
|
final Widget body;
|
|
final VoidCallback onTap;
|
|
|
|
/// Latar sobekan berwarna untuk voucher yang bisa ditukar / dipakai.
|
|
final bool active;
|
|
|
|
static const double stubWidth = 84;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return PhysicalShape(
|
|
clipper: const _TicketClipper(notchX: stubWidth + 0.5),
|
|
color: AppColor.white,
|
|
elevation: 2,
|
|
shadowColor: Colors.black.withOpacity(0.25),
|
|
child: Material(
|
|
type: MaterialType.transparency,
|
|
child: InkWell(
|
|
onTap: onTap,
|
|
child: IntrinsicHeight(
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Container(
|
|
width: stubWidth,
|
|
color: active
|
|
? AppColor.primary.withOpacity(0.06)
|
|
: AppColor.background,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 16,
|
|
),
|
|
child: stub,
|
|
),
|
|
const Padding(
|
|
padding: EdgeInsets.symmetric(vertical: 14),
|
|
child: CustomPaint(
|
|
size: Size(1, double.infinity),
|
|
painter: _VerticalDashPainter(),
|
|
),
|
|
),
|
|
Expanded(child: body),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Persegi panjang membulat dengan lekuk setengah lingkaran di atas dan
|
|
/// bawah pada posisi [notchX], tempat garis sobekan tiket.
|
|
class _TicketClipper extends CustomClipper<Path> {
|
|
const _TicketClipper({required this.notchX});
|
|
|
|
final double notchX;
|
|
|
|
static const double _radius = 16;
|
|
static const double _notchRadius = 8;
|
|
|
|
@override
|
|
Path getClip(Size size) {
|
|
final card = Path()
|
|
..addRRect(
|
|
RRect.fromRectAndRadius(
|
|
Offset.zero & size,
|
|
const Radius.circular(_radius),
|
|
),
|
|
);
|
|
final notches = Path()
|
|
..addOval(
|
|
Rect.fromCircle(center: Offset(notchX, 0), radius: _notchRadius),
|
|
)
|
|
..addOval(
|
|
Rect.fromCircle(
|
|
center: Offset(notchX, size.height),
|
|
radius: _notchRadius,
|
|
),
|
|
);
|
|
return Path.combine(PathOperation.difference, card, notches);
|
|
}
|
|
|
|
@override
|
|
bool shouldReclip(covariant _TicketClipper oldClipper) =>
|
|
oldClipper.notchX != notchX;
|
|
}
|
|
|
|
class _VerticalDashPainter extends CustomPainter {
|
|
const _VerticalDashPainter();
|
|
|
|
static const double _dash = 5;
|
|
static const double _gap = 4;
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final paint = Paint()
|
|
..color = AppColor.border
|
|
..strokeWidth = size.width;
|
|
final x = size.width / 2;
|
|
|
|
for (double y = 0; y < size.height; y += _dash + _gap) {
|
|
final end = (y + _dash).clamp(0, size.height).toDouble();
|
|
canvas.drawLine(Offset(x, y), Offset(x, end), paint);
|
|
}
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
|
|
}
|
|
|
|
/// Isi sobekan: gambar voucher dan EnakPoint yang ditukar.
|
|
class TicketStub extends StatelessWidget {
|
|
const TicketStub({
|
|
super.key,
|
|
required this.image,
|
|
required this.pointCost,
|
|
this.active = true,
|
|
});
|
|
|
|
final Widget image;
|
|
final String pointCost;
|
|
final bool active;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
image,
|
|
const SizedBox(height: 8),
|
|
FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
child: Text(
|
|
pointCost,
|
|
style: AppStyle.md.copyWith(
|
|
color: active ? AppColor.primary : AppColor.textSecondary,
|
|
fontWeight: FontWeight.w700,
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
'EnakPoint',
|
|
style: AppStyle.xs.copyWith(
|
|
fontSize: 10,
|
|
color: AppColor.textSecondary,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|