import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/utils/responsive_utils.dart'; import '../../../shared/widgets/custom_app_bar.dart'; import '../../../shared/widgets/loading_widget.dart'; class StudyPlanScreen extends ConsumerStatefulWidget { const StudyPlanScreen({super.key}); @override ConsumerState createState() => _StudyPlanScreenState(); } class _StudyPlanScreenState extends ConsumerState with TickerProviderStateMixin { late TabController _tabController; bool _isLoading = false; List _studyPlans = []; StudyPlan? _currentPlan; @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); _loadStudyPlans(); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final isMobile = ResponsiveUtils.isMobile(context); return Scaffold( appBar: CustomAppBar( title: '学习计划', actions: [ IconButton( icon: const Icon(Icons.add), onPressed: _showCreatePlanDialog, ), PopupMenuButton( icon: const Icon(Icons.more_vert), onSelected: _handleMenuAction, itemBuilder: (context) => [ const PopupMenuItem( value: 'templates', child: Row( children: [ Icon(Icons.library_books), SizedBox(width: 8), Text('计划模板'), ], ), ), const PopupMenuItem( value: 'statistics', child: Row( children: [ Icon(Icons.analytics), SizedBox(width: 8), Text('学习统计'), ], ), ), const PopupMenuItem( value: 'settings', child: Row( children: [ Icon(Icons.settings), SizedBox(width: 8), Text('设置'), ], ), ), ], ), ], ), body: Column( children: [ _buildTabBar(context, isMobile), Expanded( child: TabBarView( controller: _tabController, children: [ _buildCurrentPlanTab(context, isMobile), _buildAllPlansTab(context, isMobile), _buildProgressTab(context, isMobile), ], ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: _showCreatePlanDialog, backgroundColor: Colors.blue, child: const Icon(Icons.add, color: Colors.white), ), ); } Widget _buildTabBar(BuildContext context, bool isMobile) { return Container( decoration: BoxDecoration( color: Colors.white, border: Border( bottom: BorderSide(color: Colors.grey.shade200), ), ), child: TabBar( controller: _tabController, labelColor: Colors.blue, unselectedLabelColor: Colors.grey, indicatorColor: Colors.blue, tabs: const [ Tab( icon: Icon(Icons.today), text: '当前计划', ), Tab( icon: Icon(Icons.list), text: '所有计划', ), Tab( icon: Icon(Icons.trending_up), text: '学习进度', ), ], ), ); } Widget _buildCurrentPlanTab(BuildContext context, bool isMobile) { if (_isLoading) { return const LoadingWidget(); } if (_currentPlan == null) { return _buildNoPlanState(context, isMobile); } return SingleChildScrollView( padding: EdgeInsets.all(isMobile ? 16.0 : 24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildCurrentPlanHeader(context, _currentPlan!, isMobile), const SizedBox(height: 24), _buildTodayTasks(context, _currentPlan!, isMobile), const SizedBox(height: 24), _buildWeeklyProgress(context, _currentPlan!, isMobile), const SizedBox(height: 24), _buildQuickActions(context, isMobile), ], ), ); } Widget _buildAllPlansTab(BuildContext context, bool isMobile) { if (_isLoading) { return const LoadingWidget(); } if (_studyPlans.isEmpty) { return _buildEmptyPlansState(context, isMobile); } return ListView.builder( padding: EdgeInsets.all(isMobile ? 16.0 : 24.0), itemCount: _studyPlans.length, itemBuilder: (context, index) { final plan = _studyPlans[index]; return _buildPlanCard(context, plan, isMobile); }, ); } Widget _buildProgressTab(BuildContext context, bool isMobile) { if (_isLoading) { return const LoadingWidget(); } return SingleChildScrollView( padding: EdgeInsets.all(isMobile ? 16.0 : 24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildOverallProgress(context, isMobile), const SizedBox(height: 24), _buildWeeklyChart(context, isMobile), const SizedBox(height: 24), _buildMonthlyStats(context, isMobile), const SizedBox(height: 24), _buildAchievements(context, isMobile), ], ), ); } Widget _buildCurrentPlanHeader(BuildContext context, StudyPlan plan, bool isMobile) { final progress = plan.completedTasks / plan.totalTasks; return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Container( padding: EdgeInsets.all(isMobile ? 20.0 : 24.0), decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.blue.shade400, Colors.blue.shade600], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( plan.title, style: TextStyle( color: Colors.white, fontSize: isMobile ? 20 : 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( plan.description, style: TextStyle( color: Colors.white.withOpacity(0.9), fontSize: isMobile ? 14 : 16, ), ), ], ), ), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Icon( _getPlanTypeIcon(plan.type), color: Colors.white, size: isMobile ? 28 : 32, ), ), ], ), const SizedBox(height: 20), Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '总体进度', style: TextStyle( color: Colors.white.withOpacity(0.9), fontSize: isMobile ? 14 : 16, ), ), const SizedBox(height: 8), LinearProgressIndicator( value: progress, backgroundColor: Colors.white.withOpacity(0.3), valueColor: const AlwaysStoppedAnimation(Colors.white), minHeight: 6, ), const SizedBox(height: 4), Text( '${(progress * 100).toInt()}% (${plan.completedTasks}/${plan.totalTasks})', style: TextStyle( color: Colors.white, fontSize: isMobile ? 12 : 14, fontWeight: FontWeight.w500, ), ), ], ), ), const SizedBox(width: 20), Column( children: [ Text( '剩余天数', style: TextStyle( color: Colors.white.withOpacity(0.9), fontSize: isMobile ? 12 : 14, ), ), Text( '${plan.endDate.difference(DateTime.now()).inDays}', style: TextStyle( color: Colors.white, fontSize: isMobile ? 24 : 28, fontWeight: FontWeight.bold, ), ), ], ), ], ), ], ), ), ); } Widget _buildTodayTasks(BuildContext context, StudyPlan plan, bool isMobile) { final todayTasks = plan.tasks.where((task) { final today = DateTime.now(); return task.dueDate.year == today.year && task.dueDate.month == today.month && task.dueDate.day == today.day; }).toList(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.today, color: Colors.blue[700], size: isMobile ? 24 : 28, ), const SizedBox(width: 8), Text( '今日任务', style: TextStyle( fontSize: isMobile ? 18 : 20, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), const Spacer(), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( '${todayTasks.where((t) => t.isCompleted).length}/${todayTasks.length}', style: TextStyle( fontSize: 12, color: Colors.blue[700], fontWeight: FontWeight.w500, ), ), ), ], ), const SizedBox(height: 16), if (todayTasks.isEmpty) Container( padding: EdgeInsets.all(isMobile ? 20.0 : 24.0), decoration: BoxDecoration( color: Colors.green.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.green.withOpacity(0.3)), ), child: Row( children: [ Icon( Icons.check_circle, color: Colors.green[600], size: isMobile ? 24 : 28, ), const SizedBox(width: 12), Expanded( child: Text( '今日无任务,您可以休息或提前学习明天的内容', style: TextStyle( fontSize: isMobile ? 14 : 16, color: Colors.green[700], ), ), ), ], ), ) else ...todayTasks.map((task) => _buildTaskCard(context, task, isMobile)), ], ); } Widget _buildTaskCard(BuildContext context, StudyTask task, bool isMobile) { return Container( margin: const EdgeInsets.only(bottom: 12), child: Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: InkWell( onTap: () => _toggleTaskCompletion(task), borderRadius: BorderRadius.circular(12), child: Padding( padding: EdgeInsets.all(isMobile ? 16.0 : 20.0), child: Row( children: [ Container( width: 24, height: 24, decoration: BoxDecoration( shape: BoxShape.circle, color: task.isCompleted ? Colors.green : Colors.transparent, border: Border.all( color: task.isCompleted ? Colors.green : Colors.grey[400]!, width: 2, ), ), child: task.isCompleted ? const Icon( Icons.check, color: Colors.white, size: 16, ) : null, ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( task.title, style: TextStyle( fontSize: isMobile ? 16 : 18, fontWeight: FontWeight.w600, color: task.isCompleted ? Colors.grey[500] : Colors.grey[800], decoration: task.isCompleted ? TextDecoration.lineThrough : null, ), ), if (task.description.isNotEmpty) ...[ const SizedBox(height: 4), Text( task.description, style: TextStyle( fontSize: isMobile ? 14 : 16, color: task.isCompleted ? Colors.grey[400] : Colors.grey[600], ), ), ], const SizedBox(height: 8), Row( children: [ _buildTaskTypeChip(task.type, isMobile), const SizedBox(width: 8), if (task.estimatedMinutes > 0) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.access_time, size: 12, color: Colors.orange[700], ), const SizedBox(width: 4), Text( '${task.estimatedMinutes}分钟', style: TextStyle( fontSize: 12, color: Colors.orange[700], ), ), ], ), ), ], ), ], ), ), if (!task.isCompleted) IconButton( icon: const Icon(Icons.play_arrow), onPressed: () => _startTask(task), color: Colors.blue, ), ], ), ), ), ), ); } Widget _buildTaskTypeChip(TaskType type, bool isMobile) { Color color; String text; IconData icon; switch (type) { case TaskType.vocabulary: color = Colors.blue; text = '词汇'; icon = Icons.book; break; case TaskType.reading: color = Colors.green; text = '阅读'; icon = Icons.article; break; case TaskType.listening: color = Colors.purple; text = '听力'; icon = Icons.headphones; break; case TaskType.speaking: color = Colors.orange; text = '口语'; icon = Icons.mic; break; case TaskType.writing: color = Colors.red; text = '写作'; icon = Icons.edit; break; case TaskType.review: color = Colors.teal; text = '复习'; icon = Icons.refresh; break; } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 12, color: color), const SizedBox(width: 4), Text( text, style: TextStyle( fontSize: 12, color: color, fontWeight: FontWeight.w500, ), ), ], ), ); } Widget _buildWeeklyProgress(BuildContext context, StudyPlan plan, bool isMobile) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.calendar_view_week, color: Colors.blue[700], size: isMobile ? 24 : 28, ), const SizedBox(width: 8), Text( '本周进度', style: TextStyle( fontSize: isMobile ? 18 : 20, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), ], ), const SizedBox(height: 16), Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(isMobile ? 16.0 : 20.0), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: List.generate(7, (index) { final date = DateTime.now().subtract(Duration(days: 6 - index)); final dayName = ['一', '二', '三', '四', '五', '六', '日'][date.weekday - 1]; final isToday = date.day == DateTime.now().day; final hasTask = plan.tasks.any((task) => task.dueDate.year == date.year && task.dueDate.month == date.month && task.dueDate.day == date.day); final isCompleted = hasTask && plan.tasks .where((task) => task.dueDate.year == date.year && task.dueDate.month == date.month && task.dueDate.day == date.day) .every((task) => task.isCompleted); return Column( children: [ Text( dayName, style: TextStyle( fontSize: 12, color: isToday ? Colors.blue : Colors.grey[600], fontWeight: isToday ? FontWeight.bold : FontWeight.normal, ), ), const SizedBox(height: 8), Container( width: 32, height: 32, decoration: BoxDecoration( shape: BoxShape.circle, color: isCompleted ? Colors.green : hasTask ? Colors.orange.withOpacity(0.3) : Colors.grey.withOpacity(0.2), border: isToday ? Border.all(color: Colors.blue, width: 2) : null, ), child: Center( child: isCompleted ? const Icon( Icons.check, color: Colors.white, size: 16, ) : hasTask ? Icon( Icons.circle, color: Colors.orange, size: 8, ) : null, ), ), const SizedBox(height: 4), Text( '${date.day}', style: TextStyle( fontSize: 10, color: Colors.grey[500], ), ), ], ); }), ), ], ), ), ), ], ); } Widget _buildQuickActions(BuildContext context, bool isMobile) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '快速操作', style: TextStyle( fontSize: isMobile ? 18 : 20, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), const SizedBox(height: 16), GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: isMobile ? 2 : 4, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: isMobile ? 1.2 : 1.0, children: [ _buildQuickActionCard( context, '开始学习', Icons.play_arrow, Colors.green, () => _startStudySession(), isMobile, ), _buildQuickActionCard( context, '复习单词', Icons.refresh, Colors.blue, () => _startReview(), isMobile, ), _buildQuickActionCard( context, '查看统计', Icons.analytics, Colors.purple, () => _showStatistics(), isMobile, ), _buildQuickActionCard( context, '调整计划', Icons.edit, Colors.orange, () => _editCurrentPlan(), isMobile, ), ], ), ], ); } Widget _buildQuickActionCard( BuildContext context, String title, IconData icon, Color color, VoidCallback onTap, bool isMobile, ) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Padding( padding: EdgeInsets.all(isMobile ? 12.0 : 16.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( icon, color: color, size: isMobile ? 24 : 28, ), ), const SizedBox(height: 8), Text( title, style: TextStyle( fontSize: isMobile ? 12 : 14, fontWeight: FontWeight.w600, color: Colors.grey[800], ), textAlign: TextAlign.center, ), ], ), ), ), ); } Widget _buildPlanCard(BuildContext context, StudyPlan plan, bool isMobile) { final progress = plan.completedTasks / plan.totalTasks; final isActive = plan == _currentPlan; return Container( margin: const EdgeInsets.only(bottom: 16), child: Card( elevation: isActive ? 4 : 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: isActive ? BorderSide(color: Colors.blue, width: 2) : BorderSide.none, ), child: InkWell( onTap: () => _selectPlan(plan), borderRadius: BorderRadius.circular(12), child: Padding( padding: EdgeInsets.all(isMobile ? 16.0 : 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: _getPlanTypeColor(plan.type).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( _getPlanTypeIcon(plan.type), color: _getPlanTypeColor(plan.type), size: isMobile ? 20 : 24, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( plan.title, style: TextStyle( fontSize: isMobile ? 16 : 18, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), ), if (isActive) Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(12), ), child: const Text( '当前', style: TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.w500, ), ), ), ], ), const SizedBox(height: 4), Text( plan.description, style: TextStyle( fontSize: isMobile ? 14 : 16, color: Colors.grey[600], ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), PopupMenuButton( icon: Icon( Icons.more_vert, color: Colors.grey[600], ), onSelected: (value) => _handlePlanAction(value, plan), itemBuilder: (context) => [ if (!isActive) const PopupMenuItem( value: 'activate', child: Row( children: [ Icon(Icons.play_arrow, size: 18), SizedBox(width: 8), Text('设为当前'), ], ), ), const PopupMenuItem( value: 'edit', child: Row( children: [ Icon(Icons.edit, size: 18), SizedBox(width: 8), Text('编辑'), ], ), ), const PopupMenuItem( value: 'duplicate', child: Row( children: [ Icon(Icons.copy, size: 18), SizedBox(width: 8), Text('复制'), ], ), ), const PopupMenuItem( value: 'delete', child: Row( children: [ Icon(Icons.delete, size: 18, color: Colors.red), SizedBox(width: 8), Text('删除', style: TextStyle(color: Colors.red)), ], ), ), ], ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '进度', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), const SizedBox(height: 4), LinearProgressIndicator( value: progress, backgroundColor: Colors.grey[300], valueColor: AlwaysStoppedAnimation( _getPlanTypeColor(plan.type), ), minHeight: 4, ), const SizedBox(height: 4), Text( '${(progress * 100).toInt()}% (${plan.completedTasks}/${plan.totalTasks})', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ), ), const SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '剩余', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), Text( '${plan.endDate.difference(DateTime.now()).inDays}天', style: TextStyle( fontSize: isMobile ? 16 : 18, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), ], ), ], ), ], ), ), ), ), ); } Widget _buildOverallProgress(BuildContext context, bool isMobile) { return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Container( padding: EdgeInsets.all(isMobile ? 20.0 : 24.0), decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.purple.shade400, Colors.purple.shade600], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.trending_up, color: Colors.white, size: isMobile ? 28 : 32, ), const SizedBox(width: 12), Expanded( child: Text( '学习统计', style: TextStyle( color: Colors.white, fontSize: isMobile ? 20 : 24, fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 20), Row( children: [ Expanded( child: _buildStatItem('连续学习', '7天', Icons.local_fire_department, isMobile), ), Expanded( child: _buildStatItem('总学习时长', '45小时', Icons.access_time, isMobile), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: _buildStatItem('掌握单词', '328个', Icons.book, isMobile), ), Expanded( child: _buildStatItem('完成任务', '156个', Icons.check_circle, isMobile), ), ], ), ], ), ), ); } Widget _buildStatItem(String label, String value, IconData icon, bool isMobile) { return Column( children: [ Icon( icon, color: Colors.white.withOpacity(0.8), size: isMobile ? 24 : 28, ), const SizedBox(height: 8), Text( value, style: TextStyle( color: Colors.white, fontSize: isMobile ? 18 : 20, fontWeight: FontWeight.bold, ), ), Text( label, style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: isMobile ? 12 : 14, ), ), ], ); } Widget _buildWeeklyChart(BuildContext context, bool isMobile) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(isMobile ? 16.0 : 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '本周学习时长', style: TextStyle( fontSize: isMobile ? 18 : 20, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), const SizedBox(height: 20), SizedBox( height: 200, child: Center( child: Text( '图表组件开发中...', style: TextStyle( fontSize: isMobile ? 16 : 18, color: Colors.grey[500], ), ), ), ), ], ), ), ); } Widget _buildMonthlyStats(BuildContext context, bool isMobile) { return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Padding( padding: EdgeInsets.all(isMobile ? 16.0 : 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '月度统计', style: TextStyle( fontSize: isMobile ? 18 : 20, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: _buildMonthlyStatCard( '学习天数', '23/30', Icons.calendar_today, Colors.blue, isMobile, ), ), const SizedBox(width: 12), Expanded( child: _buildMonthlyStatCard( '平均时长', '1.8小时', Icons.access_time, Colors.green, isMobile, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildMonthlyStatCard( '新学单词', '156个', Icons.add_circle, Colors.orange, isMobile, ), ), const SizedBox(width: 12), Expanded( child: _buildMonthlyStatCard( '复习次数', '89次', Icons.refresh, Colors.purple, isMobile, ), ), ], ), ], ), ), ); } Widget _buildMonthlyStatCard( String label, String value, IconData icon, Color color, bool isMobile, ) { return Container( padding: EdgeInsets.all(isMobile ? 12.0 : 16.0), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Icon( icon, color: color, size: isMobile ? 24 : 28, ), const SizedBox(height: 8), Text( value, style: TextStyle( fontSize: isMobile ? 16 : 18, fontWeight: FontWeight.bold, color: color, ), ), Text( label, style: TextStyle( fontSize: isMobile ? 12 : 14, color: Colors.grey[600], ), ), ], ), ); } Widget _buildAchievements(BuildContext context, bool isMobile) { final achievements = [ Achievement( title: '连续学习7天', description: '坚持每天学习,养成良好习惯', icon: Icons.local_fire_department, color: Colors.red, isUnlocked: true, ), Achievement( title: '掌握100个单词', description: '词汇量达到新的里程碑', icon: Icons.book, color: Colors.blue, isUnlocked: true, ), Achievement( title: '学习时长达到50小时', description: '累计学习时间的重要节点', icon: Icons.access_time, color: Colors.green, isUnlocked: false, ), Achievement( title: '完成第一个学习计划', description: '成功完成制定的学习目标', icon: Icons.emoji_events, color: Colors.orange, isUnlocked: false, ), ]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '成就徽章', style: TextStyle( fontSize: isMobile ? 18 : 20, fontWeight: FontWeight.bold, color: Colors.grey[800], ), ), const SizedBox(height: 16), GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: isMobile ? 2 : 4, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1.0, ), itemCount: achievements.length, itemBuilder: (context, index) { final achievement = achievements[index]; return _buildAchievementCard(context, achievement, isMobile); }, ), ], ); } Widget _buildAchievementCard(BuildContext context, Achievement achievement, bool isMobile) { return Card( elevation: achievement.isUnlocked ? 4 : 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), child: Container( padding: EdgeInsets.all(isMobile ? 12.0 : 16.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: achievement.isUnlocked ? achievement.color.withOpacity(0.1) : Colors.grey.withOpacity(0.05), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( shape: BoxShape.circle, color: achievement.isUnlocked ? achievement.color : Colors.grey.withOpacity(0.3), ), child: Icon( achievement.icon, color: achievement.isUnlocked ? Colors.white : Colors.grey, size: isMobile ? 24 : 28, ), ), const SizedBox(height: 8), Text( achievement.title, style: TextStyle( fontSize: isMobile ? 12 : 14, fontWeight: FontWeight.bold, color: achievement.isUnlocked ? Colors.grey[800] : Colors.grey[500], ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Text( achievement.description, style: TextStyle( fontSize: 10, color: achievement.isUnlocked ? Colors.grey[600] : Colors.grey[400], ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ); } Widget _buildNoPlanState(BuildContext context, bool isMobile) { return Center( child: Padding( padding: EdgeInsets.all(isMobile ? 32.0 : 48.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.assignment, size: isMobile ? 80 : 100, color: Colors.grey[400], ), const SizedBox(height: 24), Text( '暂无学习计划', style: TextStyle( fontSize: isMobile ? 20 : 24, fontWeight: FontWeight.bold, color: Colors.grey[600], ), ), const SizedBox(height: 12), Text( '创建您的第一个学习计划,开始高效学习之旅', style: TextStyle( fontSize: isMobile ? 16 : 18, color: Colors.grey[500], ), textAlign: TextAlign.center, ), const SizedBox(height: 32), ElevatedButton.icon( onPressed: _showCreatePlanDialog, icon: const Icon(Icons.add), label: const Text('创建计划'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, padding: EdgeInsets.symmetric( horizontal: isMobile ? 24 : 32, vertical: isMobile ? 12 : 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(25), ), ), ), ], ), ), ); } Widget _buildEmptyPlansState(BuildContext context, bool isMobile) { return Center( child: Padding( padding: EdgeInsets.all(isMobile ? 32.0 : 48.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.list_alt, size: isMobile ? 80 : 100, color: Colors.grey[400], ), const SizedBox(height: 24), Text( '暂无学习计划', style: TextStyle( fontSize: isMobile ? 20 : 24, fontWeight: FontWeight.bold, color: Colors.grey[600], ), ), const SizedBox(height: 12), Text( '创建学习计划,制定个性化的学习目标', style: TextStyle( fontSize: isMobile ? 16 : 18, color: Colors.grey[500], ), textAlign: TextAlign.center, ), const SizedBox(height: 32), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton.icon( onPressed: _showCreatePlanDialog, icon: const Icon(Icons.add), label: const Text('创建计划'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, padding: EdgeInsets.symmetric( horizontal: isMobile ? 20 : 24, vertical: isMobile ? 12 : 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(25), ), ), ), const SizedBox(width: 16), OutlinedButton.icon( onPressed: _showPlanTemplates, icon: const Icon(Icons.library_books), label: const Text('使用模板'), style: OutlinedButton.styleFrom( padding: EdgeInsets.symmetric( horizontal: isMobile ? 20 : 24, vertical: isMobile ? 12 : 16, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(25), ), ), ), ], ), ], ), ), ); } Future _loadStudyPlans() async { setState(() { _isLoading = true; }); // 模拟加载数据 await Future.delayed(const Duration(milliseconds: 800)); setState(() { _studyPlans = _generateMockPlans(); _currentPlan = _studyPlans.isNotEmpty ? _studyPlans.first : null; _isLoading = false; }); } List _generateMockPlans() { final now = DateTime.now(); return [ StudyPlan( id: '1', title: '30天词汇突破计划', description: '每天学习20个新单词,复习已学单词', type: PlanType.vocabulary, startDate: now.subtract(const Duration(days: 7)), endDate: now.add(const Duration(days: 23)), tasks: _generateMockTasks(), totalTasks: 30, completedTasks: 7, isActive: true, ), StudyPlan( id: '2', title: '雅思备考计划', description: '全面提升听说读写能力,目标7分', type: PlanType.exam, startDate: now.add(const Duration(days: 1)), endDate: now.add(const Duration(days: 90)), tasks: [], totalTasks: 90, completedTasks: 0, isActive: false, ), ]; } List _generateMockTasks() { final now = DateTime.now(); return [ StudyTask( id: '1', title: '学习商务词汇', description: '学习20个商务相关单词', type: TaskType.vocabulary, dueDate: now, estimatedMinutes: 30, isCompleted: false, ), StudyTask( id: '2', title: '复习昨日单词', description: '复习昨天学习的单词', type: TaskType.review, dueDate: now, estimatedMinutes: 15, isCompleted: true, ), ]; } void _showCreatePlanDialog() { showDialog( context: context, builder: (context) { return AlertDialog( title: const Text('创建学习计划'), content: const Text('计划创建功能开发中...'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: const Text('关闭'), ), ], ); }, ); } void _showPlanTemplates() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('计划模板功能开发中')), ); } void _handleMenuAction(String action) { switch (action) { case 'templates': _showPlanTemplates(); break; case 'statistics': _showStatistics(); break; case 'settings': _showSettings(); break; } } void _handlePlanAction(String action, StudyPlan plan) { switch (action) { case 'activate': _selectPlan(plan); break; case 'edit': _editPlan(plan); break; case 'duplicate': _duplicatePlan(plan); break; case 'delete': _deletePlan(plan); break; } } void _selectPlan(StudyPlan plan) { setState(() { _currentPlan = plan; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('已切换到计划:${plan.title}')), ); } void _editPlan(StudyPlan plan) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('编辑计划功能开发中')), ); } void _duplicatePlan(StudyPlan plan) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('复制计划功能开发中')), ); } void _deletePlan(StudyPlan plan) { showDialog( context: context, builder: (context) { return AlertDialog( title: const Text('确认删除'), content: Text('确定要删除计划 "${plan.title}" 吗?'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: const Text('取消'), ), TextButton( onPressed: () { Navigator.of(context).pop(); setState(() { _studyPlans.removeWhere((p) => p.id == plan.id); if (_currentPlan?.id == plan.id) { _currentPlan = _studyPlans.isNotEmpty ? _studyPlans.first : null; } }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('计划已删除')), ); }, child: const Text('删除', style: TextStyle(color: Colors.red)), ), ], ); }, ); } void _toggleTaskCompletion(StudyTask task) { setState(() { task.isCompleted = !task.isCompleted; if (_currentPlan != null) { if (task.isCompleted) { _currentPlan!.completedTasks++; } else { _currentPlan!.completedTasks--; } } }); } void _startTask(StudyTask task) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('开始任务:${task.title}')), ); } void _startStudySession() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('开始学习会话')), ); } void _startReview() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('开始复习')), ); } void _showStatistics() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('统计功能开发中')), ); } void _showSettings() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('设置功能开发中')), ); } void _editCurrentPlan() { if (_currentPlan != null) { _editPlan(_currentPlan!); } } IconData _getPlanTypeIcon(PlanType type) { switch (type) { case PlanType.vocabulary: return Icons.book; case PlanType.grammar: return Icons.school; case PlanType.speaking: return Icons.mic; case PlanType.listening: return Icons.headphones; case PlanType.reading: return Icons.article; case PlanType.writing: return Icons.edit; case PlanType.exam: return Icons.quiz; case PlanType.custom: return Icons.settings; } } Color _getPlanTypeColor(PlanType type) { switch (type) { case PlanType.vocabulary: return Colors.blue; case PlanType.grammar: return Colors.green; case PlanType.speaking: return Colors.orange; case PlanType.listening: return Colors.purple; case PlanType.reading: return Colors.teal; case PlanType.writing: return Colors.red; case PlanType.exam: return Colors.indigo; case PlanType.custom: return Colors.grey; } } } enum PlanType { vocabulary, grammar, speaking, listening, reading, writing, exam, custom, } enum TaskType { vocabulary, reading, listening, speaking, writing, review, } class StudyPlan { final String id; final String title; final String description; final PlanType type; final DateTime startDate; final DateTime endDate; final List tasks; final int totalTasks; int completedTasks; final bool isActive; StudyPlan({ required this.id, required this.title, required this.description, required this.type, required this.startDate, required this.endDate, required this.tasks, required this.totalTasks, required this.completedTasks, required this.isActive, }); } class StudyTask { final String id; final String title; final String description; final TaskType type; final DateTime dueDate; final int estimatedMinutes; bool isCompleted; StudyTask({ required this.id, required this.title, required this.description, required this.type, required this.dueDate, required this.estimatedMinutes, required this.isCompleted, }); } class Achievement { final String title; final String description; final IconData icon; final Color color; final bool isUnlocked; const Achievement({ required this.title, required this.description, required this.icon, required this.color, required this.isUnlocked, }); }