Files
tuiche/lib/pages/sleep_report/chart/StatusBarWithIndicator.dart
2026-03-10 12:01:00 +08:00

358 lines
14 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:flutter/material.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 StatusBarWithIndicator extends StatelessWidget {
// final int selectKey;
// final List<Map<String, dynamic>> showLabel;
// final IconData icon;
// final double gap; // 每段之间的间距
// final bool showCurrentValue; // 新增参数,控制是否显示当前值
// final String? currentValueText; // 可选的当前值文字如果不提供则使用选中的name
// const StatusBarWithIndicator({
// super.key,
// required this.selectKey,
// required this.showLabel,
// this.icon = Icons.favorite,
// this.gap = 8.0, // 默认 8.rpx 间距
// this.showCurrentValue = false, // 默认为false
// this.currentValueText,
// });
// @override
// Widget build(BuildContext context) {
// return LayoutBuilder(builder: (context, constraints) {
// final totalWidth = constraints.maxWidth;
// final itemCount = showLabel.length;
// // 每条线的宽度 = (总宽度 - 总间隔)/ 项数
// final totalGap = (itemCount - 1) * gap.rpx;
// final itemWidth = (totalWidth - totalGap) / itemCount;
// // 找到选中项的 index 和对应的数据
// final selectedIndex = showLabel.indexWhere((e) => e['key'] == selectKey);
// final selectedItem = selectedIndex >= 0 ? showLabel[selectedIndex] : null;
// final iconLeft = selectedIndex >= 0
// ? selectedIndex * (itemWidth + gap.rpx) + itemWidth / 2
// : 0.0;
// // 确定要显示的当前值文字
// String displayValue = '';
// if (showCurrentValue) {
// if (currentValueText != null) {
// displayValue = currentValueText!;
// } else if (selectedItem != null) {
// displayValue = selectedItem['name'] ?? '';
// }
// }
// return SizedBox(
// width: double.infinity,
// child: Stack(
// clipBehavior: Clip.none,
// children: [
// if (selectedIndex >= 0) ...[
// // 如果显示当前值,在箭头上方添加文字
// if (showCurrentValue && displayValue.isNotEmpty)
// Positioned(
// left: iconLeft,
// top: -50.rpx, // 调整位置,给文字留出空间
// child: Transform.translate(
// offset: Offset(-45.rpx, 0), // 图片宽度 45.rpx居中偏移
// child: Container(
// // padding: EdgeInsets.symmetric(
// // horizontal: 8.rpx,
// // vertical: 4.rpx,
// // ),
// // decoration: BoxDecoration(
// // color: selectedItem?['color'] ?? Colors.blue,
// // borderRadius: BorderRadius.circular(4.rpx),
// // boxShadow: [
// // BoxShadow(
// // color: Colors.black.withOpacity(0.1),
// // blurRadius: 4.rpx,
// // offset: Offset(0, 2.rpx),
// // ),
// // ],
// // ),
// child: Text(
// displayValue,
// style: TextStyle(
// fontSize: AppConstants().small_an_text_fontSize,
// color: themeController.currentColor.sc9,
// fontWeight: FontWeight.w500,
// ),
// ),
// ),
// ),
// ),
// // 箭头图片
// Positioned(
// left: iconLeft,
// top: showCurrentValue ? -20.rpx : -20.rpx, // 如果有文字,箭头位置稍下移
// child: Transform.translate(
// offset: Offset(-22.5.rpx, 0), // 图片宽度 45.rpx居中偏移
// child: Container(
// width: 45.rpx,
// height: 76.rpx,
// decoration: BoxDecoration(
// image: DecorationImage(
// image: AssetImage('assets/img/tip_arrow.gif'),
// fit: BoxFit.cover,
// ),
// ),
// ),
// ),
// ),
// ],
// // 条形图和文字标签
// Padding(
// padding: EdgeInsets.only(top: showCurrentValue ? 70.rpx : 50.rpx),
// child: Column(
// children: [
// // 条形段(带间距)
// Row(
// children: showLabel.asMap().entries.map((entry) {
// int index = entry.key;
// var item = entry.value;
// return Container(
// width: itemWidth,
// height: 15.rpx,
// margin: EdgeInsets.only(
// left: index == 0 ? 0 : gap.rpx,
// ),
// decoration: BoxDecoration(
// color: item['color'],
// borderRadius: BorderRadius.circular(0.rpx),
// ),
// );
// }).toList(),
// ),
// SizedBox(height: 12.rpx),
// // 名称文字
// Row(
// children: showLabel.asMap().entries.map((entry) {
// int index = entry.key;
// var item = entry.value;
// return Container(
// width: itemWidth,
// margin: EdgeInsets.only(
// left: index == 0 ? 0 : gap.rpx,
// ),
// alignment: Alignment.center,
// child: Text(
// item['name'],
// style: TextStyle(
// fontSize: 24.rpx,
// color: Colors.white,
// ),
// textAlign: TextAlign.center,
// ),
// );
// }).toList(),
// ),
// ],
// ),
// ),
// ],
// ),
// );
// });
// }
// }
import 'package:flutter/material.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 StatusBarWithIndicator extends StatelessWidget {
final int selectKey;
final List<Map<String, dynamic>> showLabel;
final IconData icon;
final double gap; // 每段之间的间距
final bool showCurrentValue; // 控制是否显示当前值
final String? currentValueText; // 可选的当前值文字如果不提供则使用选中的name
final bool showRange; // 新增参数,控制是否显示范围
final String? Function(Map<String, dynamic> item)?
rangeTextBuilder; // 可选的范围文字构建器
const StatusBarWithIndicator({
super.key,
required this.selectKey,
required this.showLabel,
this.icon = Icons.favorite,
this.gap = 8.0, // 默认 8.rpx 间距
this.showCurrentValue = false, // 默认为false
this.currentValueText,
this.showRange = false, // 默认为false不显示范围
this.rangeTextBuilder, // 自定义范围文字构建器
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final totalWidth = constraints.maxWidth;
final itemCount = showLabel.length;
// 每条线的宽度 = (总宽度 - 总间隔)/ 项数
final totalGap = (itemCount - 1) * gap.rpx;
final itemWidth = (totalWidth - totalGap) / itemCount;
// 找到选中项的 index 和对应的数据
final selectedIndex = showLabel.indexWhere((e) => e['key'] == selectKey);
final selectedItem = selectedIndex >= 0 ? showLabel[selectedIndex] : null;
final iconLeft = selectedIndex >= 0
? selectedIndex * (itemWidth + gap.rpx) + itemWidth / 2
: 0.0;
// 确定要显示的当前值文字
String displayValue = '';
if (showCurrentValue) {
if (currentValueText != null) {
displayValue = currentValueText!;
} else if (selectedItem != null) {
displayValue = selectedItem['name'] ?? '';
}
}
// 获取范围文字
String getRangeText(Map<String, dynamic> item) {
if (rangeTextBuilder != null) {
return rangeTextBuilder!(item) ?? '';
}
// 默认返回空字符串,需要外部传入范围数据
return item['range']?.toString() ?? '';
}
return SizedBox(
width: double.infinity,
child: Stack(
clipBehavior: Clip.none,
children: [
if (selectedIndex >= 0) ...[
// 如果显示当前值,在箭头上方添加文字
if (showCurrentValue && displayValue.isNotEmpty)
Positioned(
left: iconLeft,
top: -50.rpx,
child: Transform.translate(
offset: Offset(-45.rpx, 0),
child: Text(
displayValue,
style: TextStyle(
fontSize: AppConstants().small_an_text_fontSize,
color: themeController.currentColor.sc9,
fontWeight: FontWeight.w500,
),
),
),
),
// 箭头图片
Positioned(
left: iconLeft,
top: showCurrentValue ? -20.rpx : -20.rpx,
child: Transform.translate(
offset: Offset(-22.5.rpx, 0),
child: Container(
width: 45.rpx,
height: 76.rpx,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/tip_arrow.gif'),
fit: BoxFit.cover,
),
),
),
),
),
],
// 条形图和文字标签
Padding(
padding: EdgeInsets.only(top: showCurrentValue ? 70.rpx : 50.rpx),
child: Column(
children: [
// 条形段(带间距)
Row(
children: showLabel.asMap().entries.map((entry) {
int index = entry.key;
var item = entry.value;
return Container(
width: itemWidth,
height: 15.rpx,
margin: EdgeInsets.only(
left: index == 0 ? 0 : gap.rpx,
),
decoration: BoxDecoration(
color: item['color'],
borderRadius: BorderRadius.circular(0.rpx),
),
);
}).toList(),
),
SizedBox(height: 12.rpx),
// 名称文字和范围(每个下面显示对应的范围)
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: showLabel.asMap().entries.map((entry) {
int index = entry.key;
var item = entry.value;
return Expanded(
child: Container(
margin: EdgeInsets.only(
left: index == 0 ? 0 : gap.rpx,
),
child: Column(
children: [
// 名称文字
Text(
item['name'],
style: TextStyle(
fontSize: 24.rpx,
color: Colors.white,
),
textAlign: TextAlign.center,
),
// 范围文字(如果显示)
if (showRange)
Padding(
padding: EdgeInsets.only(top: 4.rpx),
child: Text(
"(" + getRangeText(item) + ")",
style: TextStyle(
fontSize: 20.rpx,
color: Colors.white.withOpacity(0.7),
),
textAlign: TextAlign.center,
),
),
],
),
),
);
}).toList(),
),
],
),
),
],
),
);
});
}
}