/// 时间: 2025.03.21 /// 功能: 个人页面(类似朋友圈布局) /// 介绍: 展示用户头像、个性签名、昵称、统计信息和设置列表,支持左右滑动切换页面 /// 最新变化: 重新设计布局,实现朋友圈风格的个人页面 import 'dart:convert'; import 'dart:math' show Random; import 'dart:io' as io; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import '../../constants/app_constants.dart'; import '../../controllers/history_controller.dart'; import '../../controllers/shared_preferences_storage_controller.dart'; import 'history_page.dart'; import 'per_card.dart'; import 'settings/app_fun.dart'; import 'settings/user-plan.dart'; import 'settings/offline-data.dart'; import 'settings/privacy.dart'; import 'settings/learn-us.dart'; import 'app-info.dart'; import 'level/poetry.dart'; import 'guide/permission.dart'; import 'guide/app-data.dart'; import 'guide/tongji.dart'; import 'theme/app-diy.dart'; import 'expand/vote.dart'; import 'expand/manu-script.dart'; import 'components/bug_list_page.dart'; import 'components/pop-menu.dart'; class ProfilePage extends StatefulWidget { const ProfilePage({super.key}); @override State createState() => _ProfilePageState(); } class _ProfilePageState extends State with TickerProviderStateMixin, WidgetsBindingObserver { late PageController _pageController; late TabController _tabController; int _currentPage = 1; // 默认显示第2页(设置) bool _isCardExpanded = false; // 个人卡片展开状态 bool _isStatsHidden = false; // 统计数据隐藏状态 bool _isScreenWakeEnabled = false; // 屏幕常亮状态 double _startY = 0.0; // 历史记录相关 List> _poetryHistory = []; // PersonalCard 的 Key,用于调用其刷新方法 final GlobalKey _personalCardKey = GlobalKey(); // 答题统计数据 int _correctAnswers = 0; int _weekQuestions = 0; // 统计数据 int _todayViews = 0; int _weekViews = 0; String _firstUseTime = '未记录'; int _useDays = 1; String _dataSize = '0 B'; int _noteCount = 0; int _totalQuestions = 0; int _todayQuestions = 0; int _todayLikes = 0; // 可爱的emoji列表 final List _avatars = [ '👤', '😊', '🎉', '🌟', '🔥', '💎', '🌈', '🦋', '🌸', '🐱', '🐶', '🐼', '🐨', '🐵', '🦄', '🐸', '🐹', '🐰', '🦊', '🐻', ]; // 模拟用户数据 final Map _userData = { 'avatar': '👤', // 使用emoji代替网络图片 'nickname': '诗词爱好者', 'signature': '人生如诗,岁月如歌', 'level': 'Lv.12', 'vip': true, 'posts': 156, 'followers': 1280, 'following': 89, 'likes': 2560, 'favorites': 128, 'views': 3560, }; // 更换头像 void _changeAvatar() { final random = Random(); setState(() { _userData['avatar'] = _avatars[random.nextInt(_avatars.length)]; }); } @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _pageController = PageController(initialPage: 1); _tabController = TabController(length: 3, vsync: this); _loadPoetryHistory(); _loadPoetryStatistics(); _loadStatistics(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); _pageController.dispose(); _tabController.dispose(); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { // 应用恢复时刷新数据 refreshData(); } } // 刷新所有数据(公共方法,供外部调用) Future refreshData() async { await _loadPoetryStatistics(); await _loadStatistics(); // 同时刷新个人卡片的数据 final personalCardState = _personalCardKey.currentState; if (personalCardState != null && personalCardState.mounted) { await personalCardState.refreshData(); } } void _onPageChanged(int page) { setState(() { _currentPage = page; }); HapticFeedback.lightImpact(); } // === 历史记录相关方法 === Future _loadPoetryHistory() async { try { final history = await HistoryController.getHistory(); setState(() { _poetryHistory = history; }); } catch (e) {} } // === 答题统计相关方法 === Future _loadPoetryStatistics() async { try { // 加载总体统计 _correctAnswers = await SharedPreferencesStorageController.getInt( 'correctAnswers', defaultValue: 0, ); // 加载答题记录列表来计算今日和本周答题数 List records = await SharedPreferencesStorageController.getStringList( 'poetryAnswerRecords', defaultValue: [], ); final now = DateTime.now(); final todayStart = DateTime(now.year, now.month, now.day); final weekStart = todayStart.subtract( Duration(days: todayStart.weekday - 1), ); _todayQuestions = 0; _weekQuestions = 0; for (String recordStr in records) { try { final record = jsonDecode(recordStr) as Map; final answerTime = record['answerTime']; if (answerTime != null) { final time = DateTime.parse(answerTime); if (time.isAfter(todayStart)) { _todayQuestions++; } if (time.isAfter(weekStart)) { _weekQuestions++; } } } catch (e) { continue; } } setState(() {}); } catch (e) { // 加载失败 } } // === 加载统计数据 === Future _loadStatistics() async { try { // 加载浏览统计 _todayViews = await StatisticsManager().getTodayViews(); _weekViews = await StatisticsManager().getWeekViews(); _firstUseTime = await StatisticsManager().getFirstUseTime(); _useDays = await StatisticsManager().getUseDays(); _dataSize = await StatisticsManager().getDataSize(); _todayLikes = await StatisticsManager().getTodayLikes(); _todayQuestions = await StatisticsManager().getTodayQuestions(); // 加载笔记总数 _noteCount = await HistoryController.getNotesCount(); // 加载累计答题数 _totalQuestions = await SharedPreferencesStorageController.getInt( 'totalQuestions', defaultValue: 0, ); setState(() {}); } catch (e) { // 加载失败 } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: _buildAppBar(), body: GestureDetector( behavior: HitTestBehavior.opaque, // 确保手势检测能够捕获整个区域的事件 onVerticalDragStart: (details) { _startY = details.globalPosition.dy; }, onVerticalDragUpdate: (details) { double currentY = details.globalPosition.dy; double deltaY = currentY - _startY; if (deltaY > 20) { // 下拉超过20像素,张开卡片(降低阈值提高灵敏度) if (!_isCardExpanded) { setState(() { _isCardExpanded = true; }); } } else if (deltaY < -60) { // 上滑超过60像素,收起卡片(降低阈值提高灵敏度) if (_isCardExpanded) { setState(() { _isCardExpanded = false; }); } } }, child: Column( children: [ _buildProfileHeader(), Expanded( child: PageView( controller: _pageController, onPageChanged: _onPageChanged, children: [ _buildPage1(), // 个人信息卡片 _buildPage2(), // 设置列表 _buildPage3(), // 其他功能 ], ), ), ], ), ), ); } PreferredSizeWidget _buildAppBar() { // === 顶部导航栏:显示页面标题 === return AppBar( title: Text( '个人', style: TextStyle( color: AppConstants.primaryColor, fontWeight: FontWeight.bold, ), ), backgroundColor: Colors.white, elevation: 0, centerTitle: true, actions: [ // 右上角更多按钮 IconButton( icon: Icon(Icons.more_horiz, color: AppConstants.primaryColor), onPressed: _showMoreOptions, ), ], ); } Widget _buildProfileHeader() { // === 个人信息头部:可收起/张开的个人卡片 === return PersonalCard( key: _personalCardKey, userData: _userData, isExpanded: _isCardExpanded, onExpandChanged: (value) { setState(() { _isCardExpanded = value; }); }, currentPage: _currentPage, onPageChanged: (page) { // 添加 mounted 和 hasClients 检查,避免 dispose 后调用导致黑屏 if (mounted && _pageController.hasClients) { _pageController.animateToPage( page, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); } }, onAvatarTap: _changeAvatar, ); } Widget _buildPage1() { // === 第1页:个人信息卡片展示 === return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( children: [ // 个人信息卡片 Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.08), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // === 卡片标题 === Row( children: [ Icon( Icons.person_outline, color: AppConstants.primaryColor, size: 20, ), const SizedBox(width: 8), Text( '统计', style: TextStyle( color: AppConstants.primaryColor, fontSize: 16, fontWeight: FontWeight.bold, ), ), const Spacer(), IconButton( icon: Icon( _isStatsHidden ? Icons.visibility_off_outlined : Icons.visibility_outlined, color: Colors.grey[600], size: 20, ), onPressed: () { setState(() { _isStatsHidden = !_isStatsHidden; }); HapticFeedback.lightImpact(); }, tooltip: _isStatsHidden ? '显示数据' : '隐藏数据', ), ], ), const SizedBox(height: 16), // === 信息列表 === if (!_isStatsHidden) ...[ _buildInfoItem('今日浏览', '$_todayViews 条'), _buildInfoItem('本周浏览', '$_weekViews 条'), _buildInfoItem('已用', '$_useDays 天'), _buildInfoItem('数据', _dataSize), _buildInfoItem('笔记', '$_noteCount 个'), _buildInfoItem('今日答题', '$_todayQuestions 题'), _buildInfoItem('今日点赞', '$_todayLikes 个'), ] else Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Text( '数据已隐藏', style: TextStyle(color: Colors.grey[500], fontSize: 14), ), ), ), ], ), ), const SizedBox(height: 16), // === 互动统计卡片 === Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.08), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.analytics_outlined, color: AppConstants.primaryColor, size: 20, ), const SizedBox(width: 8), Text( '诗词挑战', style: TextStyle( color: AppConstants.primaryColor, fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), if (!_isStatsHidden) Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildInteractionItem('今日答题', '$_todayQuestions'), _buildInteractionItem('本周答题', '$_weekQuestions'), _buildInteractionItem('答对次数', '$_correctAnswers'), ], ) else Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Text( '数据已隐藏', style: TextStyle(color: Colors.grey[500], fontSize: 14), ), ), ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => const PoetryLevelPage(), ), ); }, style: ElevatedButton.styleFrom( backgroundColor: AppConstants.primaryColor, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('开始诗词答题', style: TextStyle(fontSize: 16)), ), ), ], ), ), ], ), ); } Widget _buildPage2() { // === 第2页:设置列表 === return ListView( padding: const EdgeInsets.all(16), children: [ // === 账户设置组 === _buildSettingsGroup('软件设置', [ _buildSettingsItem( '功能设置', Icons.verified_user, () => _navigateToAppFunSettings(), ), _buildSettingsItem( '离线使用', Icons.volunteer_activism, () => _navigateToOfflineDataPage(), ), _buildSettingsItem( '历史记录', Icons.history, () => _navigateToHistoryPage(), ), _buildSettingsItem( '主题风格', Icons.palette, () => _navigateToAppDiyPage(), ), ]), const SizedBox(height: 16), // === 隐私设置组 === _buildSettingsGroup('隐私设置', [ _buildSettingsItem( '权限管理', Icons.block, () => _navigateToPermissionPage(), ), _buildSettingsItem( '应用数据', Icons.security, () => _navigateToAppDataPage(), ), //撤回同意 _buildSettingsItem( '软件协议', Icons.description, () => _navigateToPrivacyPage(), ), ]), const SizedBox(height: 16), // === 通用设置组 === _buildSettingsGroup('软件信息', [ _buildSettingsItem( '应用信息', Icons.phone_android, () => _navigateToAppInfoPage(), ), _buildSettingsItem( '了解我们', Icons.info, () => _navigateToLearnUsPage(), ), _buildSettingsItem( '已知bug', Icons.cleaning_services, () => showBugListBottomSheet(context), ), _buildScreenWakeItem(), ]), ], ); } Widget _buildPage3() { // === 第3页:其他功能列表 === return ListView( padding: const EdgeInsets.all(16), children: [ // === 功能服务组 === _buildSettingsGroup('用户体验计划(限免)', [ _buildSettingsItem( '加入体验', Icons.edit, () => _navigateToUserPlanPage(), ), _buildSettingsItem( '参与投票', Icons.how_to_vote, () => _navigateToVotePage(), ), _buildSettingsItem( '查看全站统计', Icons.history, () => _showSnackBar('查看全站统计'), ), _buildSettingsItem( '开发计划', Icons.analytics, () => _showSnackBar('软件开发进度'), ), _buildSettingsItem('去投稿', Icons.edit_note, () { Navigator.push( context, MaterialPageRoute(builder: (context) => const ManuscriptPage()), ); }), ]), const SizedBox(height: 16), // === 帮助支持组 === // _buildSettingsGroup('帮助支持', [ // _buildSettingsItem( // '使用教程', // Icons.help_outline, // () => _navigateToSpGuidePage(), // ), // _buildSettingsItem( // '意见反馈', // Icons.feedback, // () => _showSnackBar('意见反馈'), // ), // _buildSettingsItem( // '联系客服', // Icons.support_agent, // () => _showSnackBar('联系客服'), // ), // _buildSettingsItem( // '商务合作', // Icons.info_outline, // () => _showSnackBar('商务合作'), // ), // ]), // const SizedBox(height: 16), // === 关于信息组 === Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.08), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.info, color: AppConstants.primaryColor, size: 20), const SizedBox(width: 8), Text( '关于应用', style: TextStyle( color: AppConstants.primaryColor, fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 16), Text( AppConstants.appName, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( '版本 ${AppConstants.appVersion}', style: const TextStyle(fontSize: 14, color: Colors.grey), ), const SizedBox(height: 16), const Text( '基于Flutter开发的现代化诗词应用,致力于为用户提供优质的诗词阅读和创作体验。', style: TextStyle( fontSize: 14, color: Colors.black87, height: 1.5, ), textAlign: TextAlign.center, ), ], ), ), ], ); } Widget _buildInfoItem(String label, String value) { // === 信息项:显示标签和值 === return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: const TextStyle( fontSize: 14, color: Color(0xFF9E9E9E), // 使用具体颜色值代替Colors.grey[600] ), ), Text( value, style: const TextStyle( fontSize: 14, color: Colors.black87, fontWeight: FontWeight.w500, ), ), ], ), ); } Widget _buildSettingsGroup(String title, List items) { // === 设置组:显示组标题和设置项列表 === return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.08), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 组标题 Padding( padding: const EdgeInsets.all(16), child: Row( children: [ Icon( Icons.settings_outlined, color: AppConstants.primaryColor, size: 20, ), const SizedBox(width: 8), Text( title, style: TextStyle( color: AppConstants.primaryColor, fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), ), const Divider(height: 1), // 设置项列表 ...items, ], ), ); } Widget _buildSettingsItem(String title, IconData icon, VoidCallback onTap) { // === 设置项:显示图标、标题和箭头 === return ListTile( leading: Icon(icon, color: AppConstants.primaryColor, size: 20), title: Text( title, style: const TextStyle(fontSize: 14, color: Colors.black87), ), trailing: const Icon(Icons.chevron_right, color: Colors.grey, size: 20), onTap: () { HapticFeedback.lightImpact(); onTap(); }, ); } Widget _buildScreenWakeItem() { // === 屏幕常亮设置项:显示图标、标题和开关 === return ListTile( leading: Icon( Icons.screen_lock_rotation, color: AppConstants.primaryColor, size: 20, ), title: Text( '屏幕常亮', style: const TextStyle(fontSize: 14, color: Colors.black87), ), trailing: Switch( value: _isScreenWakeEnabled, onChanged: (value) async { if (value) { // 显示提示对话框 showDialog( context: context, builder: (context) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), title: Row( children: [ Icon( Icons.info_outline, color: AppConstants.primaryColor, size: 20, ), const SizedBox(width: 8), const Text( '屏幕常亮提示', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 8), Text( '⚠️ 注意事项', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.orange[600], ), ), const SizedBox(height: 8), Text( '• OLED屏幕长时间显示相同内容可能会造成不可逆老化', style: const TextStyle( fontSize: 14, color: Colors.black87, ), ), const SizedBox(height: 6), Text( '• 开启常亮后,配合自动刷新内容的设置,实现常亮自动加载诗句', style: const TextStyle( fontSize: 14, color: Colors.black87, ), ), const SizedBox(height: 6), Text( '• 挂起常亮后,需保持该软件在屏幕中显示,记得关闭常亮哦!', style: const TextStyle( fontSize: 14, color: Colors.black87, ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), style: TextButton.styleFrom( foregroundColor: Colors.grey[600], ), child: const Text('取消'), ), TextButton( onPressed: () async { Navigator.pop(context); await _toggleScreenWake(true); }, style: TextButton.styleFrom( foregroundColor: AppConstants.primaryColor, textStyle: const TextStyle(fontWeight: FontWeight.bold), ), child: const Text('确定'), ), ], ), ); } else { await _toggleScreenWake(false); } }, activeColor: AppConstants.primaryColor, ), ); } Future _toggleScreenWake(bool enable) async { try { // 使用 io.Platform 检测平台 final String osName = io.Platform.operatingSystem; final String osVersion = io.Platform.operatingSystemVersion; print('Current platform: $osName, version: $osVersion'); if (enable) { await WakelockPlus.enable(); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('屏幕常亮已开启'))); } } else { await WakelockPlus.disable(); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('屏幕常亮已关闭'))); } } setState(() { _isScreenWakeEnabled = enable; }); } catch (e, stackTrace) { print('WakelockPlus error: $e'); print('Stack trace: $stackTrace'); if (mounted) { // 检查错误类型,判断是否是设备不支持 String errorMessage; if (e.toString().contains('not supported') || e.toString().contains('unsupported') || e.toString().contains('不支持')) { errorMessage = '该设备不支持屏幕常亮功能'; } else { errorMessage = '屏幕常亮功能异常: $e'; } showDialog( context: context, builder: (context) => AlertDialog( title: const Text('提示'), content: Text(errorMessage), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('确定'), ), ], ), ); } } } Widget _buildInteractionItem(String label, String value) { // === 互动统计项:显示标签和数值 === return Expanded( child: Column( children: [ Text( value, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: AppConstants.primaryColor, ), ), const SizedBox(height: 4), Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)), ], ), ); } void _showSnackBar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: AppConstants.primaryColor, duration: const Duration(seconds: 2), ), ); } // === 导航到历史记录页面 === void _navigateToHistoryPage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const HistoryPage()), ); } void _navigateToAppFunSettings() { Navigator.push( context, MaterialPageRoute(builder: (context) => const AppFunSettingsPage()), ); } void _navigateToUserPlanPage() { Navigator.push( context, MaterialPageRoute(builder: (_) => const UserPlanPage()), ); } void _navigateToOfflineDataPage() { Navigator.push( context, MaterialPageRoute(builder: (_) => const OfflineDataPage()), ); } void _navigateToPermissionPage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const PermissionPage()), ); } void _navigateToAppDataPage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const AppDataPage()), ); } void _navigateToPrivacyPage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const PrivacyPage()), ); } void _navigateToLearnUsPage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const LearnUsPage()), ); } void _navigateToAppInfoPage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const AppInfoPage()), ); } void _navigateToAppDiyPage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const AppDiyPage()), ); } void _navigateToVotePage() { Navigator.push( context, MaterialPageRoute(builder: (context) => const VotePage()), ); } void _showMoreOptions() { PopMenu.show( context, onRefresh: () => refreshData(), onEdit: () => _showSnackBar('编辑资料'), onScanQr: () => _showSnackBar('了解软件功能'), onDarkMode: () => _showSnackBar('夜间模式'), onSettings: () { Future.delayed(const Duration(milliseconds: 100), () { if (mounted && _pageController.hasClients) { _pageController.animateToPage( 1, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ); } }); }, ); } }