cashumit/lib/widgets/dashed_border.dart

94 lines
2.3 KiB
Dart
Raw Permalink Normal View History

feat: multi-grup sumber, batch send, & perbaikan tip Multi-Grup Sumber & UI: - Model ReceiptGroup: satu struk bisa punya banyak grup sumber ( revenue) - Tombol "Tambah Sumber" dengan dashed border (max 10 grup) - Hapus label "Dari:" di SourceAccountSelector (chip saja) - Default 1 grup kosong di ReceiptProvider constructor & _resetState Akun Tujuan ke Config: - Pindah DestinationAccountSelector dari receipt body ke Config screen - Persist & load via SharedPreferences (permanen, tidak di-reset) - Dropdown auto-load dari cache/mirror saat buka Config - Tombol "Sinkronisasi Manual" sekarang simpan ke mirror & refresh dropdown Batch Send via SubmissionTask: - Model SubmissionTask: konteks per-grup (group/tip + destination + date) - TransactionSubmissionDialog refactor: terima List<SubmissionTask> - Helper buildTasksFromReceipt & buildTasksFromAllUnsubmittedReceipts - LocalReceiptsScreen: tombol "Kirim Semua Nota" + post-process status Bug Fix: - copyWith sentinel pattern: field nullable (tipSourceAccount, destinationAccount, fireflyUrl, accessToken) kini bisa di-set ke null - Tip dapat dibersihkan dengan "Batal Tip" & reset antar transaksi - Hapus tombol "Muat Akun" (redundan dgn Sinkronisasi Manual) Lainnya: - Date picker untuk tanggal transaksi - Font courier prime caching via CourierStyles - Perf: compute() untuk decode image, cancel stream bluetooth, fix rebuild loop - External link: <queries> AndroidManifest + skip canLaunchUrl - Layout cetak: hapus Ke:, sederhanakan separator grup, hapus TIP: TIDAK - Hapus TransactionLinksScreen, ganti inline copy/open di popup
2026-06-27 20:18:51 +07:00
import 'package:flutter/material.dart';
/// Widget yang menggambar border putus-putus (dashed) mengelilingi child.
/// Untuk tombol "template card" gaya struk.
class DashedBorder extends StatelessWidget {
final Widget child;
final Color color;
final double strokeWidth;
final double dashLength;
final double gapLength;
final double radius;
final EdgeInsets padding;
const DashedBorder({
super.key,
required this.child,
this.color = Colors.grey,
this.strokeWidth = 1.0,
this.dashLength = 5.0,
this.gapLength = 3.0,
this.radius = 4.0,
this.padding = const EdgeInsets.all(8.0),
});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _DashedBorderPainter(
color: color,
strokeWidth: strokeWidth,
dashLength: dashLength,
gapLength: gapLength,
radius: radius,
),
child: Padding(
padding: padding,
child: child,
),
);
}
}
class _DashedBorderPainter extends CustomPainter {
final Color color;
final double strokeWidth;
final double dashLength;
final double gapLength;
final double radius;
_DashedBorderPainter({
required this.color,
required this.strokeWidth,
required this.dashLength,
required this.gapLength,
required this.radius,
});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
// Bangun path rounded rect
final rrect = RRect.fromRectAndRadius(
Offset.zero & size,
Radius.circular(radius),
);
// Gunakan PathMetric untuk dash effect pada rounded rect
final path = Path()..addRRect(rrect);
final metrics = path.computeMetrics();
for (final metric in metrics) {
double distance = 0;
while (distance < metric.length) {
final next = distance + dashLength;
final extract = metric.extractPath(distance, next);
canvas.drawPath(extract, paint);
distance = next + gapLength;
}
}
}
@override
bool shouldRepaint(covariant _DashedBorderPainter oldDelegate) =>
color != oldDelegate.color ||
strokeWidth != oldDelegate.strokeWidth ||
dashLength != oldDelegate.dashLength ||
gapLength != oldDelegate.gapLength ||
radius != oldDelegate.radius;
}