Files
tuiche/lib/pages/sleep_report/chart/LineChart.dart
2025-05-22 08:56:27 +08:00

159 lines
4.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'dart:ui' as ui;
class LineChart extends StatelessWidget {
final int startTime;
final int endTime;
final double minValue;
final double maxValue;
final List<Map<String, dynamic>> dataPoints;
LineChart({
required this.startTime,
required this.endTime,
required this.minValue,
required this.maxValue,
required this.dataPoints,
});
@override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(double.infinity, 300),
painter: LineChartPainter(
startTime: startTime,
endTime: endTime,
minValue: minValue,
maxValue: maxValue,
dataPoints: dataPoints,
),
);
}
}
class LineChartPainter extends CustomPainter {
final int startTime;
final int endTime;
final double minValue;
final double maxValue;
final List<Map<String, dynamic>> dataPoints;
LineChartPainter({
required this.startTime,
required this.endTime,
required this.minValue,
required this.maxValue,
required this.dataPoints,
});
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()..style = PaintingStyle.stroke;
double chartWidth = size.width;
double chartHeight = size.height;
// 时间轴刻度设置
DateFormat timeFormatStartEnd = DateFormat('HH:mm');
DateFormat timeFormatMiddle = DateFormat('h');
// 绘制Y轴刻度
double yAxisHeight = chartHeight - 40; // 留一些空间给X轴
double yAxisStep = yAxisHeight / 4;
paint.color = Colors.grey;
paint.strokeWidth = 1;
canvas.drawLine(Offset(30, 0), Offset(30, chartHeight), paint); // Y轴
// 绘制Y轴的刻度线
paint.color = Colors.grey;
for (int i = 0; i < 5; i++) {
double y = i * yAxisStep;
if (i == 0) {
paint.color = Colors.grey; // 0线
canvas.drawLine(Offset(25, y), Offset(35, y), paint);
} else if (i == 1) {
paint.color = Colors.red; // 最小值线
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 1;
canvas.drawLine(Offset(25, y), Offset(35, y), paint);
} else if (i == 2) {
paint.color = Colors.grey; // 最大值与最小值中间线
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 1;
_drawDashedLine(canvas, paint, 25, y, 35, y); // Custom dashed line
} else {
paint.color = Colors.red; // 最大值线
canvas.drawLine(Offset(25, y), Offset(35, y), paint);
}
}
// 绘制X轴时间刻度
DateTime startDate = DateTime.fromMillisecondsSinceEpoch(startTime);
DateTime endDate = DateTime.fromMillisecondsSinceEpoch(endTime);
double xAxisStep = (chartWidth - 60) /
(endDate.millisecondsSinceEpoch - startDate.millisecondsSinceEpoch);
for (DateTime date = startDate;
date.isBefore(endDate);
date = date.add(Duration(hours: 1))) {
String timeLabel = (date == startDate || date == endDate)
? timeFormatStartEnd.format(date)
: timeFormatMiddle.format(date);
paint.color = Colors.black;
// Draw text using TextPainter
_drawText(
canvas, timeLabel, Offset(30, yAxisHeight)); // Position dynamically
}
// 绘制折线图数据点
Path path = Path();
for (var i = 0; i < dataPoints.length; i++) {
var point = dataPoints[i];
DateTime pointTime = DateTime.fromMillisecondsSinceEpoch(point['time']);
double x = (pointTime.millisecondsSinceEpoch -
startDate.millisecondsSinceEpoch) *
xAxisStep +
30;
double y = chartHeight -
(point['value'] - minValue) * yAxisHeight / (maxValue - minValue);
path.lineTo(x, y);
}
paint.color = Colors.green; // Line color based on range
paint.strokeWidth = 2;
canvas.drawPath(path, paint);
}
// Custom method to draw dashed line
void _drawDashedLine(Canvas canvas, Paint paint, double startX, double startY,
double endX, double endY) {
double dashWidth = 5;
double dashSpace = 3;
double distance = (endX - startX).abs();
double dashCount = (distance / (dashWidth + dashSpace)).floorToDouble();
for (int i = 0; i < dashCount; i++) {
double startXDash = startX + (i * (dashWidth + dashSpace));
double endXDash = startXDash + dashWidth;
canvas.drawLine(
Offset(startXDash, startY), Offset(endXDash, endY), paint);
}
}
// Custom method to draw text
void _drawText(Canvas canvas, String text, Offset offset) {
TextPainter textPainter = TextPainter(
text: TextSpan(
text: text, style: TextStyle(color: Colors.black, fontSize: 12)),
textDirection: ui.TextDirection.ltr,
)..layout();
textPainter.paint(canvas, offset);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}