/// 时间: 2025-03-22 /// 功能: 诗词页面通用组件和工具函数 /// 介绍: 从 home_page.dart 和 home_part.dart 中提取的公共组件,用于代码复用和简化 import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../constants/app_constants.dart'; import '../../utils/http/poetry_api.dart'; import 'home-load.dart'; /// 加载状态组件 class LoadingWidget extends StatelessWidget { const LoadingWidget({super.key}); @override Widget build(BuildContext context) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( AppConstants.primaryColor, ), ), SizedBox(height: 16), Text( '加载中...', style: TextStyle(fontSize: 16, color: const Color(0xFF757575)), ), ], ), ); } } /// 错误状态组件 class CustomErrorWidget extends StatelessWidget { final String errorMessage; final VoidCallback onRetry; const CustomErrorWidget({ super.key, required this.errorMessage, required this.onRetry, }); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 64, color: Colors.grey[400]), const SizedBox(height: 16), Text( errorMessage, style: const TextStyle(fontSize: 16, color: Colors.grey), textAlign: TextAlign.center, ), const SizedBox(height: 16), ElevatedButton( onPressed: onRetry, style: ElevatedButton.styleFrom( backgroundColor: AppConstants.primaryColor, ), child: const Text('重试'), ), ], ), ); } } /// 空状态组件 class EmptyWidget extends StatelessWidget { const EmptyWidget({super.key}); @override Widget build(BuildContext context) { return const Center( child: Text('暂无诗词内容', style: TextStyle(fontSize: 16, color: Colors.grey)), ); } } /// 诗词状态管理工具类 class PoetryStateManager { static void setLoadingState(VoidCallback setState, bool loading) { setState(); } static void setErrorState(VoidCallback setState, String error) { setState(); } static void setSuccessState(VoidCallback setState, PoetryData data) { setState(); } static String formatErrorMessage(String error) { return error.toString().contains('HttpException') ? error.toString().replaceAll('HttpException: ', '') : '获取诗词失败'; } static void showSnackBar( BuildContext context, String message, { Color? backgroundColor, Duration? duration, }) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: backgroundColor ?? AppConstants.successColor, duration: duration ?? const Duration(seconds: 2), ), ); } static void triggerHapticFeedback() { HapticFeedback.lightImpact(); } static void triggerHapticFeedbackMedium() { HapticFeedback.mediumImpact(); } } /// 诗词数据工具类 class PoetryDataUtils { static List extractKeywords(PoetryData? poetryData) { return poetryData?.keywordList ?? []; } static String getStarDisplay(PoetryData? poetryData) { return poetryData?.starDisplay ?? ''; } static String generateStars(int? starCount) { if (starCount == null) return ''; final count = starCount > 5 ? 5 : starCount; if (count == 5) { return ' 🌟$count'; } else { return ' ⭐$count'; } } static String generateLikeText(int? likeCount) { if (likeCount == null) return ''; return ' ❤️ $likeCount'; } static String generateViewText(int? viewCount) { if (viewCount == null) return ''; return ' 🔥 $viewCount'; } static bool isValidPoetryData(PoetryData? poetryData) { return poetryData != null && poetryData.name.isNotEmpty; } } /// 动画工具类 class AnimationUtils { static AnimationController createFadeController(TickerProvider vsync) { return AnimationController( duration: AppConstants.animationDurationMedium, vsync: vsync, ); } static AnimationController createSlideController(TickerProvider vsync) { return AnimationController( duration: AppConstants.animationDurationShort, vsync: vsync, ); } static Animation createFadeAnimation(AnimationController controller) { return CurvedAnimation(parent: controller, curve: Curves.easeIn); } static Animation createSlideAnimation( AnimationController controller, ) { return Tween( begin: const Offset(0.0, 0.3), end: Offset.zero, ).animate(CurvedAnimation(parent: controller, curve: Curves.easeOutCubic)); } } /// 复制工具类 class CopyUtils { static void copyToClipboard( BuildContext context, String content, String contentType, { VoidCallback? onSuccess, }) { try { Clipboard.setData(ClipboardData(text: content)); PoetryStateManager.showSnackBar(context, '已复制$contentType'); DebugInfoManager().showCopySuccess(); onSuccess?.call(); } catch (e) { PoetryStateManager.showSnackBar( context, '复制失败', backgroundColor: AppConstants.errorColor, ); DebugInfoManager().showCopyFailed(); } } static void showCopyDialog( BuildContext context, String content, String contentType, ) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('复制$contentType'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '受隐私权限管理,写入剪切板需告知用户', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), const SizedBox(height: 12), Text( '预览内容:', style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), const SizedBox(height: 4), Container( width: double.infinity, padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.grey[300]!), ), child: Text( content.length > 50 ? '${content.substring(0, 50)}...' : content, style: const TextStyle(fontSize: 12, color: Colors.black87), ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('返回'), ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); copyToClipboard(context, content, contentType); }, style: ElevatedButton.styleFrom( backgroundColor: AppConstants.primaryColor, foregroundColor: Colors.white, ), child: Text('复制$contentType'), ), ], ), ); } }