Files
kent_logistics_app/lib/providers/invoice_installment_screen.dart

227 lines
6.4 KiB
Dart
Raw Permalink Normal View History

2025-12-03 11:57:05 +05:30
import 'package:flutter/material.dart';
import '../services/dio_client.dart';
import '../services/invoice_service.dart';
class InvoiceInstallmentScreen extends StatefulWidget {
final int invoiceId;
2025-12-11 18:36:11 +05:30
const InvoiceInstallmentScreen({
super.key,
required this.invoiceId,
});
2025-12-03 11:57:05 +05:30
@override
State<InvoiceInstallmentScreen> createState() =>
_InvoiceInstallmentScreenState();
}
class _InvoiceInstallmentScreenState extends State<InvoiceInstallmentScreen> {
bool loading = true;
List installments = [];
@override
void initState() {
super.initState();
load();
}
Future<void> load() async {
final service = InvoiceService(DioClient.getInstance(context));
final res = await service.getInstallments(widget.invoiceId);
if (res['success'] == true) {
installments = res['installments'] ?? [];
}
loading = false;
setState(() {});
}
@override
Widget build(BuildContext context) {
2025-12-11 18:36:11 +05:30
final width = MediaQuery.of(context).size.width;
2025-12-03 11:57:05 +05:30
return Scaffold(
2025-12-11 18:36:11 +05:30
backgroundColor: Colors.grey.shade100,
appBar: AppBar(
title: const Text("Installments"),
elevation: 1,
),
2025-12-03 11:57:05 +05:30
body: loading
? const Center(child: CircularProgressIndicator())
: installments.isEmpty
2025-12-11 18:36:11 +05:30
? _buildEmptyState()
2025-12-03 11:57:05 +05:30
: ListView.builder(
2025-12-11 18:36:11 +05:30
padding: EdgeInsets.symmetric(
horizontal: width * 0.04,
vertical: 16,
),
2025-12-03 11:57:05 +05:30
itemCount: installments.length,
itemBuilder: (_, i) {
2025-12-11 18:36:11 +05:30
return InstallmentCard(inst: installments[i]);
2025-12-03 11:57:05 +05:30
},
),
);
}
2025-12-11 18:36:11 +05:30
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.receipt_long,
size: 70, color: Colors.grey.shade400),
const SizedBox(height: 12),
Text(
"No Installments Created",
style: TextStyle(
fontSize: 18,
color: Colors.grey.shade600,
),
),
],
),
);
}
}
class InstallmentCard extends StatelessWidget {
final Map inst;
const InstallmentCard({super.key, required this.inst});
String getString(key) => inst[key]?.toString() ?? "N/A";
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
final isTablet = width > 600;
final padding = isTablet ? 28.0 : 20.0;
final amountSize = isTablet ? 30.0 : 26.0;
return LayoutBuilder(
builder: (context, constraints) {
return Container(
margin: const EdgeInsets.only(bottom: 18),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Padding(
padding: EdgeInsets.all(padding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Amount + Payment Method Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${getString('amount')}",
style: TextStyle(
fontSize: amountSize,
fontWeight: FontWeight.bold,
letterSpacing: 0.2,
),
),
// Payment Chip
Container(
padding: EdgeInsets.symmetric(
vertical: isTablet ? 8 : 6,
horizontal: isTablet ? 16 : 12,
),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(50),
),
child: Text(
getString('payment_method'),
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.blue.shade700,
fontSize: isTablet ? 15 : 13.5,
),
),
),
],
),
SizedBox(height: isTablet ? 24 : 18),
// Responsive Info Rows
buildInfoRow(
Icons.calendar_month,
"Date",
getString("installment_date"),
isTablet
),
SizedBox(height: isTablet ? 14 : 10),
buildInfoRow(
Icons.confirmation_number,
"Reference",
getString("reference_no"),
isTablet
),
SizedBox(height: isTablet ? 24 : 18),
Divider(color: Colors.grey.shade300, thickness: 1),
SizedBox(height: isTablet ? 10 : 6),
// Align(
// alignment: Alignment.centerRight,
// child: Text(
// "Installment #${inst['id'] ?? ''}",
// style: TextStyle(
// fontSize: isTablet ? 15 : 13,
// color: Colors.grey.shade600,
// fontWeight: FontWeight.w500,
// ),
// ),
// ),
2025-12-11 18:36:11 +05:30
],
),
),
);
},
);
}
/// Responsive info row builder
Widget buildInfoRow(IconData icon, String label, String value, bool isTablet) {
return Row(
children: [
Icon(icon, size: isTablet ? 24 : 20, color: Colors.grey.shade600),
const SizedBox(width: 10),
Text(
"$label:",
style: TextStyle(
fontSize: isTablet ? 17 : 15,
fontWeight: FontWeight.w600,
color: Colors.grey.shade700,
),
),
const SizedBox(width: 6),
Expanded(
child: Text(
value,
style: TextStyle(
fontSize: isTablet ? 16 : 15,
color: Colors.grey.shade800,
fontWeight: FontWeight.w500,
),
),
),
],
);
}
2025-12-03 11:57:05 +05:30
}