817 lines
24 KiB
Dart
817 lines
24 KiB
Dart
/// 时间: 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<HomePage> createState() => _HomePageState();
|
||
}
|
||
|
||
class _HomePageState extends State<HomePage>
|
||
with TickerProviderStateMixin, NetworkListenerMixin {
|
||
PoetryData? _poetryData;
|
||
List<String> _keywordList = [];
|
||
bool _loading = false;
|
||
bool _isLiked = false;
|
||
bool _isLoadingLike = false;
|
||
String _errorMessage = '';
|
||
String _starDisplay = '';
|
||
final String _historyKey = 'poetry_history';
|
||
List<Map<String, dynamic>> _historyList = [];
|
||
int _currentHistoryIndex = -1;
|
||
late AnimationController _fadeController;
|
||
late AnimationController _slideController;
|
||
late Animation<double> _fadeAnimation;
|
||
late Animation<Offset> _slideAnimation;
|
||
|
||
// 动态加载状态
|
||
bool _isLoadingNext = false;
|
||
bool _isLoadingPrevious = false;
|
||
Map<String, bool> _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<void> _initAudioManager() async {
|
||
await AudioManager().init();
|
||
}
|
||
|
||
Future<void> _initOfflineDataManager() async {
|
||
final offlineDataManager = OfflineDataManager();
|
||
await offlineDataManager.init();
|
||
}
|
||
|
||
Future<void> _initAutoRefresh() async {
|
||
final autoRefreshManager = AutoRefreshManager();
|
||
await autoRefreshManager.init();
|
||
autoRefreshManager.setOnRefresh(() {
|
||
if (mounted) {
|
||
_loadNextPoetry();
|
||
}
|
||
});
|
||
if (autoRefreshManager.isEnabled) {
|
||
autoRefreshManager.setEnabled(true);
|
||
}
|
||
}
|
||
|
||
Future<void> _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<void> _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<void> _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<void> _toggleLike() async {
|
||
if (_poetryData == null || _isLoadingLike) return;
|
||
|
||
// 播放点赞音效
|
||
await 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<void> _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) {
|
||
print('加载历史记录失败: $e');
|
||
}
|
||
}
|
||
|
||
Future<void> _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) {
|
||
print('保存历史记录失败: $e');
|
||
}
|
||
}
|
||
|
||
void _updateCurrentHistoryIndex() {
|
||
if (_poetryData?.id != null) {
|
||
final index = _historyList.indexWhere(
|
||
(item) => item['id'] == _poetryData!.id,
|
||
);
|
||
_currentHistoryIndex = index >= 0 ? index : -1;
|
||
}
|
||
}
|
||
|
||
Future<void> _checkIfLiked() async {
|
||
// 这里可以实现检查收藏状态的逻辑
|
||
// 暂时使用简单的模拟逻辑
|
||
}
|
||
|
||
void _loadNextPoetry() async {
|
||
if (_isLoadingNext) return;
|
||
|
||
// 播放下一条音效
|
||
await 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<void> _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<void> _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<bool>(
|
||
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<String>(
|
||
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,
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
}
|