更新
This commit is contained in:
@@ -1,113 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
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'; // 假设你自己扩展的 .rpx
|
||||
import 'SleepdateWidget.dart'; // 导入你自定义的 SleepdateWidget
|
||||
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||||
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
|
||||
import 'package:vbvs_app/controller/date/CalendarController.dart';
|
||||
import 'SleepdateWidget.dart';
|
||||
|
||||
class SleepCalendarWidget extends StatefulWidget {
|
||||
final int? timestamp; // 可选时间戳
|
||||
final int? timestamp;
|
||||
final ValueChanged<DateTime>? onDateSelected;
|
||||
|
||||
const SleepCalendarWidget({super.key, this.timestamp});
|
||||
const SleepCalendarWidget({
|
||||
super.key,
|
||||
this.timestamp,
|
||||
this.onDateSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SleepCalendarWidget> createState() => _SleepCalendarWidgetState();
|
||||
}
|
||||
|
||||
class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
|
||||
late DateTime _currentDate;
|
||||
CalendarController calendarController = Get.find();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentDate = widget.timestamp != null
|
||||
final initialDate = widget.timestamp != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(widget.timestamp!)
|
||||
: DateTime.now();
|
||||
}
|
||||
|
||||
List<DateTime> getDaysInMonth(DateTime date) {
|
||||
// 获取当前月份的第一天
|
||||
DateTime firstDayOfMonth = DateTime(date.year, date.month, 1);
|
||||
// 获取当前月份的最后一天
|
||||
DateTime lastDayOfMonth = DateTime(date.year, date.month + 1, 0);
|
||||
List<DateTime> days = [];
|
||||
|
||||
// 获取该月的所有日期
|
||||
for (int i = 0; i < lastDayOfMonth.day; i++) {
|
||||
days.add(firstDayOfMonth.add(Duration(days: i)));
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
List<List<DateTime>> getCalendarRows(List<DateTime> daysInMonth) {
|
||||
// 获取该月所有日期后,处理为 7 列的格式
|
||||
List<List<DateTime>> calendarRows = [];
|
||||
int firstWeekday = daysInMonth.first.weekday; // 获取该月第一天是周几
|
||||
int emptyDays = (firstWeekday == 7 ? 0 : firstWeekday) - 1; // 调整为空白天数
|
||||
|
||||
List<DateTime> row = [];
|
||||
for (int i = 0; i < emptyDays; i++) {
|
||||
row.add(DateTime(0)); // 填充空白日期
|
||||
}
|
||||
|
||||
for (var day in daysInMonth) {
|
||||
row.add(day);
|
||||
if (row.length == 7) {
|
||||
// 如果当前行满了 7 个日期,则添加到 calendarRows 中,并重置 row
|
||||
calendarRows.add(List.from(row));
|
||||
row.clear();
|
||||
}
|
||||
}
|
||||
if (row.isNotEmpty) {
|
||||
// 如果最后一行的日期不足 7 个,则补充空白日期
|
||||
while (row.length < 7) {
|
||||
row.add(DateTime(0)); // 填充空白日期
|
||||
}
|
||||
calendarRows.add(List.from(row)); // 添加最后一行
|
||||
}
|
||||
return calendarRows;
|
||||
calendarController.displayedMonth.value = initialDate;
|
||||
calendarController.selectedDate.value = initialDate;
|
||||
}
|
||||
|
||||
@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), // 灰色
|
||||
},
|
||||
{"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)},
|
||||
];
|
||||
List sleepData = [];
|
||||
|
||||
// 获取当前月的所有日期
|
||||
List<DateTime> daysInMonth = getDaysInMonth(_currentDate);
|
||||
// 获取按行排列的日期
|
||||
List<List<DateTime>> calendarRows = getCalendarRows(daysInMonth);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(0),
|
||||
bottomRight: Radius.circular(0),
|
||||
topLeft: Radius.circular(20.rpx),
|
||||
topRight: Radius.circular(20.rpx),
|
||||
),
|
||||
@@ -117,46 +57,51 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
// height: MediaQuery.sizeOf(context).height * 0.055,
|
||||
constraints: BoxConstraints(
|
||||
minHeight: 90.rpx,
|
||||
),
|
||||
constraints: BoxConstraints(minHeight: 90.rpx),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF313541),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(0),
|
||||
bottomRight: Radius.circular(0),
|
||||
topLeft: Radius.circular(20.rpx),
|
||||
topRight: Radius.circular(20.rpx),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
65.rpx,
|
||||
0.rpx,
|
||||
65.rpx,
|
||||
0.rpx,
|
||||
),
|
||||
padding:
|
||||
EdgeInsetsDirectional.fromSTEB(65.rpx, 0.rpx, 65.rpx, 0.rpx),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.arrow_back_ios_new,
|
||||
color: const Color(0xFF6D6F73),
|
||||
size: 24.rpx,
|
||||
),
|
||||
Text(
|
||||
'${_currentDate.year}年${_currentDate.month}月',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 30.rpx,
|
||||
letterSpacing: 0.0,
|
||||
ClickableContainer(
|
||||
backgroundColor: Colors.transparent,
|
||||
highlightColor: Colors.grey,
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
8.rpx, 8.rpx, 8.rpx, 8.rpx),
|
||||
onTap: () => calendarController.previousMonth(),
|
||||
child: Icon(
|
||||
Icons.arrow_back_ios_new,
|
||||
color: const Color(0xFF6D6F73),
|
||||
size: 24.rpx,
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: const Color(0xFF6D6F73),
|
||||
size: 24.rpx,
|
||||
Obx(() => Text(
|
||||
'${calendarController.displayedMonth.value.year}年${calendarController.displayedMonth.value.month}月',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 30.rpx,
|
||||
letterSpacing: 0.0,
|
||||
),
|
||||
)),
|
||||
ClickableContainer(
|
||||
backgroundColor: Colors.transparent,
|
||||
highlightColor: Colors.grey,
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
8.rpx, 8.rpx, 8.rpx, 8.rpx),
|
||||
onTap: () => calendarController.nextMonth(),
|
||||
child: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: const Color(0xFF6D6F73),
|
||||
size: 24.rpx,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -164,97 +109,112 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
constraints: BoxConstraints(
|
||||
minHeight: 720.rpx,
|
||||
),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF242835),
|
||||
),
|
||||
constraints: BoxConstraints(minHeight: 720.rpx),
|
||||
decoration: const BoxDecoration(color: Color(0xFF242835)),
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
65.rpx,
|
||||
13.rpx,
|
||||
65.rpx,
|
||||
38.rpx,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
// Weekdays Header
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 90.rpx),
|
||||
child: Row(
|
||||
children: [
|
||||
for (var day in ["一", "二", "三", "四", "五", "六", "日"])
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
day,
|
||||
style: TextStyle(
|
||||
color: stringToColor("#FFFFFF"),
|
||||
fontSize: AppConstants().normal_text_fontSize,
|
||||
65.rpx, 13.rpx, 65.rpx, 38.rpx),
|
||||
child: Obx(() {
|
||||
final daysInMonth = calendarController.getDaysInMonth();
|
||||
final calendarRows =
|
||||
calendarController.getCalendarRows(daysInMonth);
|
||||
final selectedDate = calendarController.selectedDate.value;
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
// Weekdays Header
|
||||
Container(
|
||||
constraints: BoxConstraints(minHeight: 90.rpx),
|
||||
child: Row(
|
||||
children: [
|
||||
for (var day in ["一", "二", "三", "四", "五", "六", "日"])
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
day,
|
||||
style: TextStyle(
|
||||
color: stringToColor("#FFFFFF"),
|
||||
fontSize:
|
||||
AppConstants().normal_text_fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// 日历显示部分
|
||||
Column(
|
||||
children: calendarRows.map((week) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start, // 保证每一行左对齐
|
||||
children: week.map((date) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(4.rpx),
|
||||
child: date.year != 0 // 如果是空白日期就不显示
|
||||
? SleepdateWidget(date: date)
|
||||
: SizedBox.shrink(),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
SizedBox(
|
||||
height: 55.rpx,
|
||||
),
|
||||
Wrap(
|
||||
direction: Axis.horizontal, // 默认是水平排列的,可以去掉这行
|
||||
spacing: 20.rpx, // 水平间距
|
||||
runSpacing: 20.rpx, // 垂直间距
|
||||
children: showLabel.map<Widget>((item) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(5.rpx), // 可选,添加一点间距
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min, // 确保 Row 不会占满整个宽度
|
||||
children: [
|
||||
Container(
|
||||
width: 20.rpx,
|
||||
height: 20.rpx,
|
||||
decoration: BoxDecoration(
|
||||
color: item["color"],
|
||||
borderRadius:
|
||||
BorderRadius.circular(10.rpx), // 圆形效果
|
||||
// Calendar days
|
||||
Column(
|
||||
children: calendarRows.map((week) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: week.map((date) {
|
||||
if (date.year == 0) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(0.rpx),
|
||||
child: SizedBox.shrink(),
|
||||
),
|
||||
);
|
||||
}
|
||||
final isSelected = selectedDate != null &&
|
||||
date.year == selectedDate.year &&
|
||||
date.month == selectedDate.month &&
|
||||
date.day == selectedDate.day;
|
||||
return Expanded(
|
||||
child: SleepdateWidget(
|
||||
date: date,
|
||||
isSelected: isSelected,
|
||||
onTap: () {
|
||||
calendarController.selectDate(date);
|
||||
if (widget.onDateSelected != null) {
|
||||
widget.onDateSelected!(date);
|
||||
}
|
||||
print(date);
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.rpx), // 标签和文本之间的间距
|
||||
Text(
|
||||
item["name"],
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24.rpx,
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
SizedBox(height: 55.rpx),
|
||||
Wrap(
|
||||
spacing: 20.rpx,
|
||||
runSpacing: 20.rpx,
|
||||
children: showLabel.map<Widget>((item) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(5.rpx),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 20.rpx,
|
||||
height: 20.rpx,
|
||||
decoration: BoxDecoration(
|
||||
color: item["color"],
|
||||
borderRadius: BorderRadius.circular(10.rpx),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 8.rpx),
|
||||
Text(
|
||||
item["name"],
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24.rpx,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -262,3 +222,5 @@ class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,34 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:vbvs_app/common/util/FitTool.dart';
|
||||
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
|
||||
|
||||
class SleepdateWidget extends StatelessWidget {
|
||||
final DateTime date;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const SleepdateWidget({super.key, required this.date});
|
||||
const SleepdateWidget({
|
||||
super.key,
|
||||
required this.date,
|
||||
required this.isSelected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 90.rpx,
|
||||
height: 90.rpx,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(30.rpx),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(10.rpx, 10.rpx, 10.rpx, 10.rpx),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFDC1C1C), // 默认红色
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional(0, 0),
|
||||
child: Text(
|
||||
'${date.day}',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 26.rpx,
|
||||
letterSpacing: 0.0,
|
||||
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 ? Colors.black : Colors.transparent,
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(10.rpx, 10.rpx, 10.rpx, 10.rpx),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFDC1C1C),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional(0, 0),
|
||||
child: Text(
|
||||
'${date.day}',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 26.rpx,
|
||||
letterSpacing: 0.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user