TKK_E32230859/lib/app/widgets/history_grafik_card.dart

253 lines
8.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import '../models/chart_data_point.dart';
class HistoryGrafikCard extends StatelessWidget {
final List<ChartDataPoint> dataPoints;
final VoidCallback? onBookmarkTap;
const HistoryGrafikCard({
super.key,
required this.dataPoints,
this.onBookmarkTap,
});
@override
Widget build(BuildContext context) {
if (dataPoints.isEmpty) {
return Container(
padding: const EdgeInsets.all(32),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[200]!),
),
child: Center(
child: Column(
children: [
Icon(Icons.show_chart, size: 48, color: Colors.grey[400]),
const SizedBox(height: 12),
Text(
'Belum ada data history',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
],
),
),
);
}
final hours = dataPoints.map((p) => p.hour.toDouble()).toList();
final drops = dataPoints.map((p) => p.dropsPerMinute.toDouble()).toList();
final minX = hours.reduce((a, b) => a < b ? a : b);
final maxX = hours.reduce((a, b) => a > b ? a : b);
final minY = (drops.reduce((a, b) => a < b ? a : b) - 5.0).clamp(
0.0,
double.infinity,
);
final maxY = drops.reduce((a, b) => a > b ? a : b) + 5.0;
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey[200]!),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 0,
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFFE3F2FD),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.insert_chart_outlined,
size: 20,
color: Color(0xFF2196F3),
),
),
const SizedBox(width: 12),
const Text(
'Grafik Infus Pasien',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 20),
SizedBox(
height: 180,
child: LineChart(
LineChartData(
gridData: FlGridData(
show: true,
drawVerticalLine: false,
horizontalInterval: (maxY - minY) / 4,
getDrawingHorizontalLine: (value) {
return FlLine(color: Colors.grey[200]!, strokeWidth: 1);
},
),
titlesData: FlTitlesData(
show: true,
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
interval: dataPoints.length > 5 ? 2.0 : 1.0,
getTitlesWidget: (double value, TitleMeta meta) {
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'${value.toInt()}:00',
style: TextStyle(
color: Colors.grey[600],
fontWeight: FontWeight.w500,
fontSize: 10,
),
),
);
},
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: (maxY - minY) / 4,
reservedSize: 35,
getTitlesWidget: (double value, TitleMeta meta) {
return Text(
'${value.toInt()}',
style: TextStyle(
color: Colors.grey[600],
fontWeight: FontWeight.w500,
fontSize: 11,
),
);
},
),
),
),
borderData: FlBorderData(
show: true,
border: Border(
left: BorderSide(color: Colors.grey[300]!),
bottom: BorderSide(color: Colors.grey[300]!),
),
),
minX: minX,
maxX: maxX,
minY: minY,
maxY: maxY,
lineBarsData: [
LineChartBarData(
spots: dataPoints
.map(
(point) => FlSpot(
point.hour.toDouble(),
point.dropsPerMinute.toDouble(),
),
)
.toList(),
isCurved: true,
gradient: const LinearGradient(
colors: [Color(0xFF2196F3), Color(0xFF1976D2)],
),
barWidth: 3,
isStrokeCapRound: true,
dotData: FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) {
return FlDotCirclePainter(
radius: 4,
color: Colors.white,
strokeWidth: 2,
strokeColor: const Color(0xFF2196F3),
);
},
),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFF2196F3).withOpacity(0.3),
const Color(0xFF2196F3).withOpacity(0.0),
],
),
),
),
],
lineTouchData: LineTouchData(
enabled: true,
touchTooltipData: LineTouchTooltipData(
getTooltipColor: (touchedSpot) => const Color(0xFF2196F3),
tooltipBorder: const BorderSide(
color: Color(0xFF1976D2),
width: 1,
),
tooltipPadding: const EdgeInsets.all(8),
tooltipMargin: 8,
getTooltipItems: (List<LineBarSpot> touchedBarSpots) {
return touchedBarSpots.map((barSpot) {
return LineTooltipItem(
'${barSpot.y.toInt()} tpm\n${barSpot.x.toInt()}:00',
const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
);
}).toList();
},
),
),
),
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 12,
height: 3,
decoration: BoxDecoration(
color: const Color(0xFF2196F3),
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 6),
Text(
'Tetes per Menit',
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
],
),
],
),
);
}
}