/// Overlays a highlight on the receipt photo for a parsed line item. library; import 'dart:io'; import 'package:flutter/material.dart'; import '../models/receipt_bounds.dart'; class ReceiptHighlightOverlay extends StatelessWidget { const ReceiptHighlightOverlay({ super.key, required this.imagePath, required this.bounds, this.maxHeight = 280, }); final String imagePath; final ReceiptBounds bounds; final double maxHeight; @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return ClipRRect( borderRadius: BorderRadius.circular(12), child: LayoutBuilder( builder: (context, constraints) { return Stack( fit: StackFit.passthrough, children: [ Image.file( File(imagePath), fit: BoxFit.contain, height: maxHeight, width: constraints.maxWidth, ), Positioned.fill( child: CustomPaint( painter: _BoundsPainter( bounds: bounds, color: scheme.primary.withValues(alpha: 0.35), stroke: scheme.primary, ), ), ), ], ); }, ), ); } } class _BoundsPainter extends CustomPainter { _BoundsPainter({ required this.bounds, required this.color, required this.stroke, }); final ReceiptBounds bounds; final Color color; final Color stroke; @override void paint(Canvas canvas, Size size) { if (bounds.isEmpty) return; final rect = Rect.fromLTRB( bounds.left * size.width, bounds.top * size.height, bounds.right * size.width, bounds.bottom * size.height, ); canvas.drawRect(rect, Paint()..color = color); canvas.drawRect( rect, Paint() ..color = stroke ..style = PaintingStyle.stroke ..strokeWidth = 2, ); } @override bool shouldRepaint(covariant _BoundsPainter oldDelegate) { return oldDelegate.bounds != bounds || oldDelegate.color != color || oldDelegate.stroke != stroke; } }