Files
kent_logistics_app/lib/screens/invoice_screen.dart

290 lines
9.8 KiB
Dart
Raw Permalink Normal View History

2025-12-03 11:57:05 +05:30
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/invoice_installment_screen.dart';
import '../providers/invoice_provider.dart';
import 'invoice_detail_screen.dart';
class InvoiceScreen extends StatefulWidget {
const InvoiceScreen({super.key});
@override
State<InvoiceScreen> createState() => _InvoiceScreenState();
}
class _InvoiceScreenState extends State<InvoiceScreen> {
2025-12-11 18:36:11 +05:30
String searchQuery = "";
2025-12-03 11:57:05 +05:30
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<InvoiceProvider>(context, listen: false)
.loadInvoices(context);
});
}
@override
Widget build(BuildContext context) {
final provider = Provider.of<InvoiceProvider>(context);
2025-12-11 18:36:11 +05:30
final width = MediaQuery.of(context).size.width;
final scale = (width / 430).clamp(0.88, 1.08);
2025-12-03 11:57:05 +05:30
if (provider.loading) {
return const Center(child: CircularProgressIndicator());
}
2025-12-11 18:36:11 +05:30
// 🔍 Filter invoices based on search query
final filteredInvoices = provider.invoices.where((inv) {
final q = searchQuery.toLowerCase();
return inv['invoice_number'].toString().toLowerCase().contains(q) ||
inv['invoice_date'].toString().toLowerCase().contains(q) ||
inv['formatted_amount'].toString().toLowerCase().contains(q) ||
inv['status'].toString().toLowerCase().contains(q);
}).toList();
2025-12-03 11:57:05 +05:30
2025-12-11 18:36:11 +05:30
return Column(
children: [
// 🔍 SEARCH BAR
Container(
margin: EdgeInsets.fromLTRB(16 * scale, 16 * scale, 16 * scale, 8 * scale),
padding: EdgeInsets.symmetric(horizontal: 14 * scale),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14 * scale),
boxShadow: [
BoxShadow(
color: Colors.black12.withOpacity(0.08),
blurRadius: 8 * scale,
offset: Offset(0, 3 * scale),
),
],
),
child: TextField(
onChanged: (v) => setState(() => searchQuery = v),
style: TextStyle(fontSize: 14 * scale),
decoration: InputDecoration(
icon: Icon(Icons.search, size: 22 * scale),
hintText: "Search Invoice Number, Date, Amount...",
border: InputBorder.none,
),
),
),
2025-12-03 11:57:05 +05:30
2025-12-11 18:36:11 +05:30
// 📄 LIST OF INVOICES
Expanded(
child: filteredInvoices.isEmpty
? Center(
child: Text(
"No invoices found",
style: TextStyle(
fontSize: 18 * scale,
fontWeight: FontWeight.w600,
),
),
)
: ListView.builder(
padding: EdgeInsets.all(16 * scale),
itemCount: filteredInvoices.length,
itemBuilder: (_, i) {
final inv = filteredInvoices[i];
2025-12-03 11:57:05 +05:30
2025-12-11 18:36:11 +05:30
return Card(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16 * scale),
),
elevation: 3,
margin: EdgeInsets.only(bottom: 14 * scale),
child: Stack(
2025-12-03 11:57:05 +05:30
children: [
2025-12-11 18:36:11 +05:30
Padding(
padding: EdgeInsets.all(16 * scale),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Invoice Number
Text(
"Invoice ${inv['invoice_number'] ?? 'N/A'}",
style: TextStyle(
fontSize: 20 * scale,
fontWeight: FontWeight.bold,
color: Colors.blue,
2025-12-03 11:57:05 +05:30
),
),
2025-12-11 18:36:11 +05:30
SizedBox(height: 8 * scale),
/// Date + Amount
Text(
"Date: ${inv['invoice_date'] ?? 'N/A'}",
style: TextStyle(fontSize: 15 * scale),
),
Text(
"Amount: ₹${inv['formatted_amount'] ?? '0'}",
style: TextStyle(fontSize: 15 * scale),
),
SizedBox(height: 16 * scale),
/// BUTTONS ROW
Row(
children: [
Expanded(
child: GradientButton(
text: "Invoice Details",
fontSize: 15 * scale,
radius: 12 * scale,
padding: 14 * scale,
gradient: const LinearGradient(
colors: [
Color(0xFF1976D2),
Color(0xFF42A5F5),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
InvoiceDetailScreen(
invoiceId:
inv['invoice_id']),
),
);
},
),
),
SizedBox(width: 12 * scale),
Expanded(
child: GradientButton(
text: "Installments",
fontSize: 15 * scale,
radius: 12 * scale,
padding: 14 * scale,
gradient: const LinearGradient(
colors: [
Color(0xFF43A047),
Color(0xFF81C784),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
InvoiceInstallmentScreen(
invoiceId:
inv['invoice_id']),
),
);
},
),
),
],
),
],
),
2025-12-03 11:57:05 +05:30
),
2025-12-11 18:36:11 +05:30
/// STATUS BADGE
Positioned(
right: 12 * scale,
top: 12 * scale,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 10 * scale,
vertical: 6 * scale,
),
decoration: BoxDecoration(
color: _getStatusColor(inv['status']),
borderRadius: BorderRadius.circular(12 * scale),
),
child: Text(
inv['status'] ?? 'N/A',
style: TextStyle(
color: Colors.white,
fontSize: 12 * scale,
fontWeight: FontWeight.bold,
2025-12-03 11:57:05 +05:30
),
2025-12-11 18:36:11 +05:30
),
),
2025-12-03 11:57:05 +05:30
),
],
),
2025-12-11 18:36:11 +05:30
);
},
),
),
],
);
}
}
/// Status Color Helper
Color _getStatusColor(String? status) {
switch (status?.toLowerCase()) {
case 'pending':
return Colors.orange;
case 'in transit':
return Colors.blue;
case 'overdue':
return Colors.redAccent;
case 'paid':
return Colors.green;
default:
return Colors.grey;
}
}
/// -------------------------------------------------------
/// RESPONSIVE GRADIENT BUTTON
/// -------------------------------------------------------
class GradientButton extends StatelessWidget {
final String text;
final Gradient gradient;
final VoidCallback onTap;
final double fontSize;
final double padding;
final double radius;
const GradientButton({
super.key,
required this.text,
required this.gradient,
required this.onTap,
required this.fontSize,
required this.padding,
required this.radius,
});
@override
Widget build(BuildContext context) {
return InkWell(
borderRadius: BorderRadius.circular(radius),
onTap: onTap,
child: Ink(
decoration: BoxDecoration(
gradient: gradient,
borderRadius: BorderRadius.circular(radius),
),
child: Container(
padding: EdgeInsets.symmetric(vertical: padding),
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(
color: Colors.white,
fontSize: fontSize,
fontWeight: FontWeight.w600,
2025-12-03 11:57:05 +05:30
),
),
2025-12-11 18:36:11 +05:30
),
),
2025-12-03 11:57:05 +05:30
);
}
}