import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:fl_chart/fl_chart.dart'; /// Bar chart for monthly payment overview class MonthlyPaymentChart extends StatelessWidget { final List data; final Color barColor; const MonthlyPaymentChart({ super.key, required this.data, this.barColor = const Color(0xFF2E7D32), }); @override Widget build(BuildContext context) { return Card( child: Padding( padding: EdgeInsets.all(20.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Monthly Payments', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, ), ), SizedBox(height: 20.h), SizedBox( height: 200.h, child: BarChart( BarChartData( alignment: BarChartAlignment.spaceAround, maxY: data.isNotEmpty ? data.map((e) => e.amount).reduce((a, b) => a > b ? a : b) * 1.2 : 100, barTouchData: BarTouchData( enabled: true, touchTooltipData: BarTouchTooltipData( getTooltipItem: (group, groupIndex, rod, rodIndex) { return BarTooltipItem( '₹${rod.toY.toStringAsFixed(0)}', TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14.sp, ), ); }, ), ), titlesData: FlTitlesData( show: true, bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { if (value.toInt() < 0 || value.toInt() >= data.length) { return const SizedBox(); } return Padding( padding: EdgeInsets.only(top: 8.h), child: Text( data[value.toInt()].month, style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ); }, reservedSize: 28.h, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { return Text( '₹${(value / 1000).toStringAsFixed(0)}k', style: TextStyle( fontSize: 10.sp, fontWeight: FontWeight.w500, ), ); }, reservedSize: 42.w, ), ), topTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), gridData: FlGridData( show: true, drawVerticalLine: false, horizontalInterval: 5000, getDrawingHorizontalLine: (value) { return FlLine( color: Colors.grey.shade200, strokeWidth: 1, ); }, ), borderData: FlBorderData(show: false), barGroups: data.asMap().entries.map((entry) { return BarChartGroupData( x: entry.key, barRods: [ BarChartRodData( toY: entry.value.amount, color: barColor, width: 20.w, borderRadius: BorderRadius.vertical( top: Radius.circular(4.r), ), ), ], ); }).toList(), ), ), ), ], ), ), ); } } /// Pie chart for payment distribution class PaymentDistributionChart extends StatelessWidget { final List categories; const PaymentDistributionChart({ super.key, required this.categories, }); @override Widget build(BuildContext context) { return Card( child: Padding( padding: EdgeInsets.all(20.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Payment Distribution', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, ), ), SizedBox(height: 20.h), Row( children: [ // Pie Chart SizedBox( width: 150.w, height: 150.h, child: PieChart( PieChartData( sectionsSpace: 2, centerSpaceRadius: 40.r, sections: categories.map((category) { return PieChartSectionData( value: category.percentage, title: '${category.percentage.toStringAsFixed(0)}%', color: category.color, radius: 50.r, titleStyle: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.bold, color: Colors.white, ), ); }).toList(), pieTouchData: PieTouchData( touchCallback: (FlTouchEvent event, pieTouchResponse) {}, ), ), ), ), SizedBox(width: 24.w), // Legend Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: categories.map((category) { return Padding( padding: EdgeInsets.only(bottom: 12.h), child: Row( children: [ Container( width: 16.w, height: 16.h, decoration: BoxDecoration( color: category.color, borderRadius: BorderRadius.circular(4.r), ), ), SizedBox(width: 8.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( category.label, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, ), ), Text( '₹${category.amount.toStringAsFixed(0)}', style: TextStyle( fontSize: 12.sp, color: Colors.grey.shade600, ), ), ], ), ), ], ), ); }).toList(), ), ), ], ), ], ), ), ); } } /// Line chart for payment trends class PaymentTrendChart extends StatelessWidget { final List data; final Color lineColor; const PaymentTrendChart({ super.key, required this.data, this.lineColor = const Color(0xFF2E7D32), }); @override Widget build(BuildContext context) { return Card( child: Padding( padding: EdgeInsets.all(20.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Payment Trends', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, ), ), SizedBox(height: 20.h), SizedBox( height: 200.h, child: LineChart( LineChartData( gridData: FlGridData( show: true, drawVerticalLine: false, horizontalInterval: 5000, getDrawingHorizontalLine: (value) { return FlLine( color: Colors.grey.shade200, strokeWidth: 1, ); }, ), titlesData: FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { if (value.toInt() < 0 || value.toInt() >= data.length) { return const SizedBox(); } return Padding( padding: EdgeInsets.only(top: 8.h), child: Text( data[value.toInt()].label, style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), ); }, reservedSize: 28.h, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { return Text( '₹${(value / 1000).toStringAsFixed(0)}k', style: TextStyle( fontSize: 10.sp, fontWeight: FontWeight.w500, ), ); }, reservedSize: 42.w, ), ), topTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), borderData: FlBorderData(show: false), minX: 0, maxX: (data.length - 1).toDouble(), minY: 0, maxY: data.isNotEmpty ? data.map((e) => e.value).reduce((a, b) => a > b ? a : b) * 1.2 : 100, lineBarsData: [ LineChartBarData( spots: data.asMap().entries.map((entry) { return FlSpot(entry.key.toDouble(), entry.value.value); }).toList(), isCurved: true, color: lineColor, barWidth: 3, isStrokeCapRound: true, dotData: FlDotData( show: true, getDotPainter: (spot, percent, barData, index) { return FlDotCirclePainter( radius: 4, color: lineColor, strokeWidth: 2, strokeColor: Colors.white, ); }, ), belowBarData: BarAreaData( show: true, color: lineColor.withOpacity(0.1), ), ), ], lineTouchData: LineTouchData( touchTooltipData: LineTouchTooltipData( getTooltipItems: (touchedSpots) { return touchedSpots.map((spot) { return LineTooltipItem( '₹${spot.y.toStringAsFixed(0)}', TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14.sp, ), ); }).toList(); }, ), ), ), ), ), ], ), ), ); } } /// Payment status overview widget class PaymentStatusWidget extends StatelessWidget { final int totalPayments; final int successfulPayments; final int pendingPayments; final int failedPayments; const PaymentStatusWidget({ super.key, required this.totalPayments, required this.successfulPayments, required this.pendingPayments, required this.failedPayments, }); @override Widget build(BuildContext context) { return Card( child: Padding( padding: EdgeInsets.all(20.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Payment Status', style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, ), ), SizedBox(height: 20.h), _buildStatusRow( 'Successful', successfulPayments, totalPayments, Colors.green.shade600, ), SizedBox(height: 12.h), _buildStatusRow( 'Pending', pendingPayments, totalPayments, Colors.orange.shade600, ), SizedBox(height: 12.h), _buildStatusRow( 'Failed', failedPayments, totalPayments, Colors.red.shade600, ), ], ), ), ); } Widget _buildStatusRow(String label, int count, int total, Color color) { final percentage = total > 0 ? (count / total * 100) : 0.0; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, ), ), Text( '$count (${percentage.toStringAsFixed(0)}%)', style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w600, color: color, ), ), ], ), SizedBox(height: 8.h), ClipRRect( borderRadius: BorderRadius.circular(4.r), child: LinearProgressIndicator( value: percentage / 100, backgroundColor: Colors.grey.shade200, valueColor: AlwaysStoppedAnimation(color), minHeight: 8.h, ), ), ], ); } } // Data classes class PaymentData { final String month; final double amount; PaymentData({required this.month, required this.amount}); } class PaymentCategory { final String label; final double amount; final double percentage; final Color color; PaymentCategory({ required this.label, required this.amount, required this.percentage, required this.color, }); } class TrendData { final String label; final double value; TrendData({required this.label, required this.value}); }