import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../auth/providers/auth_provider.dart'; import '../widgets/writing_mode_card.dart'; import '../models/writing_task.dart'; import '../models/writing_record.dart'; import '../providers/writing_provider.dart'; import 'exam_writing_screen.dart'; /// 写作练习主页面 class WritingHomeScreen extends ConsumerStatefulWidget { const WritingHomeScreen({super.key}); @override ConsumerState createState() => _WritingHomeScreenState(); } class _WritingHomeScreenState extends ConsumerState { bool _statsForceRefresh = false; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.black87), onPressed: () => Navigator.of(context).pop(), ), title: const Text( '写作练习', style: TextStyle( color: Colors.black87, fontSize: 18, fontWeight: FontWeight.w600, ), ), centerTitle: true, ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildWritingModes(), const SizedBox(height: 20), _buildExamWriting(context), const SizedBox(height: 20), _buildRecentWritings(), const SizedBox(height: 20), _buildWritingProgress(), const SizedBox(height: 100), // 底部导航栏空间 ], ), ), ), ); } Widget _buildWritingModes() { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '写作模式', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: WritingModeCard( title: '议论文写作', subtitle: '观点论述练习', icon: Icons.article, color: const Color(0xFF2196F3), type: WritingType.essay, ), ), const SizedBox(width: 16), Expanded( child: WritingModeCard( title: '应用文写作', subtitle: '实用文体练习', icon: Icons.email, color: const Color(0xFF4CAF50), type: WritingType.email, ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: WritingModeCard( title: '初级练习', subtitle: '基础写作训练', icon: Icons.school, color: const Color(0xFFFF9800), difficulty: WritingDifficulty.beginner, ), ), const SizedBox(width: 16), Expanded( child: WritingModeCard( title: '高级练习', subtitle: '进阶写作挑战', icon: Icons.star, color: const Color(0xFFF44336), difficulty: WritingDifficulty.advanced, ), ), ], ), ], ), ); } Widget _buildExamWriting(BuildContext context) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '考试写作', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 2, crossAxisSpacing: 12, mainAxisSpacing: 12, childAspectRatio: 2.5, children: [ _buildExamCard(context, '四六级', Icons.school, Colors.blue, ExamType.cet), _buildExamCard(context, '考研', Icons.menu_book, Colors.red, ExamType.kaoyan), _buildExamCard(context, '托福', Icons.flight_takeoff, Colors.green, ExamType.toefl), _buildExamCard(context, '雅思', Icons.language, Colors.purple, ExamType.ielts), ], ), ], ), ); } Widget _buildExamCard(BuildContext context, String title, IconData icon, Color color, ExamType examType) { return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ExamWritingScreen( examType: examType, title: title, ), ), ); }, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon( icon, color: color, size: 24, ), const SizedBox(width: 8), Expanded( child: Text( title, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: color, ), ), ), ], ), ), ); } Widget _buildRecentWritings() { final user = ref.watch(currentUserProvider); final userId = user?.id?.toString() ?? ''; final historyAsync = ref.watch(userWritingHistoryProvider(userId)); return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '最近写作', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox.shrink(), ], ), const SizedBox(height: 16), historyAsync.when( data: (records) { if (records.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.edit_note, size: 48, color: Colors.grey[400], ), const SizedBox(height: 12), Text( '还没有完成的写作', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), const SizedBox(height: 8), Text( '完成一篇写作练习后,记录会显示在这里', style: TextStyle( fontSize: 12, color: Colors.grey[500], ), textAlign: TextAlign.center, ), ], ), ); } return Column( children: records.take(3).map((record) { // 将WritingSubmission转换为WritingRecord显示 final writingRecord = WritingRecord( id: record.id, taskId: record.taskId, taskTitle: '写作任务', taskDescription: '', content: record.content, wordCount: record.wordCount, timeUsed: record.timeSpent, score: record.score?.totalScore.toInt() ?? 0, feedback: record.feedback?.toJson(), completedAt: record.submittedAt, ); return Padding( padding: const EdgeInsets.only(bottom: 12), child: _buildWritingRecordItem(writingRecord), ); }).toList(), ); }, loading: () => const Center( child: Padding( padding: EdgeInsets.all(20.0), child: CircularProgressIndicator(), ), ), error: (error, stack) => Container( padding: const EdgeInsets.all(20), child: Column( children: [ Icon( Icons.error_outline, size: 48, color: Colors.grey[400], ), const SizedBox(height: 12), Text( '加载失败', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), const SizedBox(height: 8), TextButton( onPressed: () { ref.invalidate(userWritingHistoryProvider(userId)); }, child: const Text('重试'), ), ], ), ), ), ], ), ); } Widget _buildWritingRecordItem(WritingRecord record) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: const Icon( Icons.description, color: Color(0xFF2196F3), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( record.taskTitle, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), Text( record.taskDescription, style: const TextStyle( fontSize: 12, color: Colors.grey, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), Row( children: [ Text( record.formattedDate, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), const SizedBox(width: 8), Text( '${record.wordCount}词', style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), const SizedBox(width: 8), Text( record.formattedTime, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ], ), ], ), ), Column( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _getScoreColor(record.score), borderRadius: BorderRadius.circular(12), ), child: Text( record.scoreText, style: const TextStyle( fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), ], ), ); } Color _getScoreColor(int score) { if (score >= 90) return const Color(0xFF4CAF50); if (score >= 80) return const Color(0xFF2196F3); if (score >= 70) return const Color(0xFFFF9800); return const Color(0xFFFF5722); } Widget _buildWritingItem( String title, String subtitle, String score, String date, ) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: const Color(0xFF2196F3).withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: const Icon( Icons.description, color: Color(0xFF2196F3), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ), Text( subtitle, style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), const SizedBox(height: 4), Text( date, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ], ), ), Column( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color(0xFF2196F3), borderRadius: BorderRadius.circular(12), ), child: Text( score, style: const TextStyle( fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), ], ), ); } Widget _buildWritingProgress() { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '写作统计', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Consumer( builder: (context, ref, _) { final service = ref.watch(writingServiceProvider); final user = ref.watch(currentUserProvider); final userId = user?.id?.toString() ?? ''; return FutureBuilder( future: service.getUserWritingStats(userId, forceRefresh: _statsForceRefresh), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 48, color: Colors.grey[400], ), const SizedBox(height: 12), Text( '统计加载失败', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), const SizedBox(height: 8), TextButton( onPressed: () { setState(() { _statsForceRefresh = !_statsForceRefresh; }); }, child: const Text('重试'), ), ], ), ); } if (!snapshot.hasData) { return const SizedBox.shrink(); } final stats = snapshot.data!; if (stats.completedTasks == 0) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.insights, size: 48, color: Colors.grey[400], ), const SizedBox(height: 12), Text( '暂无写作统计', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), const SizedBox(height: 8), TextButton( onPressed: () { setState(() { _statsForceRefresh = !_statsForceRefresh; }); }, child: const Text('刷新'), ), ], ), ); } return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildProgressItem('${stats.completedTasks}', '完成篇数', Icons.article), _buildProgressItem('${stats.averageScore.toStringAsFixed(0)}', '平均分', Icons.grade), _buildProgressItem('${((stats.skillAnalysis.criteriaScores['grammar'] ?? 0.0) * 100).toStringAsFixed(0)}%', '语法正确率', Icons.spellcheck), ], ); }, ); }, ), ], ), ); } Widget _buildProgressItem(String value, String label, IconData icon) { return Column( children: [ Icon( icon, color: const Color(0xFF2196F3), size: 24, ), const SizedBox(height: 8), Text( value, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Color(0xFF2196F3), ), ), const SizedBox(height: 4), Text( label, style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), ], ); } }