Files
tuiche/lib/pages/sleep_report/component/WeekDataWidget.dart
2025-07-07 14:58:54 +08:00

853 lines
32 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:ef/base/chart/drawer.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/pages/mh_page/component/easychart.dart';
import 'package:vbvs_app/pages/sleep_report/component/SleepCard.dart';
import 'package:vbvs_app/pages/sleep_report/component/SleepChartWidget.dart';
import 'package:vbvs_app/pages/sleep_report/component/TrendDataTablePage.dart';
import 'package:vbvs_app/pages/sleep_report/component/TrendDataTextPage.dart';
import 'package:vbvs_app/pages/sleep_report/component/WeekSleepScoreWidget.dart';
Widget WeekDataWidget(
Map<dynamic, dynamic> sleepReport,
dynamic data,
) {
List<Widget> _buildSectionList() {
EdgeInsetsDirectional padding =
EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 25.rpx);
return [
AvgSleepScoreWidget(
sleepReport: sleepReport,
), //睡眠评分
SleepChartContainer(
title: "每日得分",
tipText: "睡眠规律性介绍",
sleepReport: sleepReport,
chartContent: LineView(
xLabels: [
ChartLables(
//标签系列1
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels:
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -16.rpx), //标签相对于原点的偏移下方20像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
);
},
),
ChartLables(
//X轴标签系列2
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -50.rpx), //标签相对于原点的偏移下方60像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
);
},
),
],
yLabels: [
ChartLables(
min: sleepReport['scoreList']['yRange']['min'] ?? 0.0,
max: sleepReport['scoreList']['yRange']['max'] ?? 100.0,
q: 100,
labels: List<String>.from(
sleepReport['scoreList']['yLable']
.map((e) => e['name'].toString()),
),
offset: Offset(-15.rpx, 0),
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFFFFFF).withOpacity(0.6),
fontSize: 18.rpx),
);
},
),
],
xCount: 7,
yCount: sleepReport['scoreList']['yLable'].length,
points: buildWeekDatesAndPoints(sleepReport['scoreList'])['points'],
displayMode: ChartDisplayMode.bar,
barColors:
buildWeekDatesAndPoints(sleepReport['scoreList'])['colors'],
tips: buildValueTexts(sleepReport['scoreList']['data'], '', 1),
xUnit: sleepReport['scoreList']['yUnit'],
barWidth: 0.1,
),
showLabel: sleepReport['scoreList']['type'],
),
SleepCard(
sleepReport: sleepReport,
highlightItem: data['itemName'],
),
IndicatorCompareCard(
title: "与上周对比",
headers: ["名称", "上周", "本周", "参考范围"],
tooltip: "与上周对比提示",
rows: (sleepReport['cwl'] ?? []).map<List<Widget>>((item) {
return [
Text(
item['name']?.toString() ?? '-',
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
),
Text(
item['lastCurr']?.toString() ?? '-',
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
),
Text(
item['curr']?.toString() ?? '-',
style: TextStyle(
color: item["currColor"] ?? Color(0xFFFFFFFF),
fontSize: 26.rpx,
),
),
Text(
item['range']?.toString() ?? '-',
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
),
];
}).toList(),
),
SleepChartContainer(
title: "本周睡眠时长",
tipText: "本周睡眠时长介绍",
sleepReport: sleepReport,
chartContent: LineView(
xLabels: [
ChartLables(
//标签系列1
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels:
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -16.rpx), //标签相对于原点的偏移下方20像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
);
},
),
ChartLables(
//X轴标签系列2
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -50.rpx), //标签相对于原点的偏移下方60像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
);
},
),
],
yLabels: [
ChartLables(
min: sleepReport['csd']['yRange']['min'] ?? 0.0,
max: sleepReport['csd']['yRange']['max'] ?? 10.0,
q: 10,
labels: List<String>.from(
sleepReport['csd']['yLable'].map((e) => e['name'].toString()),
),
offset: Offset(-15.rpx, 0),
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFFFFFF).withOpacity(0.6),
fontSize: 18.rpx),
);
},
),
],
xCount: 7,
yCount: sleepReport['csd']['yLable'].length,
points: [],
dualBarPoints: buildTripleBarData(sleepReport['csd']),
displayMode: ChartDisplayMode.dualBar,
xUnit: sleepReport['csd']['yUnit'],
tips: buildSleepValueTexts(sleepReport['csd']['data'], '小时', 1),
barWidth: 0.1,
),
showLabel: sleepReport['csd']['type'],
),
TrendDataTablePage(
title: sleepReport['dysp'][0]['name'],
tipText: "入睡时间趋势提示",
chartContent: LineView(
xLabels: [
ChartLables(
//标签系列1
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels:
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -16.rpx), //标签相对于原点的偏移下方20像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
);
},
),
ChartLables(
//X轴标签系列2
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -50.rpx), //标签相对于原点的偏移下方60像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
);
},
),
],
yLabels: [
ChartLables(
min: 0,
max: 5,
q: 5,
labels: List<String>.from(
sleepReport['dysp'][0]['yLable']
.map((e) => e['name'].toString()),
),
offset: Offset(-30.rpx, 0),
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFFFFFF).withOpacity(0.6),
fontSize: 18.rpx),
);
},
),
],
tips: buildValueTexts(sleepReport['dysp'][0]['value'], '入睡时间:', 0),
xCount: 7,
yCount: sleepReport['dysp'][0]['yLable'].length,
points: buildGeneralPoints(sleepReport['dysp'][0]),
displayMode: ChartDisplayMode.line,
xUnit: sleepReport['dysp'][0]['yUnit'],
barWidth: 0.1,
bottomPadding: 50,
),
padding: 45.rpx),
TrendDataTablePage(
title: sleepReport['dysp'][1]['name'],
tipText: "起床时间趋势提示",
chartContent: LineView(
xLabels: [
ChartLables(
//标签系列1
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels:
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -16.rpx), //标签相对于原点的偏移下方20像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
);
},
),
ChartLables(
//X轴标签系列2
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -50.rpx), //标签相对于原点的偏移下方60像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
);
},
),
],
yLabels: [
ChartLables(
min: 0,
max: 4,
q: 4,
labels: List<String>.from(
sleepReport['dysp'][1]['yLable']
.map((e) => e['name'].toString()),
),
offset: Offset(-30.rpx, 0),
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFFFFFF).withOpacity(0.6),
fontSize: 18.rpx),
);
},
),
],
tips: buildValueTexts(sleepReport['dysp'][1]['value'], '起床时间:', 0),
xCount: 7,
yCount: sleepReport['dysp'][1]['yLable'].length,
points: buildGeneralPoints(sleepReport['dysp'][1]),
displayMode: ChartDisplayMode.line,
xUnit: sleepReport['dysp'][1]['yUnit'],
barWidth: 0.1,
bottomPadding: 50,
),
padding: 45.rpx),
TrendDataTablePage(
title: sleepReport['dysp'][2]['name'],
tipText: "心率基准趋势提示",
chartContent: LineView(
xLabels: [
ChartLables(
//标签系列1
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels:
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -16.rpx), //标签相对于原点的偏移下方20像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
);
},
),
ChartLables(
//X轴标签系列2
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -50.rpx), //标签相对于原点的偏移下方60像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
);
},
),
],
yLabels: [
ChartLables(
min: 0,
max: 4,
q: 4,
labels: List<String>.from(
sleepReport['dysp'][2]['yLable']
.map((e) => e['name'].toString()),
),
offset: Offset(-15.rpx, 0),
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFFFFFF).withOpacity(0.6),
fontSize: 18.rpx),
);
},
),
],
tips: buildValueTexts(sleepReport['dysp'][2]['value'], '', 1),
xCount: 7,
yCount: sleepReport['dysp'][2]['yLable'].length,
points: buildGeneralPoints(sleepReport['dysp'][2]),
displayMode: ChartDisplayMode.line,
xUnit: sleepReport['dysp'][2]['yUnit'],
barWidth: 0.1,
bottomPadding: 50,
),
padding: 45.rpx),
TrendDataTablePage(
title: sleepReport['dysp'][3]['name'],
tipText: "心率变异性趋势提示",
chartContent: LineView(
xLabels: [
ChartLables(
//标签系列1
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels:
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -16.rpx), //标签相对于原点的偏移下方20像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
);
},
),
ChartLables(
//X轴标签系列2
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -50.rpx), //标签相对于原点的偏移下方60像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
);
},
),
],
yLabels: [
ChartLables(
min: 0,
max: 5,
q: 5,
labels: List<String>.from(
sleepReport['dysp'][3]['yLable']
.map((e) => e['name'].toString()),
),
offset: Offset(-15.rpx, 0),
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFFFFFF).withOpacity(0.6),
fontSize: 18.rpx),
);
},
),
],
tips: buildValueTexts(sleepReport['dysp'][3]['value'], '毫秒', 1),
xCount: 7,
yCount: sleepReport['dysp'][3]['yLable'].length,
points: buildGeneralPoints(sleepReport['dysp'][3]),
displayMode: ChartDisplayMode.line,
xUnit: sleepReport['dysp'][3]['yUnit'],
barWidth: 0.1,
bottomPadding: 50,
),
padding: 45.rpx),
TrendDataTablePage(
title: sleepReport['dysp'][4]['name'],
tipText: "呼吸基准趋势提示",
chartContent: LineView(
xLabels: [
ChartLables(
//标签系列1
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels:
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -16.rpx), //标签相对于原点的偏移下方20像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
);
},
),
ChartLables(
//X轴标签系列2
min: 0, //最小值0
max: 7, //最大值30
q: 6, //labels第一个与最后一个的真实距离也就是30-1 = 29
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
offset: Offset(0, -50.rpx), //标签相对于原点的偏移下方60像素位置
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style:
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
);
},
),
],
yLabels: [
ChartLables(
min: 0,
max: 5,
q: 5,
labels: List<String>.from(
sleepReport['dysp'][4]['yLable']
.map((e) => e['name'].toString()),
),
offset: Offset(-20.rpx, 0),
ondrawer: (canvas, offset, index, label, align, style) {
ChartLables.drawText(
canvas,
label,
offset,
textAlign: TextAlign.center,
style: TextStyle(
color: Color(0xFFFFFFFF).withOpacity(0.6),
fontSize: 18.rpx),
);
},
),
],
tips: buildValueTexts(sleepReport['dysp'][4]['value'], '次/分', 1),
xCount: 7,
yCount: sleepReport['dysp'][4]['yLable'].length,
points: buildGeneralPoints(sleepReport['dysp'][4]),
displayMode: ChartDisplayMode.line,
xUnit: sleepReport['dysp'][4]['yUnit'],
barWidth: 0.1,
bottomPadding: 50,
),
padding: 45.rpx),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("MAC号${data['mac']}",
style: TextStyle(
color: Color(0xFFD3D3D3).withOpacity(0.2), fontSize: 18.rpx))
],
)
]
.map((widget) => Padding(
padding: padding,
child: SizedBox(width: double.infinity, child: widget),
))
.toList();
}
return Column(
children: _buildSectionList(),
);
}
// 计算非均匀标签的 y 坐标
double getYPositionBySegmentedLabels(List<double> labels, double value) {
if (value <= labels.first) return 0.0;
if (value >= labels.last) return labels.length - 1.0;
for (int i = 0; i < labels.length - 1; i++) {
double low = labels[i];
double high = labels[i + 1];
if (value >= low && value <= high) {
double ratio = (value - low) / (high - low);
return i + ratio;
}
}
// 不应该走到这里默认返回0
return 0.0;
}
//关键点标签
List<String> buildValueTexts(
List<dynamic> data,
String unit,
int direction, // 0 表示左侧1 表示右侧
) {
if (data.isEmpty) return [];
return data.where((item) => item.containsKey('value')).map((item) {
final val = item['value'].toString();
return direction == 1 ? "$val$unit" : "$unit$val";
}).toList();
}
//多个关键点标签
List<String> buildSleepValueTexts(
List<dynamic> data,
String unit,
int direction, // 0 左侧1 右侧
) {
if (data.isEmpty) return [];
return data.map((item) {
final dst = (item['dst'] ?? 0).toString();
final lst = (item['lst'] ?? 0).toString();
final slt = (item['slt'] ?? 0).toString();
final prefix = direction == 1 ? '' : unit;
final suffix = direction == 1 ? unit : '';
var q = [
"睡眠时长:$prefix$slt$suffix",
"深睡:$prefix$dst$suffix",
"浅睡:$prefix$lst$suffix",
].join("\n");
print(q);
return q;
}).toList();
}
int getWeekdayX(DateTime current, DateTime first) {
final firstMonday = first.subtract(Duration(days: first.weekday - 1));
final currentMonday = current.subtract(Duration(days: current.weekday - 1));
final weekOffset = currentMonday.difference(firstMonday).inDays ~/ 7;
return weekOffset * 7 + (current.weekday - 1);
}
Map<String, dynamic> buildWeekDatesAndPoints(Map<String, dynamic> scoreList) {
if (!scoreList.containsKey('data') || (scoreList['data'] as List).isEmpty) {
return {
'dates': [],
'points': [],
'colors': [],
};
}
List<Map<String, dynamic>> data = (scoreList['data'] as List)
.where((item) => item is Map<String, dynamic>)
.cast<Map<String, dynamic>>()
.toList();
// 提取 level -> color 映射表
Map<int, String> levelColorMap = {};
if (scoreList.containsKey('type')) {
List typeList = scoreList['type'];
for (var item in typeList) {
if (item is Map &&
item.containsKey('level') &&
item.containsKey('color')) {
levelColorMap[item['level']] = item['color'];
}
}
}
DateTime baseDate = DateTime.fromMillisecondsSinceEpoch(data.first['st']);
int weekday = baseDate.weekday;
DateTime monday = baseDate.subtract(Duration(days: weekday - 1));
List<String> dates = List.generate(7, (i) {
DateTime d = monday.add(Duration(days: i));
String month = d.month.toString().padLeft(2, '0');
String day = d.day.toString().padLeft(2, '0');
return "$month/$day";
});
List<Offset> points = [];
List<String> colors = [];
for (var item in data) {
DateTime dt = DateTime.fromMillisecondsSinceEpoch(item['st']);
double x = getWeekdayX(dt, baseDate).toDouble();
double y = (item['value'] as num?)?.toDouble() ?? 0;
int level = item['level'];
String color = levelColorMap[level] ?? "#FFFF00";
points.add(Offset(x, y));
colors.add(color);
}
return {
'dates': dates,
'points': points,
'colors': colors,
};
}
List<Offset> buildGeneralPoints(Map<String, dynamic> dyspData) {
final values =
(dyspData['value'] as List?)?.whereType<Map<String, dynamic>>().toList();
final yLabels =
(dyspData['yLable'] as List?)?.whereType<Map<String, dynamic>>().toList();
if (values == null || values.isEmpty || yLabels == null || yLabels.length < 2)
return [];
bool isTimeAxis = yLabels.first['name'].toString().contains(":");
double labelToValue(String name) {
if (isTimeAxis) {
final parts = name.split(':');
int hour = int.parse(parts[0]);
int minute = int.parse(parts[1]);
return hour * 60 + minute * 1.0;
} else {
return double.tryParse(name) ?? 0;
}
}
List<double> labelValues =
yLabels.map((e) => labelToValue(e['name'])).toList();
bool crossMidnight = false;
if (isTimeAxis && labelValues.last < labelValues.first) {
crossMidnight = true;
double base = labelValues.first;
for (int i = 1; i < labelValues.length; i++) {
if (labelValues[i] < base) {
labelValues[i] += 1440;
}
}
}
DateTime baseDate = DateTime.fromMillisecondsSinceEpoch(values.first['st']);
List<Offset> points = [];
for (var item in values) {
DateTime date = DateTime.fromMillisecondsSinceEpoch(item['st']);
double x = getWeekdayX(date, baseDate).toDouble();
dynamic rawValue = item['value'];
double valueMinBased;
if (isTimeAxis && rawValue is String) {
final parts = rawValue.split(':');
int hour = int.parse(parts[0]);
int minute = int.parse(parts[1]);
valueMinBased = hour * 60 + minute * 1.0;
if (crossMidnight && valueMinBased < labelValues.first) {
valueMinBased += 1440;
}
} else if (!isTimeAxis && rawValue is num) {
valueMinBased = rawValue.toDouble();
} else {
continue;
}
if (valueMinBased < labelValues.first) valueMinBased = labelValues.first;
if (valueMinBased > labelValues.last) valueMinBased = labelValues.last;
double y = getYPositionBySegmentedLabels(labelValues, valueMinBased);
points.add(Offset(x, y));
}
return points;
}
List<List<Offset>> buildTripleBarData(Map<String, dynamic> csd) {
final data =
(csd['data'] as List?)?.whereType<Map<String, dynamic>>().toList() ?? [];
final yLabels =
(csd['yLable'] as List?)?.whereType<Map<String, dynamic>>().toList() ??
[];
if (data.isEmpty || yLabels.isEmpty) return [];
double minY = double.tryParse(yLabels.first['name'].toString()) ?? 0;
double maxY = double.tryParse(yLabels.last['name'].toString()) ?? 10;
DateTime firstDate = DateTime.fromMillisecondsSinceEpoch(data.first['st']);
List<List<Offset>> points = [];
for (var item in data) {
double dst = (item['dst'] as num?)?.toDouble() ?? 0;
double lst = (item['lst'] as num?)?.toDouble() ?? 0;
double slt = (item['slt'] as num?)?.toDouble() ?? 0;
dst = dst.clamp(minY, maxY);
lst = lst.clamp(minY, maxY);
slt = slt.clamp(minY, maxY);
DateTime date = DateTime.fromMillisecondsSinceEpoch(item['st']);
double x = getWeekdayX(date, firstDate).toDouble();
points.add([
Offset(x, dst),
Offset(x, dst + lst),
Offset(x, slt),
]);
}
return points;
}