深色模式、首页设置页面和功能优化

This commit is contained in:
Developer
2026-04-02 07:06:55 +08:00
parent f0a62ed68b
commit 954d173329
88 changed files with 12157 additions and 7578 deletions

View File

@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constants/app_constants.dart';
import '../../services/get/theme_controller.dart';
/// 时间: 2026-03-26
/// 功能: 活跃页面
/// 介绍: 展示用户活跃度热力图,参考 GitHub 贡献图样式
/// 最新变化: 添加调试选项,修复布局溢出,调整活跃阈值颜色
/// 最新变化: 2026-04-02 支持深色模式
class RatePage extends StatefulWidget {
const RatePage({super.key});
@@ -17,13 +19,12 @@ class _RatePageState extends State<RatePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
final List<String> _tabs = ['周活跃', '月活跃'];
final ThemeController _themeController = Get.find<ThemeController>();
// 调试参数
int _debugDayCount = 7;
int _debugBarCount = 7;
bool _showDebugPanel = false;
// 模拟活跃度数据 (实际条数)
List<int> _weekData = [];
List<List<int>> _monthData = [];
@@ -40,10 +41,8 @@ class _RatePageState extends State<RatePage>
super.dispose();
}
// 生成模拟数据
void _generateMockData() {
_weekData = List.generate(_debugDayCount, (index) {
// 生成 0-150 的随机活跃值
return [0, 3, 8, 15, 35, 80, 150][index % 7];
});
@@ -56,46 +55,45 @@ class _RatePageState extends State<RatePage>
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: Column(
children: [
// Tab 切换栏
Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(bottom: BorderSide(color: Colors.grey[200]!)),
return Obx(() {
final isDark = _themeController.isDarkModeRx.value;
return Scaffold(
backgroundColor: isDark ? const Color(0xFF121212) : Theme.of(context).scaffoldBackgroundColor,
body: Column(
children: [
Container(
decoration: BoxDecoration(
color: isDark ? Colors.grey[900] : Colors.white,
border: Border(bottom: BorderSide(color: isDark ? Colors.grey[800]! : Colors.grey[200]!)),
),
child: TabBar(
controller: _tabController,
tabs: _tabs.map((tab) => Tab(text: tab)).toList(),
labelColor: AppConstants.primaryColor,
unselectedLabelColor: isDark ? Colors.grey[400] : Colors.grey[600],
indicatorColor: AppConstants.primaryColor,
indicatorWeight: 2,
labelStyle: const TextStyle(fontWeight: FontWeight.w600),
),
),
child: TabBar(
controller: _tabController,
tabs: _tabs.map((tab) => Tab(text: tab)).toList(),
labelColor: AppConstants.primaryColor,
unselectedLabelColor: Colors.grey[600],
indicatorColor: AppConstants.primaryColor,
indicatorWeight: 2,
labelStyle: const TextStyle(fontWeight: FontWeight.w600),
_buildDebugToggle(isDark),
if (_showDebugPanel) _buildDebugPanel(isDark),
Expanded(
child: TabBarView(
controller: _tabController,
children: [_buildWeekView(isDark), _buildMonthView(isDark)],
),
),
),
// 调试开关
_buildDebugToggle(),
// 调试面板
if (_showDebugPanel) _buildDebugPanel(),
// 内容区域
Expanded(
child: TabBarView(
controller: _tabController,
children: [_buildWeekView(), _buildMonthView()],
),
),
],
),
);
],
),
);
});
}
// 调试开关
Widget _buildDebugToggle() {
Widget _buildDebugToggle(bool isDark) {
return Container(
color: Colors.orange[50],
color: isDark ? Colors.orange[900]!.withAlpha(50) : Colors.orange[50],
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
@@ -130,21 +128,20 @@ class _RatePageState extends State<RatePage>
);
}
// 调试面板
Widget _buildDebugPanel() {
Widget _buildDebugPanel(bool isDark) {
return Container(
color: Colors.orange[50],
color: isDark ? Colors.orange[900]!.withAlpha(50) : Colors.orange[50],
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// 天数调节
_buildDebugSlider(
label: '天数',
value: _debugDayCount.toDouble(),
min: 3,
max: 31,
isDark: isDark,
onChanged: (value) {
setState(() {
_debugDayCount = value.round();
@@ -153,12 +150,12 @@ class _RatePageState extends State<RatePage>
},
),
const SizedBox(height: 12),
// 条数调节
_buildDebugSlider(
label: '条数',
value: _debugBarCount.toDouble(),
min: 3,
max: 14,
isDark: isDark,
onChanged: (value) {
setState(() {
_debugBarCount = value.round();
@@ -167,11 +164,10 @@ class _RatePageState extends State<RatePage>
},
),
const SizedBox(height: 16),
// 活跃阈值说明
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? Colors.grey[850] : Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange[200]!),
),
@@ -189,19 +185,22 @@ class _RatePageState extends State<RatePage>
_buildThresholdItem(
'1-5',
'浅色',
AppConstants.primaryColor.withValues(alpha: 0.3),
AppConstants.primaryColor.withAlpha(77),
isDark,
),
_buildThresholdItem(
'6-20',
'中浅色',
AppConstants.primaryColor.withValues(alpha: 0.5),
AppConstants.primaryColor.withAlpha(128),
isDark,
),
_buildThresholdItem(
'21-100',
'中深色',
AppConstants.primaryColor.withValues(alpha: 0.7),
AppConstants.primaryColor.withAlpha(179),
isDark,
),
_buildThresholdItem('100+', '最深色', AppConstants.primaryColor),
_buildThresholdItem('100+', '最深色', AppConstants.primaryColor, isDark),
],
),
),
@@ -210,12 +209,12 @@ class _RatePageState extends State<RatePage>
);
}
// 调试滑块
Widget _buildDebugSlider({
required String label,
required double value,
required double min,
required double max,
required bool isDark,
required ValueChanged<double> onChanged,
}) {
return Row(
@@ -253,8 +252,7 @@ class _RatePageState extends State<RatePage>
);
}
// 阈值项
Widget _buildThresholdItem(String range, String label, Color color) {
Widget _buildThresholdItem(String range, String label, Color color, bool isDark) {
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
@@ -270,15 +268,14 @@ class _RatePageState extends State<RatePage>
const SizedBox(width: 8),
Text(
'$range: $label',
style: TextStyle(fontSize: 12, color: Colors.grey[700]),
style: TextStyle(fontSize: 12, color: isDark ? Colors.grey[300] : Colors.grey[700]),
),
],
),
);
}
// 周活跃视图
Widget _buildWeekView() {
Widget _buildWeekView(bool isDark) {
final weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
return SingleChildScrollView(
@@ -286,17 +283,16 @@ class _RatePageState extends State<RatePage>
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('本周活跃趋势(生成图片分享)'),
_buildSectionTitle('本周活跃趋势(生成图片分享)', isDark),
const SizedBox(height: 16),
// 热力图 - 使用 Wrap 防止溢出
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? Colors.grey[850] : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
color: Colors.black.withAlpha(isDark ? 0 : 13),
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -304,7 +300,6 @@ class _RatePageState extends State<RatePage>
),
child: Column(
children: [
// 星期标签
Wrap(
spacing: 8,
runSpacing: 8,
@@ -317,7 +312,7 @@ class _RatePageState extends State<RatePage>
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
@@ -325,44 +320,42 @@ class _RatePageState extends State<RatePage>
}),
),
const SizedBox(height: 12),
// 活跃度方块
Wrap(
spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.center,
children: List.generate(_weekData.length, (index) {
return _buildActivityBlock(_weekData[index], size: 40);
return _buildActivityBlock(_weekData[index], size: 40, isDark: isDark);
}),
),
],
),
),
const SizedBox(height: 16),
_buildStatsSection(),
_buildStatsSection(isDark),
const SizedBox(height: 16),
_buildLegend(),
_buildLegend(isDark),
],
),
);
}
// 月活跃视图
Widget _buildMonthView() {
Widget _buildMonthView(bool isDark) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('本月活跃热力图'),
_buildSectionTitle('本月活跃热力图', isDark),
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? Colors.grey[850] : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
color: Colors.black.withAlpha(isDark ? 0 : 13),
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -370,7 +363,6 @@ class _RatePageState extends State<RatePage>
),
child: Column(
children: [
// 星期标签
Wrap(
spacing: 4,
alignment: WrapAlignment.center,
@@ -383,7 +375,7 @@ class _RatePageState extends State<RatePage>
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 10,
color: Colors.grey[500],
color: isDark ? Colors.grey[500] : Colors.grey[500],
),
),
),
@@ -391,19 +383,17 @@ class _RatePageState extends State<RatePage>
.toList(),
),
const SizedBox(height: 8),
// 四周热力图 - 使用 Wrap 防止溢出
...List.generate(4, (weekIndex) {
return Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Wrap(
spacing: 4,
alignment: WrapAlignment.center,
children: List.generate(_monthData[weekIndex].length, (
dayIndex,
) {
children: List.generate(_monthData[weekIndex].length, (dayIndex) {
return _buildActivityBlock(
_monthData[weekIndex][dayIndex],
size: 28,
isDark: isDark,
);
}),
),
@@ -413,16 +403,15 @@ class _RatePageState extends State<RatePage>
),
),
const SizedBox(height: 16),
_buildStatsSection(),
_buildStatsSection(isDark),
const SizedBox(height: 16),
_buildLegend(),
_buildLegend(isDark),
],
),
);
}
// 活跃度方块
Widget _buildActivityBlock(int value, {required double size}) {
Widget _buildActivityBlock(int value, {required double size, required bool isDark}) {
return Tooltip(
message: '活跃度: $value',
child: Container(
@@ -431,14 +420,14 @@ class _RatePageState extends State<RatePage>
decoration: BoxDecoration(
color: _getActivityColor(value),
borderRadius: BorderRadius.circular(4),
border: value == 0 ? Border.all(color: Colors.grey[200]!) : null,
border: value == 0 ? Border.all(color: isDark ? Colors.grey[700]! : Colors.grey[200]!) : null,
),
child: value > 0
? Center(
child: value >= 100
? Icon(
Icons.local_fire_department,
color: Colors.white.withValues(alpha: 0.9),
color: Colors.white.withAlpha(230),
size: size * 0.6,
)
: null,
@@ -448,27 +437,21 @@ class _RatePageState extends State<RatePage>
);
}
// 获取活跃度颜色 (根据新的阈值)
Color _getActivityColor(int value) {
if (value == 0) {
return Colors.grey[100]!;
} else if (value >= 1 && value <= 5) {
// 1-5: 浅色
return AppConstants.primaryColor.withValues(alpha: 0.3);
return AppConstants.primaryColor.withAlpha(77);
} else if (value >= 6 && value <= 20) {
// 6-20: 中浅色
return AppConstants.primaryColor.withValues(alpha: 0.5);
return AppConstants.primaryColor.withAlpha(128);
} else if (value >= 21 && value <= 100) {
// 21-100: 中深色
return AppConstants.primaryColor.withValues(alpha: 0.7);
return AppConstants.primaryColor.withAlpha(179);
} else {
// 100+: 最深色
return AppConstants.primaryColor;
}
}
// 标题组件
Widget _buildSectionTitle(String title) {
Widget _buildSectionTitle(String title, bool isDark) {
return Row(
children: [
Container(
@@ -482,22 +465,21 @@ class _RatePageState extends State<RatePage>
const SizedBox(width: 8),
Text(
title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: isDark ? Colors.white : Colors.black),
),
],
);
}
// 统计信息
Widget _buildStatsSection() {
Widget _buildStatsSection(bool isDark) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? Colors.grey[850] : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
color: Colors.black.withAlpha(isDark ? 0 : 13),
blurRadius: 10,
offset: const Offset(0, 2),
),
@@ -506,64 +488,53 @@ class _RatePageState extends State<RatePage>
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem('活跃天数', '12', Icons.calendar_today),
_buildStatItem('连续活跃', '5', Icons.local_fire_department),
_buildStatItem('总活跃度', '89%', Icons.trending_up),
_buildStatItem('活跃天数', '12', Icons.calendar_today, isDark),
_buildStatItem('连续活跃', '5', Icons.local_fire_department, isDark),
_buildStatItem('总活跃度', '89%', Icons.trending_up, isDark),
],
),
);
}
// 统计项
Widget _buildStatItem(String label, String value, IconData icon) {
Widget _buildStatItem(String label, String value, IconData icon, bool isDark) {
return Column(
children: [
Icon(icon, color: AppConstants.primaryColor, size: 24),
const SizedBox(height: 8),
Text(
value,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: isDark ? Colors.white : Colors.black),
),
const SizedBox(height: 4),
Text(label, style: TextStyle(fontSize: 12, color: Colors.grey[600])),
Text(label, style: TextStyle(fontSize: 12, color: isDark ? Colors.grey[400] : Colors.grey[600])),
],
);
}
// 图例说明
Widget _buildLegend() {
Widget _buildLegend(bool isDark) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? Colors.grey[850] : Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
Text(
'活跃度说明',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: isDark ? Colors.white : Colors.black),
),
const SizedBox(height: 12),
Wrap(
spacing: 12,
runSpacing: 8,
children: [
_buildLegendItem('无活跃', Colors.grey[100]!),
_buildLegendItem(
'1-5',
AppConstants.primaryColor.withValues(alpha: 0.3),
),
_buildLegendItem(
'6-20',
AppConstants.primaryColor.withValues(alpha: 0.5),
),
_buildLegendItem(
'21-100',
AppConstants.primaryColor.withValues(alpha: 0.7),
),
_buildLegendItem('100+', AppConstants.primaryColor),
_buildLegendItem('无活跃', Colors.grey[100]!, isDark),
_buildLegendItem('1-5', AppConstants.primaryColor.withAlpha(77), isDark),
_buildLegendItem('6-20', AppConstants.primaryColor.withAlpha(128), isDark),
_buildLegendItem('21-100', AppConstants.primaryColor.withAlpha(179), isDark),
_buildLegendItem('100+', AppConstants.primaryColor, isDark),
],
),
],
@@ -571,8 +542,7 @@ class _RatePageState extends State<RatePage>
);
}
// 图例项
Widget _buildLegendItem(String label, Color color) {
Widget _buildLegendItem(String label, Color color, bool isDark) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
@@ -582,13 +552,11 @@ class _RatePageState extends State<RatePage>
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(2),
border: label == '无活跃'
? Border.all(color: Colors.grey[300]!)
: null,
border: label == '无活跃' ? Border.all(color: isDark ? Colors.grey[600]! : Colors.grey[300]!) : null,
),
),
const SizedBox(width: 4),
Text(label, style: TextStyle(fontSize: 11, color: Colors.grey[600])),
Text(label, style: TextStyle(fontSize: 11, color: isDark ? Colors.grey[400] : Colors.grey[600])),
],
);
}