import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; class BarData { final int st; // 起始时间(毫秒) final int et; // 结束时间(毫秒) final double value; // 柱子高度 final int id; final String name; final Color color; BarData({ required this.st, required this.et, required this.value, required this.id, required this.name, required this.color, }); } class BarChartWidget extends StatelessWidget { final List data; final int startTime; // 毫秒时间戳 final int endTime; // 毫秒时间戳 final double maxYValue; // Y轴最大值 final int yStepCount; // Y轴分段数 const BarChartWidget({ super.key, required this.data, required this.startTime, required this.endTime, required this.maxYValue, this.yStepCount = 5, }); @override Widget build(BuildContext context) { return CustomPaint( size: Size(double.infinity, 500.rpx), painter: BarChartPainter( data, startTime, endTime, maxYValue: maxYValue, yStepCount: yStepCount, ), ); } } class BarChartPainter extends CustomPainter { final List data; final int startTime; final int endTime; final double maxYValue; final int yStepCount; final double topPadding = 0; // 控制顶部间距 final double bottomPadding = 0; // 控制底部间距 final double leftPadding = 30.rpx; // final double labelHeight = 50.rpx; BarChartPainter( this.data, this.startTime, this.endTime, { required this.maxYValue, this.yStepCount = 5, }); @override void paint(Canvas canvas, Size size) { final chartWidth = size.width - leftPadding; final chartHeight = size.height - topPadding - bottomPadding; final totalDuration = endTime - startTime; final textPainter = TextPainter(textDirection: ui.TextDirection.ltr); final stepValue = maxYValue / yStepCount; // 绘制 Y 轴刻度线和文字 for (int i = 0; i <= yStepCount; i++) { final value = stepValue * i; final y = topPadding + chartHeight - (value / maxYValue) * chartHeight; // 横线 // canvas.drawLine( // Offset(leftPadding, y), // Offset(size.width, y), // Paint() // ..color = Colors.grey.withOpacity(0.3) // ..strokeWidth = 0.5, // ); final dashPaint = Paint() ..color = Colors.grey.withOpacity(0.5) ..strokeWidth = 0.5; drawDashedLine( canvas, Offset(leftPadding, y), Offset(size.width, y), dashPaint); // Y轴刻度文字 textPainter.text = TextSpan( text: value.toStringAsFixed(0), style: TextStyle( fontSize: 20.rpx, color: themeController.currentColor.sc4, ), ); textPainter.layout(); textPainter.paint( canvas, Offset( leftPadding - textPainter.width - 4, y - textPainter.height / 2)); } // X轴时间刻度 final startDate = DateTime.fromMillisecondsSinceEpoch(startTime); final endDate = DateTime.fromMillisecondsSinceEpoch(endTime); final hourStep = const Duration(hours: 1); final xPaint = Paint()..color = Colors.grey; final xAxisY = topPadding + chartHeight; // 绘制整点小时刻度 for (DateTime t = startDate; t.isBefore(endDate); t = t.add(hourStep)) { final x = ((t.millisecondsSinceEpoch - startTime) / totalDuration) * chartWidth + leftPadding; final timeLabel = (t == startDate || t == endDate) ? DateFormat('HH:mm').format(t) : DateFormat('h').format(t); textPainter.text = TextSpan( text: timeLabel, style: TextStyle( fontSize: AppConstants().smaller_text_fontSize, color: themeController.currentColor.sc4, ), ); textPainter.layout(); textPainter.paint(canvas, Offset(x - textPainter.width / 2, xAxisY + 4)); } // ✅ 强制绘制结束时间刻度(确保显示) final endX = ((endTime - startTime) / totalDuration) * chartWidth + leftPadding; final endLabel = DateFormat('HH:mm').format(endDate); textPainter.text = TextSpan( text: endLabel, style: TextStyle( fontSize: AppConstants().smaller_text_fontSize, color: themeController.currentColor.sc4, ), ); textPainter.layout(); textPainter.paint(canvas, Offset(endX - textPainter.width / 2, xAxisY + 4)); // 绘制柱子 for (final d in data) { final left = ((d.st - startTime) / totalDuration) * chartWidth + leftPadding; final right = ((d.et - startTime) / totalDuration) * chartWidth + leftPadding; final barHeight = (d.value / maxYValue) * chartHeight; final top = topPadding + chartHeight - barHeight; final barPaint = Paint()..color = d.color; canvas.drawRect( Rect.fromLTRB(left, top, right, topPadding + chartHeight), barPaint); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; void drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint, {double dashWidth = 5, double dashSpace = 3}) { double totalLength = (end.dx - start.dx).abs(); double dashCount = (totalLength / (dashWidth + dashSpace)).floorToDouble(); double dx = start.dx; final dy = start.dy; for (int i = 0; i < dashCount; i++) { final from = Offset(dx, dy); final to = Offset(dx + dashWidth, dy); canvas.drawLine(from, to, paint); dx += dashWidth + dashSpace; } } }