import 'package:flutter/material.dart'; class InvoiceDetailView extends StatefulWidget { final Map invoice; const InvoiceDetailView({super.key, required this.invoice}); @override State createState() => _InvoiceDetailViewState(); } class _InvoiceDetailViewState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _slideAnimation; bool s1 = true; bool s2 = false; bool s3 = false; bool s4 = false; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 350), ); _slideAnimation = Tween(begin: const Offset(0, -0.1), end: Offset.zero) .animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut)); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } // ------------------------------------------------------------------ // HEADER SUMMARY CARD // ------------------------------------------------------------------ Widget headerCard(Map invoice, double scale) { return Container( width: double.infinity, padding: EdgeInsets.all(16 * scale), // tighter margin: EdgeInsets.only(bottom: 14 * scale), // closer decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.blue.shade400, Colors.indigo.shade600], ), borderRadius: BorderRadius.circular(16 * scale), boxShadow: [ BoxShadow( blurRadius: 8 * scale, offset: Offset(0, 3 * scale), color: Colors.indigo.withOpacity(.3), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Invoice #${invoice['invoice_number']}", style: TextStyle( fontSize: 20 * scale, color: Colors.white, fontWeight: FontWeight.bold, ), ), SizedBox(height: 4 * scale), Text( "Date: ${invoice['invoice_date']}", style: TextStyle(color: Colors.white70, fontSize: 13 * scale), ), SizedBox(height: 8 * scale), Container( padding: EdgeInsets.symmetric( vertical: 5 * scale, horizontal: 12 * scale), decoration: BoxDecoration( color: Colors.white.withOpacity(.2), borderRadius: BorderRadius.circular(40 * scale), ), child: Text( invoice['status'] ?? "Unknown", style: TextStyle( color: Colors.white, fontSize: 13 * scale, ), ), ) ], ), ); } // ------------------------------------------------------------------ // SECTION HEADER (Closer Spacing) // ------------------------------------------------------------------ Widget sectionHeader( String title, IconData icon, bool expanded, Function() tap, double scale) { return GestureDetector( onTap: tap, child: Container( padding: EdgeInsets.all(12 * scale), // tighter margin: EdgeInsets.only(bottom: 8 * scale), // closer decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.indigo.shade500, Colors.blue.shade400]), borderRadius: BorderRadius.circular(12 * scale), boxShadow: [ BoxShadow( blurRadius: 5 * scale, color: Colors.black.withOpacity(.15), offset: Offset(0, 2 * scale), ) ], ), child: Row( children: [ Icon(icon, color: Colors.white, size: 20 * scale), SizedBox(width: 8 * scale), Text( title, style: TextStyle( fontSize: 15 * scale, color: Colors.white, fontWeight: FontWeight.bold), ), const Spacer(), AnimatedRotation( turns: expanded ? .5 : 0, duration: const Duration(milliseconds: 250), child: Icon(Icons.keyboard_arrow_down, color: Colors.white, size: 20 * scale), ) ], ), ), ); } // ------------------------------------------------------------------ // SECTION BODY (Closer Spacing) // ------------------------------------------------------------------ Widget sectionBody(bool visible, List children, double scale) { return AnimatedCrossFade( duration: const Duration(milliseconds: 250), crossFadeState: visible ? CrossFadeState.showSecond : CrossFadeState.showFirst, firstChild: const SizedBox.shrink(), secondChild: SlideTransition( position: _slideAnimation, child: Container( width: double.infinity, padding: EdgeInsets.all(14 * scale), // tighter margin: EdgeInsets.only(bottom: 10 * scale), // closer decoration: BoxDecoration( color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(12 * scale), boxShadow: [ BoxShadow( blurRadius: 7 * scale, offset: Offset(0, 2 * scale), color: Colors.black.withOpacity(.07)) ], ), child: Column(children: children), ), ), ); } // ------------------------------------------------------------------ // DETAIL ROW (Closer Spacing) // ------------------------------------------------------------------ Widget detailRow(IconData icon, String label, dynamic value, double scale) { return Padding( padding: EdgeInsets.symmetric(vertical: 5 * scale), // closer child: Row( children: [ Icon(icon, size: 18 * scale, color: Colors.blueGrey), SizedBox(width: 8 * scale), Expanded( flex: 3, child: Text( label, style: TextStyle( color: Colors.grey.shade600, fontSize: 13 * scale, ), ), ), Expanded( flex: 4, child: Text( value?.toString() ?? "N/A", textAlign: TextAlign.right, style: TextStyle(fontSize: 14 * scale, fontWeight: FontWeight.bold), ), ) ], ), ); } // ------------------------------------------------------------------ // TIMELINE (Closer Spacing) // ------------------------------------------------------------------ Widget invoiceTimeline(double scale) { final steps = ["Invoice Created", "Payment Received", "Out for Delivery", "Completed"]; final icons = [Icons.receipt_long, Icons.payments, Icons.local_shipping, Icons.verified]; return Container( padding: EdgeInsets.all(16 * scale), margin: EdgeInsets.only(bottom: 16 * scale), // closer decoration: BoxDecoration( color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(14 * scale), boxShadow: [ BoxShadow( blurRadius: 7 * scale, color: Colors.black.withOpacity(.1), offset: Offset(0, 3 * scale), ) ], ), child: Column( children: List.generate(steps.length, (i) { return Padding( padding: EdgeInsets.symmetric(vertical: 4 * scale), // closer child: Row( children: [ Column( children: [ Icon(icons[i], size: 22 * scale, color: Colors.indigo), if (i < steps.length - 1) Container( height: 28 * scale, width: 2 * scale, color: Colors.indigo.shade300, ) ], ), SizedBox(width: 10 * scale), Expanded( child: Text( steps[i], style: TextStyle(fontSize: 14 * scale, fontWeight: FontWeight.w600), ), ) ], ), ); }), ), ); } // ------------------------------------------------------------------ // MAIN BUILD // ------------------------------------------------------------------ @override Widget build(BuildContext context) { final invoice = widget.invoice; final width = MediaQuery.of(context).size.width; final scale = (width / 390).clamp(0.85, 1.25); return ListView( padding: EdgeInsets.all(14 * scale), // slightly tighter children: [ headerCard(invoice, scale), invoiceTimeline(scale), sectionHeader("Invoice Summary", Icons.receipt_long, s1, () => setState(() => s1 = !s1), scale), sectionBody(s1, [ detailRow(Icons.numbers, "Invoice No", invoice['invoice_number'], scale), detailRow(Icons.calendar_today, "Date", invoice['invoice_date'], scale), detailRow(Icons.label, "Status", invoice['status'], scale), ], scale), sectionHeader("Customer Details", Icons.person, s2, () => setState(() => s2 = !s2), scale), sectionBody(s2, [ detailRow(Icons.person_outline, "Name", invoice['customer_name'], scale), detailRow(Icons.mail, "Email", invoice['customer_email'], scale), detailRow(Icons.phone, "Phone", invoice['customer_mobile'], scale), detailRow(Icons.location_on, "Address", invoice['customer_address'], scale), ], scale), sectionHeader("Amount Details", Icons.currency_rupee, s3, () => setState(() => s3 = !s3), scale), sectionBody(s3, [ detailRow(Icons.money, "Amount", invoice['final_amount'], scale), detailRow(Icons.percent, "GST %", invoice['gst_percent'], scale), detailRow(Icons.summarize, "Total", invoice['final_amount_with_gst'], scale), ], scale), sectionHeader("Payment Details", Icons.payment, s4, () => setState(() => s4 = !s4), scale), sectionBody(s4, [ detailRow(Icons.credit_card, "Method", invoice['payment_method'], scale), detailRow(Icons.confirmation_number, "Reference", invoice['reference_no'], scale), ], scale), ], ); } }