/// 时间: 2025-03-22 /// 功能: 诗词主页(参考微信小程序布局) /// 介绍: 展示诗词内容,支持点赞、收藏、分享等功能,参考wxpm小程序的index页面布局 /// 最新变化: 2025-03-22 重构代码结构,使用组件化架构简化代码 import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../constants/app_constants.dart'; import '../../../controllers/history_controller.dart'; import '../../../utils/http/poetry_api.dart'; import '../../../services/network_listener_service.dart'; import '../../../utils/audio_manager.dart'; import 'home_part.dart'; import 'home_components.dart'; import 'home-load.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State with TickerProviderStateMixin, NetworkListenerMixin { PoetryData? _poetryData; List _keywordList = []; bool _loading = false; bool _isLiked = false; bool _isLoadingLike = false; String _errorMessage = ''; String _starDisplay = ''; final String _historyKey = 'poetry_history'; List> _historyList = []; int _currentHistoryIndex = -1; late AnimationController _fadeController; late AnimationController _slideController; late Animation _fadeAnimation; late Animation _slideAnimation; // 动态加载状态 bool _isLoadingNext = false; bool _isLoadingPrevious = false; Map _sectionLoadingStates = { 'title': false, 'content': false, 'name': false, 'keywords': false, 'introduction': false, }; @override void initState() { super.initState(); _initAnimations(); _loadHistory(); _initAutoRefresh(); _initDebugInfo(); _initOfflineDataManager(); _initAudioManager(); // 延迟加载诗词,确保页面先显示 WidgetsBinding.instance.addPostFrameCallback((_) { _loadPoetry(); }); } Future _initAudioManager() async { await AudioManager().init(); } Future _initOfflineDataManager() async { final offlineDataManager = OfflineDataManager(); await offlineDataManager.init(); } Future _initAutoRefresh() async { final autoRefreshManager = AutoRefreshManager(); await autoRefreshManager.init(); autoRefreshManager.setOnRefresh(() { if (mounted) { _loadNextPoetry(); } }); if (autoRefreshManager.isEnabled) { autoRefreshManager.setEnabled(true); } } Future _initDebugInfo() async { final debugInfoManager = DebugInfoManager(); await debugInfoManager.init(); } void _initAnimations() { _fadeController = AnimationUtils.createFadeController(this); _slideController = AnimationUtils.createSlideController(this); _fadeAnimation = AnimationUtils.createFadeAnimation(_fadeController); _slideAnimation = AnimationUtils.createSlideAnimation(_slideController); } @override void dispose() { _fadeController.dispose(); _slideController.dispose(); // 只停止定时器,不释放单例资源 AutoRefreshManager().stopTimer(); // 不调用DebugInfoManager的dispose,因为它是单例,其他页面可能还在使用 super.dispose(); } Future _loadPoetry() async { if (_loading) return; setState(() => _loading = true); PoetryStateManager.triggerHapticFeedback(); try { final offlineDataManager = OfflineDataManager(); final isOnline = await offlineDataManager.isOnline(); final hasCachedData = await offlineDataManager.hasCachedData(); if (isOnline) { // 在线状态:从网络加载 final response = await PoetryApi.getRandomPoetry(); if (mounted && response.data != null) { setState(() { _poetryData = response.data; _keywordList = PoetryDataUtils.extractKeywords(response.data); _starDisplay = PoetryDataUtils.getStarDisplay(response.data); _isLiked = false; _loading = false; _errorMessage = ''; }); _fadeController.forward(); _slideController.forward(); _checkIfLiked(); await _saveToHistory(response.data!); DebugInfoManager().showRefreshSuccess(); } else { // 数据为空时,显示默认内容 if (mounted) { setState(() { _poetryData = _createDefaultPoetryData(); _keywordList = []; _starDisplay = '⭐⭐⭐⭐⭐'; _isLiked = false; _loading = false; _errorMessage = ''; }); _fadeController.forward(); _slideController.forward(); } DebugInfoManager().showRefreshFailed(); } } else { // 离线状态:从本地缓存加载 if (hasCachedData) { final poetryData = await offlineDataManager.getNextPoetry(); if (mounted && poetryData != null) { setState(() { _poetryData = poetryData; _keywordList = PoetryDataUtils.extractKeywords(poetryData); _starDisplay = PoetryDataUtils.getStarDisplay(poetryData); _isLiked = false; _loading = false; _errorMessage = ''; }); _fadeController.forward(); _slideController.forward(); _checkIfLiked(); DebugInfoManager().showRefreshSuccess(); } else { // 缓存为空时,显示错误信息 if (mounted) { setState(() { _loading = false; _errorMessage = '离线模式下无缓存数据,请先在线下载'; }); } DebugInfoManager().showRefreshFailed(); } } else { // 离线且无缓存时,显示错误信息 if (mounted) { setState(() { _loading = false; _errorMessage = '离线模式下无缓存数据,请先在线下载'; }); } DebugInfoManager().showRefreshFailed(); } } } catch (e) { if (mounted) { setState(() { _loading = false; _errorMessage = '加载失败,请检查网络连接'; }); } DebugInfoManager().showRefreshFailed(); } } // 创建默认诗词数据,确保页面始终有内容显示 PoetryData _createDefaultPoetryData() { final now = DateTime.now(); final dateStr = now.toString().substring(0, 10); final monthStr = dateStr.substring(0, 7); final timeStr = now.toString().substring(11, 19); return PoetryData( id: 1, name: '静夜思', alias: '李白', keywords: '思乡,月亮,静夜', introduce: '床前明月光,疑是地上霜。举头望明月,低头思故乡。', drtime: '唐·李白《静夜思》', like: 0, url: '李白-静夜思', tui: 1, star: 5, hitsTotal: 10000, hitsMonth: 1000, hitsDay: 100, date: dateStr, datem: monthStr, time: timeStr, createTime: timeStr, updateTime: timeStr, ); } Future _loadPoetryById(int poetryId) async { if (_loading) return; setState(() => _loading = true); try { final response = await PoetryApi.getPoetryById(poetryId); if (mounted && response.data != null) { setState(() { _poetryData = response.data; _keywordList = PoetryDataUtils.extractKeywords(response.data); _starDisplay = PoetryDataUtils.getStarDisplay(response.data); _loading = false; _errorMessage = ''; }); _fadeController.forward(); _slideController.forward(); _checkIfLiked(); _updateCurrentHistoryIndex(); } else { // 数据为空时,显示默认内容 if (mounted) { setState(() { _poetryData = _createDefaultPoetryData(); _keywordList = []; _starDisplay = '⭐⭐⭐⭐⭐'; _isLiked = false; _loading = false; _errorMessage = ''; }); _fadeController.forward(); _slideController.forward(); } } } catch (e) { if (mounted) { setState(() { _poetryData = _createDefaultPoetryData(); _keywordList = []; _starDisplay = '⭐⭐⭐⭐⭐'; _isLiked = false; _loading = false; _errorMessage = ''; }); _fadeController.forward(); _slideController.forward(); } } } Future _toggleLike() async { if (_poetryData == null || _isLoadingLike) return; // 播放点赞音效(不等待完成) AudioManager().playLikeSound(); // 立即切换按钮状态和显示加载 setState(() { _isLoadingLike = true; }); // 发送网络加载开始事件 startNetworkLoading('toggle_like'); try { final response = await PoetryApi.toggleLike(_poetryData!.id); if (mounted) { setState(() { // 根据API响应消息直接判断状态 _isLiked = response.message.contains('点赞成功'); // 更新诗词数据 if (response.data != null) { _poetryData = PoetryData( id: _poetryData!.id, name: _poetryData!.name, alias: _poetryData!.alias, keywords: _poetryData!.keywords, introduce: _poetryData!.introduce, drtime: _poetryData!.drtime, like: response.data!.like, url: _poetryData!.url, tui: _poetryData!.tui, star: _poetryData!.star, hitsTotal: _poetryData!.hitsTotal, hitsMonth: _poetryData!.hitsMonth, hitsDay: _poetryData!.hitsDay, date: _poetryData!.date, datem: _poetryData!.datem, time: _poetryData!.time, createTime: _poetryData!.createTime, updateTime: _poetryData!.updateTime, ); } }); // 管理点赞存储 if (_isLiked) { // 添加到点赞列表 await HistoryController.addToLiked(_poetryData!.toJson()); } else { // 从点赞列表移除 await HistoryController.removeLikedPoetry(_poetryData!.id.toString()); } // 发送点赞事件通知其他页面 sendLikeEvent(_poetryData!.id.toString(), _isLiked); if (_isLiked) { DebugInfoManager().showLiked(); } else { DebugInfoManager().showUnliked(); } PoetryStateManager.showSnackBar( context, response.message, backgroundColor: AppConstants.successColor, duration: const Duration(milliseconds: 200), ); } } catch (e) { if (mounted) { PoetryStateManager.showSnackBar( context, '操作失败,请重试', backgroundColor: AppConstants.errorColor, duration: const Duration(milliseconds: 200), ); } } finally { if (mounted) { setState(() => _isLoadingLike = false); // 发送网络加载结束事件 endNetworkLoading('toggle_like'); } } } Future _loadHistory() async { try { final historyJson = await HistoryController.getHistory(); if (mounted) { setState(() { _historyList = historyJson; // 重新计算当前诗词在历史记录中的索引 if (_poetryData != null && _historyList.isNotEmpty) { _currentHistoryIndex = _historyList.indexWhere( (item) => item['id'] == _poetryData!.id, ); } else { _currentHistoryIndex = -1; } }); } } catch (e) { // 加载失败 } } Future _saveToHistory(PoetryData poetryData) async { try { final poetryMap = { 'id': poetryData.id, 'name': poetryData.name, // 诗句名称/标题 'alias': poetryData.alias, // 诗句朝代/作者 'introduce': poetryData.introduce, // 诗句译文/解释 'drtime': poetryData.drtime, // 诗句原文/内容 }; await HistoryController.addToHistory(poetryMap); } catch (e) { // 保存失败 } } void _updateCurrentHistoryIndex() { if (_poetryData?.id != null) { final index = _historyList.indexWhere( (item) => item['id'] == _poetryData!.id, ); _currentHistoryIndex = index >= 0 ? index : -1; } } Future _checkIfLiked() async { // 这里可以实现检查收藏状态的逻辑 // 暂时使用简单的模拟逻辑 } void _loadNextPoetry() async { if (_isLoadingNext) return; // 播放下一条音效(不等待完成) AudioManager().playNextSound(); setState(() { _isLoadingNext = true; // 设置所有区域为加载状态 _sectionLoadingStates = { 'title': true, 'content': true, 'name': true, 'keywords': true, 'introduction': true, }; }); try { final offlineDataManager = OfflineDataManager(); final isOnline = await offlineDataManager.isOnline(); final hasCachedData = await offlineDataManager.hasCachedData(); PoetryData? newPoetryData; if (isOnline) { // 在线状态:从网络加载 // 确保历史记录已加载 if (_historyList.isEmpty) { await _loadHistory(); } if (_currentHistoryIndex < 0 || _currentHistoryIndex >= _historyList.length - 1) { // 如果没有下一条了,加载新的诗词 final response = await PoetryApi.getRandomPoetry(); newPoetryData = response.data; if (mounted && newPoetryData != null) { await _saveToHistory(newPoetryData); } } else { // 如果有下一条,加载下一条 final nextPoetry = _historyList[_currentHistoryIndex + 1]; final response = await PoetryApi.getPoetryById(nextPoetry['id']); newPoetryData = response.data; } } else { // 离线状态:从本地缓存加载 if (hasCachedData) { newPoetryData = await offlineDataManager.getNextPoetry(); } else { // 离线且无缓存时,显示错误信息 if (mounted) { PoetryStateManager.showSnackBar( context, '离线模式下无缓存数据,请先在线下载', backgroundColor: AppConstants.errorColor, duration: const Duration(milliseconds: 200), ); // 重置所有加载状态 setState(() { _sectionLoadingStates.updateAll((key, value) => false); }); } DebugInfoManager().showNextFailed(); return; } } if (mounted && newPoetryData != null) { // 模拟分步加载 await _simulateSectionLoading(newPoetryData); DebugInfoManager().showNextSuccess(); } } catch (e) { if (mounted) { PoetryStateManager.showSnackBar( context, '加载下一条失败,建议开启离线模式', backgroundColor: AppConstants.errorColor, duration: const Duration(milliseconds: 200), ); // 重置所有加载状态 setState(() { _sectionLoadingStates.updateAll((key, value) => false); }); } DebugInfoManager().showNextFailed(); } finally { if (mounted) { setState(() { _isLoadingNext = false; }); } } } // 模拟分步加载过程 Future _simulateSectionLoading(PoetryData newPoetryData) async { // 1. 加载标题区域 setState(() { _sectionLoadingStates['title'] = false; _poetryData = newPoetryData; }); await Future.delayed(const Duration(milliseconds: 200)); // 2. 加载诗句区域 setState(() { _sectionLoadingStates['name'] = false; }); await Future.delayed(const Duration(milliseconds: 200)); // 3. 加载原文区域 setState(() { _sectionLoadingStates['content'] = false; }); await Future.delayed(const Duration(milliseconds: 200)); // 4. 加载关键词区域 setState(() { _sectionLoadingStates['keywords'] = false; _keywordList = PoetryDataUtils.extractKeywords(newPoetryData); }); await Future.delayed(const Duration(milliseconds: 200)); // 5. 加载译文区域 setState(() { _sectionLoadingStates['introduction'] = false; _starDisplay = PoetryDataUtils.getStarDisplay(newPoetryData); _isLiked = false; _errorMessage = ''; }); _checkIfLiked(); _updateCurrentHistoryIndex(); } void _loadPreviousPoetry() async { try { final offlineDataManager = OfflineDataManager(); final isOnline = await offlineDataManager.isOnline(); final hasCachedData = await offlineDataManager.hasCachedData(); if (isOnline) { // 在线状态:从历史记录加载 // 确保历史记录已加载 if (_historyList.isEmpty) { await _loadHistory(); } if (_currentHistoryIndex <= 0) { // 如果当前索引无效或已经是第一条,则重新加载历史记录 await _loadHistory(); // 如果历史记录为空,显示提示 if (_historyList.isEmpty) { if (mounted) { PoetryStateManager.showSnackBar( context, '暂无历史记录', backgroundColor: AppConstants.errorColor, duration: const Duration(milliseconds: 200), ); } return; } // 如果当前索引无效,设置为最新的一条 if (_currentHistoryIndex < 0) { _currentHistoryIndex = 0; } } // 检查是否有上一条 if (_currentHistoryIndex > 0) { final previousPoetry = _historyList[_currentHistoryIndex - 1]; await _loadPoetryById(previousPoetry['id']); } else { // 如果没有上一条了,显示提示 if (mounted) { PoetryStateManager.showSnackBar( context, '已经是第一条了', backgroundColor: AppConstants.infoColor, duration: const Duration(milliseconds: 200), ); } } } else { // 离线状态:从本地缓存加载 if (hasCachedData) { final poetryData = await offlineDataManager.getPreviousPoetry(); if (mounted && poetryData != null) { setState(() { _poetryData = poetryData; _keywordList = PoetryDataUtils.extractKeywords(poetryData); _starDisplay = PoetryDataUtils.getStarDisplay(poetryData); _isLiked = false; _errorMessage = ''; }); _fadeController.forward(); _slideController.forward(); _checkIfLiked(); DebugInfoManager().showPreviousSuccess(); } else { // 缓存为空时,显示错误信息 if (mounted) { PoetryStateManager.showSnackBar( context, '离线模式下无缓存数据,请先在线下载', backgroundColor: AppConstants.errorColor, duration: const Duration(milliseconds: 200), ); } DebugInfoManager().showPreviousFailed(); } } else { // 离线且无缓存时,显示错误信息 if (mounted) { PoetryStateManager.showSnackBar( context, '离线模式下无缓存数据,请先在线下载', backgroundColor: AppConstants.errorColor, duration: const Duration(milliseconds: 200), ); } DebugInfoManager().showPreviousFailed(); } } } catch (e) { if (mounted) { PoetryStateManager.showSnackBar( context, '加载上一条失败', backgroundColor: AppConstants.errorColor, duration: const Duration(milliseconds: 200), ); } } } Future _refreshPoetry() async { if (_poetryData?.id != null) { // 如果有当前诗词ID,则重新加载当前诗词 await _loadPoetryById(_poetryData!.id); } else { // 如果没有当前诗词ID,则加载新的诗词 await _loadPoetry(); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey[50], body: SafeArea(child: _buildBody()), ); } Widget _buildBody() { if (_loading) { return const LoadingWidget(); } if (_errorMessage.isNotEmpty) { return CustomErrorWidget( errorMessage: _errorMessage, onRetry: _loadPoetry, ); } if (!PoetryDataUtils.isValidPoetryData(_poetryData)) { return const EmptyWidget(); } return _buildContent(); } Widget _buildContent() { return FutureBuilder( future: OfflineDataManager().isOnline(), builder: (context, snapshot) { final isOnline = snapshot.data ?? true; return RefreshIndicator( onRefresh: _refreshPoetry, child: FadeTransition( opacity: _fadeAnimation, child: Stack( children: [ SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), // 确保可以下拉刷新 padding: const EdgeInsets.symmetric(vertical: 16), child: Column( children: [ PoetryCard( poetryData: _poetryData!, keywordList: _keywordList, onTap: _loadNextPoetry, sectionLoadingStates: _sectionLoadingStates, ), const SizedBox(height: 160), // 为悬浮按钮留出更多空间 ], ), ), // 调试信息气泡 Positioned( bottom: 66, left: 0, right: 0, child: Center(child: _buildDebugInfoBubble()), ), // 悬浮上一条按钮 - 左边上方 Positioned( left: 16, bottom: 100, child: FloatingPreviousButton( onPrevious: _loadPreviousPoetry, ), ), // 悬浮下一条按钮 - 左边下方 Positioned( left: 16, bottom: 32, child: FloatingNextButton(onNext: _loadNextPoetry), ), // 悬浮点赞按钮 - 右边(仅在线状态显示) if (isOnline) Positioned( right: 16, bottom: 32, child: FloatingLikeButton( isLiked: _isLiked, isLoadingLike: _isLoadingLike, onToggleLike: _toggleLike, ), ), ], ), ), ); }, ); } Widget _buildDebugInfoBubble() { return ValueListenableBuilder( valueListenable: DebugInfoManager().messageNotifier, builder: (context, message, child) { if (message.isEmpty) { return const SizedBox.shrink(); } return AnimatedContainer( duration: const Duration(milliseconds: 300), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: AppConstants.primaryColor.withValues(alpha: 0.9), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Text( message, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500, ), ), ); }, ); } }