更新日历样式

This commit is contained in:
czz
2025-07-10 11:16:26 +08:00
parent ceb228b256
commit 30027f40c2
10 changed files with 502 additions and 480 deletions

View File

@@ -3,8 +3,11 @@ import 'package:get/get.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';
import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/date/CalendarController.dart';
import 'package:vbvs_app/pages/main_bottom/component/main_page_b_bottom_change.dart';
import 'SleepdateWidget.dart';
class SleepCalendarWidget extends StatefulWidget {
@@ -12,9 +15,11 @@ class SleepCalendarWidget extends StatefulWidget {
final ValueChanged<DateTime>? onDateSelected;
final int? type; // 新增参数,默认日历类型为日
final Color highlightColor; // ✅ 新增
final String? mac;
const SleepCalendarWidget({
super.key,
this.mac,
this.timestamp,
this.onDateSelected,
this.type = 1,
@@ -27,27 +32,89 @@ class SleepCalendarWidget extends StatefulWidget {
class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
CalendarController calendarController = Get.find();
RxMap sleepDate = <String, dynamic>{}.obs;
RxList showLabel = <dynamic>[
{"level": 5, "name": "无报告", "color": "#9E9E9E"}
].obs;
// @override
// void initState() {
// super.initState();
// final initialDate = widget.timestamp != null
// ? DateTime.fromMillisecondsSinceEpoch(widget.timestamp!)
// : DateTime.now();
// calendarController.displayedMonth.value = initialDate;
// calendarController.selectedDate.value = initialDate;
// }
@override
void initState() {
super.initState();
final initialDate = widget.timestamp != null
? DateTime.fromMillisecondsSinceEpoch(widget.timestamp!)
: DateTime.now();
calendarController.displayedMonth.value = initialDate;
calendarController.selectedDate.value = initialDate;
// 初始化请求
fetchDate(initialDate);
// 每当月份变化时,重新请求数据
ever(calendarController.displayedMonth, (DateTime newMonth) {
fetchDate(newMonth);
});
}
Future<void> fetchDate(DateTime timeStamp) async {
final dateStr = timeStamp.toString().split(' ')[0];
await requestWithLog(
logTitle: "查询睡眠报告",
method: MyHttpMethod.get,
queryUrl:
"https://sleepdata.he-info.com/api/analysis/sleep/analysis?mac=${widget.mac}&time=$dateStr&type=3",
onSuccess: (res) {
sleepDate.value = res.data;
showLabel.value = [
...res.data['scoreList']['type'],
{"level": 5, "name": "无报告", "color": "#9E9E9E"},
// ✅ 注意拼写是 scoreList
];
},
onFailure: (res) {
sleepDate.value = {};
showLabel.value = [
{"level": 5, "name": "无报告", "color": "#9E9E9E"},
];
},
);
}
Future<void> fetchSleepColor(DateTime timeStamp) async {
final dateStr = timeStamp.toString().split(' ')[0];
await requestWithLog(
logTitle: "查询睡眠报告",
method: MyHttpMethod.get,
queryUrl: "https://sleepdata.he-info.com/api/analysis/sleep/score/type",
onSuccess: (res) {
sleepDate.value = res.data;
showLabel.value = [
...res.data['scoreList']['type'],
{"level": 5, "name": "无报告", "color": "#9E9E9E"},
// ✅ 注意拼写是 scoreList
];
},
onFailure: (res) {
sleepDate.value = {};
showLabel.value = [
{"level": 5, "name": "无报告", "color": "#9E9E9E"},
];
},
);
}
@override
Widget build(BuildContext context) {
List<Map<String, dynamic>> showLabel = [
{"level": 1, "name": "优秀", "color": Color(0xFF4CAF50)},
{"level": 2, "name": "良好", "color": Color(0xFF8BC34A)},
{"level": 3, "name": "合格", "color": Color(0xFFFFC107)},
{"level": 4, "name": "注意", "color": Color(0xFFF44336)},
{"level": 5, "name": "无报告", "color": Color(0xFF9E9E9E)},
];
return Container(
width: double.infinity,
decoration: BoxDecoration(
@@ -117,7 +184,7 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
);
}
Widget _buildCalendarBody(List<Map<String, dynamic>> showLabel) {
Widget _buildCalendarBody(List<dynamic> showLabel) {
return Container(
width: double.infinity,
constraints: BoxConstraints(minHeight: 720.rpx),
@@ -135,9 +202,8 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
children: [
// 仅当为日历模式显示周标题
_buildWeekdayHeader(),
// 日历日期格子,支持日和周的高亮逻辑
_buildCalendarGrid(calendarRows, selectedDate),
// 日历日期格子,支持日和周的高亮逻辑sleep
_buildCalendarGrid(calendarRows, selectedDate, sleepDate.value),
// TODO: 你可以扩展 month 类型的展示
SizedBox(height: 55.rpx),
_buildLegend(showLabel),
@@ -169,8 +235,8 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
);
}
Widget _buildCalendarGrid(
List<List<DateTime>> calendarRows, DateTime? selectedDate) {
Widget _buildCalendarGrid(List<List<DateTime>> calendarRows,
DateTime? selectedDate, Map sleepDate) {
final isMonthSelected = widget.type == 3;
Widget content = Column(
@@ -197,6 +263,7 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
return Expanded(
child: SleepdateWidget(
highlightColor: widget.highlightColor, // ✅ 传入高亮颜色
sleepDate: sleepDate,
date: date,
isSelected: isSelected,
onTap: () {
@@ -257,7 +324,7 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
}
}
Widget _buildLegend(List<Map<String, dynamic>> showLabel) {
Widget _buildLegend(List showLabel) {
return Wrap(
spacing: 20.rpx,
runSpacing: 20.rpx,
@@ -271,7 +338,7 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
width: 20.rpx,
height: 20.rpx,
decoration: BoxDecoration(
color: item["color"],
color: stringToColor(item["color"]),
borderRadius: BorderRadius.circular(10.rpx),
),
),

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
class SleepdateWidget extends StatelessWidget {
@@ -7,17 +8,90 @@ class SleepdateWidget extends StatelessWidget {
final bool isSelected;
final VoidCallback onTap;
final Color highlightColor; // 新增
const SleepdateWidget({
super.key,
final Map sleepDate;
SleepdateWidget({
required this.sleepDate,
required this.date,
required this.isSelected,
required this.onTap,
this.highlightColor = Colors.black, // 默认值黑色
});
@override
// Widget build(BuildContext context) {
// return ClickableContainer(
// onTap: onTap,
// backgroundColor: Colors.transparent,
// highlightColor: Colors.transparent,
// padding: EdgeInsets.all(4.rpx),
// child: Container(
// width: 90.rpx,
// height: 90.rpx,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(30.rpx),
// color: isSelected ? highlightColor : Colors.transparent, // 使用传入的颜色
// ),
// child: Padding(
// padding:
// EdgeInsetsDirectional.fromSTEB(10.rpx, 10.rpx, 10.rpx, 10.rpx),
// child: Container(
// decoration: BoxDecoration(
// color: Color(0xFF757575),
// shape: BoxShape.circle,
// ),
// alignment: Alignment.center,
// child: Text(
// '${date.day}',
// style: TextStyle(
// color: Colors.white,
// fontSize: 26.rpx,
// letterSpacing: 0.0,
// ),
// ),
// ),
// ),
// ),
// );
// }
@override
Widget build(BuildContext context) {
Color? fillColor;
// 判断是否存在 score 数据
final List<dynamic>? dataList = sleepDate['scoreList']?['data'];
final List<dynamic>? typeList = sleepDate['scoreList']?['type'];
if (dataList != null && typeList != null) {
// 查找是否有匹配日期的数据
for (var item in dataList) {
final st = item['st'];
final level = item['level'];
if (st is int) {
final itemDate =
DateTime.fromMillisecondsSinceEpoch(st).toLocal(); // 转为本地时间
// 判断是否是同一天
if (itemDate.year == date.year &&
itemDate.month == date.month &&
itemDate.day == date.day) {
// 找到对应 level 的颜色
final matchType = typeList.firstWhere(
(e) => e['level'] == level,
orElse: () => null,
);
if (matchType != null && matchType['color'] != null) {
final hexColor = matchType['color'];
fillColor = stringToColor(hexColor);
}
break; // 找到就跳出
}
}
}
}
return ClickableContainer(
onTap: onTap,
backgroundColor: Colors.transparent,
@@ -28,14 +102,14 @@ class SleepdateWidget extends StatelessWidget {
height: 90.rpx,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.rpx),
color: isSelected ? highlightColor : Colors.transparent, // 使用传入的颜色
color: isSelected ? highlightColor : Colors.transparent,
),
child: Padding(
padding:
EdgeInsetsDirectional.fromSTEB(10.rpx, 10.rpx, 10.rpx, 10.rpx),
child: Container(
decoration: BoxDecoration(
color: Color(0xFF757575),
color: fillColor ?? Color(0xFF757575), // 如果匹配不到就默认灰色
shape: BoxShape.circle,
),
alignment: Alignment.center,