chitfund/luckychit/lib/shared/widgets/monthly_schedule_table.dart

667 lines
21 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/// Monthly payment schedule table
/// Shows month-wise breakdown of payments and chit win amounts
class MonthlyScheduleTable extends StatelessWidget {
final double totalValue;
final int durationMonths;
final double monthlyInstallment;
final double commission;
final double maxDividend;
const MonthlyScheduleTable({
super.key,
required this.totalValue,
required this.durationMonths,
required this.monthlyInstallment,
required this.commission,
required this.maxDividend,
});
@override
Widget build(BuildContext context) {
if (totalValue <= 0 || durationMonths <= 0) {
return _buildEmptyState();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
_buildHeader(),
SizedBox(height: 12.h),
// Month list
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(12.r),
),
constraints: BoxConstraints(
maxHeight: 350.h, // Scrollable if too many months
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12.r),
child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: durationMonths,
itemBuilder: (context, index) {
return _buildMonthRow(index + 1);
},
),
),
),
SizedBox(height: 12.h),
// Summary footer
_buildSummaryFooter(),
],
);
}
Widget _buildHeader() {
return Row(
children: [
Icon(Icons.calendar_month_rounded, color: Colors.green.shade600, size: 20.w),
SizedBox(width: 8.w),
Text(
'Month-wise Payment Schedule',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
const Spacer(),
Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(color: Colors.green.shade300),
),
child: Text(
'$durationMonths Months',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w600,
color: Colors.green.shade700,
),
),
),
],
);
}
Widget _buildMonthRow(int month) {
final subscriptionAmount = totalValue / durationMonths;
final chitWinAmount = _calculateChitWinAmount(month);
final isEarlyMonth = month <= (durationMonths * 0.3).ceil();
final isLastMonth = month == durationMonths;
return Container(
decoration: BoxDecoration(
color: isLastMonth
? Colors.purple.shade50
: isEarlyMonth
? Colors.green.shade50
: Colors.white,
border: Border(
bottom: BorderSide(
color: Colors.grey.shade200,
width: 1,
),
),
),
child: IntrinsicHeight(
child: Row(
children: [
// Month number
Container(
width: 55.w,
padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 8.w),
decoration: BoxDecoration(
color: isLastMonth
? Colors.purple.shade100
: isEarlyMonth
? Colors.green.shade100
: Colors.grey.shade100,
border: Border(
right: BorderSide(
color: Colors.grey.shade300,
width: 1,
),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'M',
style: TextStyle(
fontSize: 9.sp,
color: Colors.grey.shade600,
),
),
Text(
'$month',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: isLastMonth
? Colors.purple.shade700
: isEarlyMonth
? Colors.green.shade700
: Colors.grey.shade800,
),
),
],
),
),
// Payment details
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 10.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Payment amount
Row(
children: [
Expanded(
flex: 3,
child: Text(
'Payment:',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey.shade600,
),
),
),
Expanded(
flex: 2,
child: Text(
'${monthlyInstallment.toStringAsFixed(0)}',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
textAlign: TextAlign.right,
),
),
],
),
SizedBox(height: 4.h),
// Chit win amount
Row(
children: [
Icon(
Icons.emoji_events_rounded,
size: 12.w,
color: Colors.amber.shade700,
),
SizedBox(width: 4.w),
Expanded(
flex: 3,
child: Text(
'Win:',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey.shade600,
fontWeight: FontWeight.w500,
),
),
),
Expanded(
flex: 2,
child: Text(
'${chitWinAmount.toStringAsFixed(0)}',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: Colors.green.shade700,
),
textAlign: TextAlign.right,
),
),
],
),
// Show special badge for first and last month
if (isEarlyMonth || isLastMonth) ...[
SizedBox(height: 4.h),
Container(
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
decoration: BoxDecoration(
color: isLastMonth
? Colors.purple.shade200
: Colors.green.shade200,
borderRadius: BorderRadius.circular(6.r),
),
child: Text(
isLastMonth ? 'Last' : 'Early Adv.',
style: TextStyle(
fontSize: 9.sp,
fontWeight: FontWeight.w600,
color: isLastMonth
? Colors.purple.shade900
: Colors.green.shade900,
),
overflow: TextOverflow.ellipsis,
),
),
],
],
),
),
),
],
),
),
);
}
double _calculateChitWinAmount(int month) {
// Calculate chit win amount based on month
// Formula: Total Value - Total Commission
// This is the pool amount that winner receives
final totalCommission = commission * durationMonths;
final chitWinAmount = totalValue - totalCommission;
// Winner receives the full chit value minus commission
return chitWinAmount;
}
Widget _buildEmptyState() {
return Container(
padding: EdgeInsets.all(20.w),
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(color: Colors.orange.shade200),
),
child: Row(
children: [
Icon(Icons.info_outline, color: Colors.orange.shade600, size: 24.w),
SizedBox(width: 12.w),
Expanded(
child: Text(
'Enter chitfund details above to see the month-wise payment schedule',
style: TextStyle(
fontSize: 14.sp,
color: Colors.orange.shade700,
),
),
),
],
),
);
}
Widget _buildSummaryFooter() {
final totalCollection = monthlyInstallment * durationMonths;
final totalCommission = commission * durationMonths;
final netDistribution = totalValue;
return Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(color: Colors.blue.shade200),
),
child: Column(
children: [
Row(
children: [
Icon(Icons.summarize_rounded, color: Colors.blue.shade700, size: 16.w),
SizedBox(width: 6.w),
Text(
'Summary',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.bold,
color: Colors.blue.shade800,
),
),
],
),
SizedBox(height: 10.h),
_buildSummaryRow('Total Collected', '${totalCollection.toStringAsFixed(0)}'),
_buildSummaryRow('Commission', '${totalCommission.toStringAsFixed(0)}'),
_buildSummaryRow('Distributed', '${netDistribution.toStringAsFixed(0)}'),
Divider(height: 12.h, color: Colors.blue.shade300),
_buildSummaryRow(
'Per Month',
'${subscriptionAmount.toStringAsFixed(0)}',
isHighlight: true,
),
],
),
);
}
double get subscriptionAmount => totalValue / durationMonths;
Widget _buildSummaryRow(String label, String value, {bool isHighlight = false}) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 3.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 3,
child: Text(
label,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey.shade700,
fontWeight: isHighlight ? FontWeight.w600 : FontWeight.normal,
),
overflow: TextOverflow.ellipsis,
),
),
Expanded(
flex: 2,
child: Text(
value,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w600,
color: isHighlight ? Colors.blue.shade800 : Colors.grey.shade800,
),
textAlign: TextAlign.right,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
}
/// Expandable monthly schedule widget
class ExpandableMonthlySchedule extends StatefulWidget {
final double totalValue;
final int durationMonths;
final double monthlyInstallment;
final double commission;
final double maxDividend;
const ExpandableMonthlySchedule({
super.key,
required this.totalValue,
required this.durationMonths,
required this.monthlyInstallment,
required this.commission,
required this.maxDividend,
});
@override
State<ExpandableMonthlySchedule> createState() => _ExpandableMonthlyScheduleState();
}
class _ExpandableMonthlyScheduleState extends State<ExpandableMonthlySchedule> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
child: Column(
children: [
// Header (always visible)
InkWell(
onTap: () {
if (widget.totalValue > 0 && widget.durationMonths > 0) {
setState(() => _isExpanded = !_isExpanded);
}
},
borderRadius: BorderRadius.circular(12.r),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Row(
children: [
Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(10.r),
),
child: Icon(
Icons.event_note_rounded,
color: Colors.blue.shade600,
size: 24.w,
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Monthly Payment Schedule',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
SizedBox(height: 2.h),
Text(
widget.totalValue > 0 && widget.durationMonths > 0
? 'Tap to ${_isExpanded ? 'hide' : 'view'} all ${widget.durationMonths} months'
: 'Enter details above to preview',
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey.shade600,
),
),
],
),
),
if (widget.totalValue > 0 && widget.durationMonths > 0)
Icon(
_isExpanded ? Icons.expand_less : Icons.expand_more,
color: Colors.grey.shade600,
size: 24.w,
),
],
),
),
),
// Expandable content
if (_isExpanded)
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 12.h),
child: MonthlyScheduleTable(
totalValue: widget.totalValue,
durationMonths: widget.durationMonths,
monthlyInstallment: widget.monthlyInstallment,
commission: widget.commission,
maxDividend: widget.maxDividend,
),
),
],
),
);
}
}
/// Compact monthly schedule preview (first 3 and last 2 months)
class CompactMonthlySchedule extends StatelessWidget {
final double totalValue;
final int durationMonths;
final double monthlyInstallment;
final double commission;
const CompactMonthlySchedule({
super.key,
required this.totalValue,
required this.durationMonths,
required this.monthlyInstallment,
required this.commission,
});
@override
Widget build(BuildContext context) {
if (totalValue <= 0 || durationMonths <= 0) {
return const SizedBox.shrink();
}
final subscriptionAmount = totalValue / durationMonths;
final chitWinBase = totalValue - (commission * durationMonths);
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Payment Schedule Preview',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
SizedBox(height: 12.h),
// Table header
Row(
children: [
SizedBox(
width: 60.w,
child: Text(
'Month',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
),
),
Expanded(
child: Text(
'Payment',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
textAlign: TextAlign.right,
),
),
SizedBox(width: 12.w),
Expanded(
child: Text(
'Win Amount',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
textAlign: TextAlign.right,
),
),
],
),
Divider(height: 12.h, color: Colors.grey.shade300),
// Show first 3 months
..._buildCompactRows([1, 2, 3], subscriptionAmount, chitWinBase),
// Show separator if more months
if (durationMonths > 5) ...[
Padding(
padding: EdgeInsets.symmetric(vertical: 8.h),
child: Center(
child: Text(
'... ${durationMonths - 5} more months ...',
style: TextStyle(
fontSize: 11.sp,
color: Colors.grey.shade500,
fontStyle: FontStyle.italic,
),
),
),
),
],
// Show last 2 months
if (durationMonths > 3)
..._buildCompactRows(
[durationMonths - 1, durationMonths],
subscriptionAmount,
chitWinBase,
),
],
),
);
}
List<Widget> _buildCompactRows(List<int> months, double subscription, double chitWinAmount) {
return months.map((month) {
if (month > durationMonths) return const SizedBox.shrink();
return Padding(
padding: EdgeInsets.symmetric(vertical: 4.h),
child: Row(
children: [
SizedBox(
width: 60.w,
child: Text(
'$month',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
color: Colors.grey.shade800,
),
),
),
Expanded(
child: Text(
'${monthlyInstallment.toStringAsFixed(0)}',
style: TextStyle(
fontSize: 13.sp,
color: Colors.grey.shade700,
),
textAlign: TextAlign.right,
),
),
SizedBox(width: 12.w),
Expanded(
child: Text(
'${chitWinAmount.toStringAsFixed(0)}',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
color: Colors.green.shade700,
),
textAlign: TextAlign.right,
),
),
],
),
);
}).toList();
}
}