cashumit/lib/widgets/receipt_tear_effect.dart

118 lines
3.4 KiB
Dart
Raw Normal View History

import 'package:flutter/material.dart';
import 'dart:math';
/// Widget untuk efek kertas struk tersobek di bagian atas
class ReceiptTearTop extends StatelessWidget {
final double? width;
const ReceiptTearTop({super.key, this.width});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final effectiveWidth = width ??
(constraints.maxWidth.isFinite ? constraints.maxWidth : 360.0);
return Container(
width: effectiveWidth,
color: Colors.grey[300],
child: CustomPaint(
size: Size(effectiveWidth, 20),
painter: _TearPainter(isTop: true),
),
);
},
);
}
}
/// Widget untuk efek kertas struk tersobek di bagian bawah
class ReceiptTearBottom extends StatelessWidget {
final double? width;
const ReceiptTearBottom({super.key, this.width});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final effectiveWidth = width ??
(constraints.maxWidth.isFinite ? constraints.maxWidth : 360.0);
return Container(
width: effectiveWidth,
color: Colors.grey[300],
child: CustomPaint(
size: Size(effectiveWidth, 20),
painter: _TearPainter(isTop: false),
),
);
},
);
}
}
/// CustomPainter untuk menggambar efek sobek
class _TearPainter extends CustomPainter {
final bool isTop;
_TearPainter({required this.isTop});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.fill;
final path = Path();
// Buat efek robekan dengan pola gigi proporsional terhadap lebar struk
final random = Random(123); // Seed tetap agar pola robekan konsisten
final segmentCount = (size.width / 18).round().clamp(15, 40);
final segmentWidth = size.width / segmentCount;
if (isTop) {
// Start from top left
path.moveTo(0, 0);
// Create tear effect with randomized peaks/valleys
// For top tear, peaks point upward (out of the paper)
for (int i = 0; i <= segmentCount; i++) {
final x = i * segmentWidth;
// Alternate between peaks and valleys
final y = (i % 2 == 0)
? -(2 + random.nextDouble() * 3) // Peak pointing upward (negative Y)
: 0.0; // Valley at top edge
path.lineTo(x, y);
}
// Close the path
path.lineTo(size.width, 0);
path.lineTo(size.width, size.height);
path.lineTo(0, size.height);
path.close();
} else {
// Start from bottom left
path.moveTo(0, size.height);
// Create tear effect with randomized peaks/valleys
// For bottom tear, peaks point downward (out of the paper)
for (int i = 0; i <= segmentCount; i++) {
final x = i * segmentWidth;
// Alternate between peaks and valleys
final y = (i % 2 == 0)
? size.height + (2 + random.nextDouble() * 3) // Peak pointing downward
: size.height.toDouble(); // Valley at bottom edge
path.lineTo(x, y);
}
// Close the path
path.lineTo(size.width, size.height);
path.lineTo(size.width, 0);
path.lineTo(0, 0);
path.close();
}
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}