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

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,8 +1,10 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
import '../../../config/app_config.dart';
import '../../../services/get/theme_controller.dart';
import '../settings/privacy.dart';
import '../settings/user-plan.dart';
import 'permission.dart';
@@ -24,6 +26,7 @@ class SpGuidePage extends StatefulWidget {
class _SpGuidePageState extends State<SpGuidePage>
with TickerProviderStateMixin {
final PageController _pageController = PageController();
final ThemeController _themeController = Get.find<ThemeController>();
TabController? _tabController;
int _currentPage = 0;
bool _firstLaunch = true;
@@ -31,7 +34,6 @@ class _SpGuidePageState extends State<SpGuidePage>
bool _agreementAccepted = false;
bool _userPlanJoined = false;
// 协议焦点状态
bool _isAgreementFocused = false;
final int _totalPages = 3;
@@ -217,58 +219,55 @@ class _SpGuidePageState extends State<SpGuidePage>
@override
Widget build(BuildContext context) {
return WillPopScope(
// 拦截返回按钮,如果不是从设置页面进入,则阻止返回
onWillPop: () async {
if (widget.fromSettings) {
return true; // 允许返回
}
// 首次启动时,阻止返回,必须完成引导流程
return false;
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: _buildAppBar(),
body: Stack(
children: [
PageView(
controller: _pageController,
scrollDirection: Axis.vertical,
physics: const PageScrollPhysics(),
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
children: [
_buildWelcomePage(),
_buildPrivacyPage(),
_buildFeaturePage(),
],
),
_buildPageIndicator(),
_buildBottomNavigation(),
],
return Obx(() {
final isDark = _themeController.isDarkMode;
return WillPopScope(
onWillPop: () async {
if (widget.fromSettings) {
return true;
}
return false;
},
child: Scaffold(
backgroundColor: isDark ? const Color(0xFF1A1A1A) : Colors.white,
appBar: _buildAppBar(isDark),
body: Stack(
children: [
PageView(
controller: _pageController,
scrollDirection: Axis.vertical,
physics: const PageScrollPhysics(),
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
children: [
_buildWelcomePage(isDark),
_buildPrivacyPage(isDark),
_buildFeaturePage(isDark),
],
),
_buildPageIndicator(isDark),
_buildBottomNavigation(isDark),
],
),
),
),
);
);
});
}
Widget _buildPageIndicator() {
// 根据当前页面计算进度条位置
// 第1页屏幕上方靠近顶部
// 第2页屏幕中间不变
// 第3页屏幕下方靠近底部
Widget _buildPageIndicator(bool isDark) {
double alignmentY;
switch (_currentPage) {
case 0:
alignmentY = -0.7; // 上方靠近顶部
alignmentY = -0.7;
break;
case 1:
alignmentY = 0.0; // 中间
alignmentY = 0.0;
break;
case 2:
alignmentY = 0.7; // 下方靠近底部
alignmentY = 0.7;
break;
default:
alignmentY = 0.0;
@@ -283,11 +282,15 @@ class _SpGuidePageState extends State<SpGuidePage>
child: Container(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.9),
color: (isDark ? const Color(0xFF2A2A2A) : Colors.white).withValues(
alpha: 0.9,
),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(2, 0),
),
@@ -318,7 +321,7 @@ class _SpGuidePageState extends State<SpGuidePage>
? AppConstants.primaryColor
: isCompleted
? AppConstants.primaryColor.withValues(alpha: 0.5)
: Colors.grey[300],
: (isDark ? Colors.grey[600] : Colors.grey[300]),
border: isActive
? Border.all(color: AppConstants.primaryColor, width: 3)
: null,
@@ -343,7 +346,7 @@ class _SpGuidePageState extends State<SpGuidePage>
);
}
PreferredSizeWidget _buildAppBar() {
PreferredSizeWidget _buildAppBar(bool isDark) {
return AppBar(
title: Text(
_pageTitles[_currentPage],
@@ -352,7 +355,7 @@ class _SpGuidePageState extends State<SpGuidePage>
fontWeight: FontWeight.bold,
),
),
backgroundColor: Colors.white,
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
elevation: 0,
centerTitle: true,
leading: widget.fromSettings
@@ -365,7 +368,7 @@ class _SpGuidePageState extends State<SpGuidePage>
);
}
Widget _buildWelcomePage() {
Widget _buildWelcomePage(bool isDark) {
return Container(
padding: const EdgeInsets.all(32),
child: Column(
@@ -388,12 +391,12 @@ class _SpGuidePageState extends State<SpGuidePage>
),
),
const SizedBox(height: 40),
const Text(
Text(
'欢迎使用',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.black87,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 16),
@@ -409,18 +412,18 @@ class _SpGuidePageState extends State<SpGuidePage>
Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
decoration: BoxDecoration(
color: Colors.grey[50],
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[50],
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
_buildWelcomeItem('🌟', '在诗词里旅行,在文化中生长'),
_buildWelcomeItem('🌟', '在诗词里旅行,在文化中生长', isDark),
const SizedBox(height: 16),
_buildWelcomeItem('📚', '海量诗词,随心阅读'),
_buildWelcomeItem('📚', '海量诗词,随心阅读', isDark),
const SizedBox(height: 16),
_buildWelcomeItem('❤️', '收藏喜爱,记录感悟'),
_buildWelcomeItem('❤️', '收藏喜爱,记录感悟', isDark),
const SizedBox(height: 16),
_buildWelcomeItem('🌙', '每日推荐,发现美好'),
_buildWelcomeItem('🌙', '每日推荐,发现美好', isDark),
],
),
),
@@ -429,12 +432,15 @@ class _SpGuidePageState extends State<SpGuidePage>
margin: const EdgeInsets.only(left: 16),
height: 1,
width: double.infinity,
color: Colors.grey[300],
color: isDark ? Colors.grey[700] : Colors.grey[300],
),
const SizedBox(height: 16),
Text(
'向上滑动继续 ↓',
style: TextStyle(fontSize: 14, color: Colors.grey[400]),
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[500] : Colors.grey[400],
),
),
const SizedBox(height: 16),
Row(
@@ -475,7 +481,6 @@ class _SpGuidePageState extends State<SpGuidePage>
),
),
const SizedBox(width: 8),
// _buildShowGuideCheckbox(),
],
),
],
@@ -483,7 +488,7 @@ class _SpGuidePageState extends State<SpGuidePage>
);
}
Widget _buildWelcomeItem(String emoji, String text) {
Widget _buildWelcomeItem(String emoji, String text, bool isDark) {
return Row(
children: [
Text(emoji, style: const TextStyle(fontSize: 20)),
@@ -491,44 +496,17 @@ class _SpGuidePageState extends State<SpGuidePage>
Expanded(
child: Text(
text,
style: const TextStyle(fontSize: 15, color: Colors.black87),
style: TextStyle(
fontSize: 15,
color: isDark ? Colors.white : Colors.black87,
),
),
),
],
);
}
Widget _buildShowGuideCheckbox() {
return GestureDetector(
onTap: () {
_toggleShowGuide(!_showGuideOnStartup);
_showGuideStatusPopup();
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Icon(
_showGuideOnStartup ? Icons.check_box : Icons.check_box_outline_blank,
color: _showGuideOnStartup
? AppConstants.primaryColor
: Colors.grey[400],
size: 24,
),
),
);
}
void _showGuideStatusPopup() {
void _showGuideStatusPopup(bool isDark) {
showDialog(
context: context,
builder: (context) => Dialog(
@@ -536,11 +514,13 @@ class _SpGuidePageState extends State<SpGuidePage>
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? const Color(0xFF1A1A1A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.15),
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.15),
blurRadius: 20,
offset: const Offset(0, 4),
),
@@ -557,13 +537,17 @@ class _SpGuidePageState extends State<SpGuidePage>
: Icons.info_outline,
color: _showGuideOnStartup
? AppConstants.primaryColor
: Colors.grey[600],
: (isDark ? Colors.grey[400] : Colors.grey[600]),
size: 24,
),
const SizedBox(width: 8),
const Text(
Text(
'通知设置',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
@@ -573,7 +557,7 @@ class _SpGuidePageState extends State<SpGuidePage>
decoration: BoxDecoration(
color: _showGuideOnStartup
? AppConstants.primaryColor.withValues(alpha: 0.1)
: Colors.grey[50],
: (isDark ? const Color(0xFF2A2A2A) : Colors.grey[50]),
borderRadius: BorderRadius.circular(12),
),
child: Column(
@@ -584,7 +568,7 @@ class _SpGuidePageState extends State<SpGuidePage>
: Icons.notifications_off,
color: _showGuideOnStartup
? AppConstants.primaryColor
: Colors.grey[500],
: (isDark ? Colors.grey[400] : Colors.grey[500]),
size: 40,
),
const SizedBox(height: 12),
@@ -595,13 +579,16 @@ class _SpGuidePageState extends State<SpGuidePage>
fontWeight: FontWeight.w600,
color: _showGuideOnStartup
? AppConstants.primaryColor
: Colors.grey[700],
: (isDark ? Colors.white : Colors.grey[700]),
),
),
const SizedBox(height: 8),
Text(
_showGuideOnStartup ? '下次启动时将显示欢迎页' : '下次启动时将不显示欢迎页',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
textAlign: TextAlign.center,
),
],
@@ -630,18 +617,20 @@ class _SpGuidePageState extends State<SpGuidePage>
);
}
Widget _buildPrivacyPage() {
Widget _buildPrivacyPage(bool isDark) {
_initTabController();
return Stack(
children: [
Column(
children: [
Container(
color: Colors.white,
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
child: TabBar(
controller: _tabController!,
labelColor: AppConstants.primaryColor,
unselectedLabelColor: Colors.grey[600],
unselectedLabelColor: isDark
? Colors.grey[400]
: Colors.grey[600],
indicatorColor: AppConstants.primaryColor,
indicatorWeight: 2,
tabs: const [
@@ -653,19 +642,16 @@ class _SpGuidePageState extends State<SpGuidePage>
Expanded(
child: Stack(
children: [
// 协议内容区域
TabBarView(
controller: _tabController!,
physics: _isAgreementFocused
? const AlwaysScrollableScrollPhysics()
: const NeverScrollableScrollPhysics(),
children: [
// 隐私政策 - 使用 NotificationListener 监听滚动
NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification) {
final metrics = notification.metrics;
// 只有在到达顶部或底部时才取消焦点
if (metrics.pixels <= metrics.minScrollExtent ||
metrics.pixels >= metrics.maxScrollExtent) {
setState(() {
@@ -691,12 +677,10 @@ class _SpGuidePageState extends State<SpGuidePage>
),
),
),
// 用户协议 - 使用 NotificationListener 监听滚动
NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification) {
final metrics = notification.metrics;
// 只有在到达顶部或底部时才取消焦点
if (metrics.pixels <= metrics.minScrollExtent ||
metrics.pixels >= metrics.maxScrollExtent) {
setState(() {
@@ -724,7 +708,6 @@ class _SpGuidePageState extends State<SpGuidePage>
),
],
),
// 左右焦点竖线
if (_isAgreementFocused)
Positioned(
left: 0,
@@ -750,7 +733,6 @@ class _SpGuidePageState extends State<SpGuidePage>
),
],
),
// 底部按钮区域 - 点击时取消协议焦点
Positioned(
left: 0,
right: 0,
@@ -766,10 +748,12 @@ class _SpGuidePageState extends State<SpGuidePage>
child: Container(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
color: isDark
? Colors.black.withValues(alpha: 0.3)
: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, -2),
),
@@ -781,7 +765,7 @@ class _SpGuidePageState extends State<SpGuidePage>
Container(
height: 1,
width: double.infinity,
color: Colors.grey[300],
color: isDark ? Colors.grey[700] : Colors.grey[300],
),
const SizedBox(height: 16),
Row(
@@ -812,7 +796,9 @@ class _SpGuidePageState extends State<SpGuidePage>
fontSize: 14,
color: _agreementAccepted
? AppConstants.primaryColor
: Colors.grey[700],
: (isDark
? Colors.grey[300]
: Colors.grey[700]),
),
),
),
@@ -829,9 +815,11 @@ class _SpGuidePageState extends State<SpGuidePage>
style: ElevatedButton.styleFrom(
backgroundColor: _agreementAccepted
? AppConstants.primaryColor
: Colors.grey[600],
: (isDark ? Colors.grey[700] : Colors.grey[600]),
foregroundColor: Colors.white,
disabledBackgroundColor: Colors.grey[300],
disabledBackgroundColor: isDark
? Colors.grey[800]
: Colors.grey[300],
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
@@ -855,41 +843,19 @@ class _SpGuidePageState extends State<SpGuidePage>
);
}
Widget _buildFeaturePage() {
Widget _buildFeaturePage(bool isDark) {
return Padding(
padding: const EdgeInsets.fromLTRB(24, 16, 24, 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Center(
// child: Container(
// width: 80,
// height: 80,
// decoration: BoxDecoration(
// color: Colors.purple.withValues(alpha: 0.1),
// borderRadius: BorderRadius.circular(20),
// ),
// child: const Center(
// child: Text('✨', style: TextStyle(fontSize: 40)),
// ),
// ),
// ),
// const SizedBox(height: 24),
// const Center(
// child: Text(
// '功能介绍',
// style: TextStyle(
// fontSize: 26,
// fontWeight: FontWeight.bold,
// color: Colors.black87,
// ),
// ),
// ),
// const SizedBox(height: 8),
Center(
child: Text(
'探索丰富的诗词世界',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
),
const SizedBox(height: 16),
@@ -903,6 +869,7 @@ class _SpGuidePageState extends State<SpGuidePage>
'每日推荐精选诗词',
Icons.home,
Colors.blue,
isDark,
),
),
const SizedBox(width: 8),
@@ -912,6 +879,7 @@ class _SpGuidePageState extends State<SpGuidePage>
'浏览排行榜、分类',
Icons.explore,
Colors.green,
isDark,
),
),
],
@@ -925,6 +893,7 @@ class _SpGuidePageState extends State<SpGuidePage>
'点赞收藏、记笔记',
Icons.favorite,
Colors.red,
isDark,
),
),
const SizedBox(width: 8),
@@ -934,6 +903,7 @@ class _SpGuidePageState extends State<SpGuidePage>
'管理数据、设置',
Icons.person,
Colors.purple,
isDark,
),
),
],
@@ -945,7 +915,7 @@ class _SpGuidePageState extends State<SpGuidePage>
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
color: Colors.grey[50],
color: isDark ? const Color(0xFF2A2A2A) : Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
@@ -993,7 +963,7 @@ class _SpGuidePageState extends State<SpGuidePage>
Switch(
value: _userPlanJoined,
onChanged: _toggleUserPlan,
activeColor: AppConstants.primaryColor,
activeThumbColor: AppConstants.primaryColor,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
@@ -1099,16 +1069,23 @@ class _SpGuidePageState extends State<SpGuidePage>
String desc,
IconData icon,
Color color,
bool isDark,
) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.withValues(alpha: 0.2)),
border: Border.all(
color: isDark
? Colors.grey[700]!
: Colors.grey.withValues(alpha: 0.2),
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.03),
color: isDark
? Colors.black.withValues(alpha: 0.2)
: Colors.black.withValues(alpha: 0.03),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -1130,14 +1107,18 @@ class _SpGuidePageState extends State<SpGuidePage>
const SizedBox(height: 8),
Text(
title,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 2),
Text(
desc,
style: TextStyle(
fontSize: 11,
color: Colors.grey[600],
color: isDark ? Colors.grey[400] : Colors.grey[600],
height: 1.3,
),
),
@@ -1146,7 +1127,7 @@ class _SpGuidePageState extends State<SpGuidePage>
);
}
Widget _buildBottomNavigation() {
Widget _buildBottomNavigation(bool isDark) {
return Positioned(
bottom: 0,
left: 0,
@@ -1161,6 +1142,7 @@ class _SpGuidePageState extends State<SpGuidePage>
icon: Icons.arrow_back_ios,
label: '上一页',
onPressed: _previousPage,
isDark: isDark,
)
: const SizedBox(width: 100),
_currentPage < _totalPages - 1
@@ -1170,11 +1152,13 @@ class _SpGuidePageState extends State<SpGuidePage>
onPressed: _currentPage == 1 && !_agreementAccepted
? null
: _nextPage,
isDark: isDark,
)
: _buildNavButton(
icon: Icons.check,
label: '完成',
onPressed: _agreementAccepted ? _finishGuide : null,
isDark: isDark,
),
],
),
@@ -1186,10 +1170,13 @@ class _SpGuidePageState extends State<SpGuidePage>
required IconData icon,
required String label,
VoidCallback? onPressed,
required bool isDark,
}) {
return Container(
decoration: BoxDecoration(
color: onPressed != null ? AppConstants.primaryColor : Colors.grey[300],
color: onPressed != null
? AppConstants.primaryColor
: (isDark ? Colors.grey[700] : Colors.grey[300]),
borderRadius: BorderRadius.circular(12),
boxShadow: onPressed != null
? [
@@ -1213,14 +1200,18 @@ class _SpGuidePageState extends State<SpGuidePage>
children: [
Icon(
icon,
color: onPressed != null ? Colors.white : Colors.grey[500],
color: onPressed != null
? Colors.white
: (isDark ? Colors.grey[400] : Colors.grey[500]),
size: 20,
),
const SizedBox(width: 8),
Text(
label,
style: TextStyle(
color: onPressed != null ? Colors.white : Colors.grey[500],
color: onPressed != null
? Colors.white
: (isDark ? Colors.grey[400] : Colors.grey[500]),
fontSize: 14,
fontWeight: FontWeight.w500,
),