import 'package:flutter/material.dart'; import '../models/writing_task.dart'; import '../models/writing_record.dart'; import '../services/writing_record_service.dart'; /// 写作结果页面 class WritingResultScreen extends StatefulWidget { final WritingTask task; final String content; final int wordCount; final int timeUsed; // 使用的时间(秒) const WritingResultScreen({ super.key, required this.task, required this.content, required this.wordCount, required this.timeUsed, }); @override State createState() => _WritingResultScreenState(); } class _WritingResultScreenState extends State { late int score; late Map feedback; bool _recordSaved = false; @override void initState() { super.initState(); score = _calculateScore(); feedback = _generateFeedback(score); _saveWritingRecord(); } Future _saveWritingRecord() async { if (_recordSaved) return; final record = WritingRecord( id: 'record_${DateTime.now().millisecondsSinceEpoch}', taskId: widget.task.id, taskTitle: widget.task.title, taskDescription: widget.task.description, content: widget.content, wordCount: widget.wordCount, timeUsed: widget.timeUsed, score: score, completedAt: DateTime.now(), feedback: feedback, ); await WritingRecordService.saveRecord(record); _recordSaved = true; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: AppBar( title: const Text('写作结果'), backgroundColor: Colors.white, foregroundColor: Colors.black, elevation: 0, automaticallyImplyLeading: false, actions: [ IconButton( icon: const Icon(Icons.close), onPressed: () { Navigator.popUntil(context, (route) => route.isFirst); }, ), ], ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildScoreCard(score), const SizedBox(height: 20), _buildStatistics(), const SizedBox(height: 20), _buildFeedback(feedback), const SizedBox(height: 20), _buildContentReview(), const SizedBox(height: 30), _buildActionButtons(context), ], ), ), ); } Widget _buildScoreCard(int score) { Color scoreColor; String scoreLevel; if (score >= 90) { scoreColor = const Color(0xFF4CAF50); scoreLevel = '优秀'; } else if (score >= 80) { scoreColor = const Color(0xFF2196F3); scoreLevel = '良好'; } else if (score >= 70) { scoreColor = const Color(0xFFFF9800); scoreLevel = '一般'; } else { scoreColor = const Color(0xFFF44336); scoreLevel = '需要改进'; } return Container( padding: const EdgeInsets.all(24), 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( children: [ const Text( '写作得分', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Container( width: 120, height: 120, decoration: BoxDecoration( shape: BoxShape.circle, color: scoreColor.withOpacity(0.1), border: Border.all( color: scoreColor, width: 4, ), ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '$score', style: TextStyle( fontSize: 36, fontWeight: FontWeight.bold, color: scoreColor, ), ), Text( scoreLevel, style: TextStyle( fontSize: 14, color: scoreColor, fontWeight: FontWeight.w500, ), ), ], ), ), ), const SizedBox(height: 16), Text( widget.task.title, style: const TextStyle( fontSize: 16, color: Colors.grey, ), textAlign: TextAlign.center, ), ], ), ); } Widget _buildStatistics() { final timeUsedMinutes = (widget.timeUsed / 60).ceil(); final timeLimit = widget.task.timeLimit; final wordLimit = widget.task.wordLimit; 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: _buildStatItem( Icons.text_fields, '字数', '${widget.wordCount} / $wordLimit', widget.wordCount <= wordLimit ? const Color(0xFF4CAF50) : const Color(0xFFF44336), ), ), Expanded( child: _buildStatItem( Icons.timer, '用时', '$timeUsedMinutes / $timeLimit 分钟', const Color(0xFF2196F3), ), ), ], ), const SizedBox(height: 16), Row( children: [ Expanded( child: _buildStatItem( Icons.category, '类型', widget.task.type.displayName, const Color(0xFFFF9800), ), ), Expanded( child: _buildStatItem( Icons.star, '难度', widget.task.difficulty.displayName, _getDifficultyColor(widget.task.difficulty), ), ), ], ), ], ), ); } Widget _buildStatItem(IconData icon, String label, String value, Color color) { return Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Icon( icon, color: color, size: 24, ), const SizedBox(height: 8), Text( label, style: const TextStyle( fontSize: 12, color: Colors.grey, ), ), const SizedBox(height: 4), Text( value, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: color, ), textAlign: TextAlign.center, ), ], ), ); } Widget _buildFeedback(Map feedback) { 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), _buildFeedbackItem('内容质量', feedback['content'], feedback['contentScore']), const SizedBox(height: 12), _buildFeedbackItem('语法准确性', feedback['grammar'], feedback['grammarScore']), const SizedBox(height: 12), _buildFeedbackItem('词汇运用', feedback['vocabulary'], feedback['vocabularyScore']), const SizedBox(height: 12), _buildFeedbackItem('结构组织', feedback['structure'], feedback['structureScore']), ], ), ); } Widget _buildFeedbackItem(String title, String description, int score) { Color scoreColor; if (score >= 90) { scoreColor = const Color(0xFF4CAF50); } else if (score >= 80) { scoreColor = const Color(0xFF2196F3); } else if (score >= 70) { scoreColor = const Color(0xFFFF9800); } else { scoreColor = const Color(0xFFF44336); } return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( title, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: scoreColor, borderRadius: BorderRadius.circular(12), ), child: Text( '$score分', style: const TextStyle( fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 8), Text( description, style: const TextStyle( fontSize: 14, color: Colors.grey, height: 1.5, ), ), ], ), ); } Widget _buildContentReview() { 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: 12), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.grey[50], borderRadius: BorderRadius.circular(12), ), child: Text( widget.content.isEmpty ? '未提交任何内容' : widget.content, style: const TextStyle( fontSize: 14, height: 1.6, ), ), ), ], ), ); } Widget _buildActionButtons(BuildContext context) { return Row( children: [ Expanded( child: OutlinedButton( onPressed: () { Navigator.popUntil(context, (route) => route.isFirst); }, style: OutlinedButton.styleFrom( foregroundColor: const Color(0xFF2196F3), side: const BorderSide(color: Color(0xFF2196F3)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(vertical: 16), ), child: const Text('返回首页'), ), ), const SizedBox(width: 16), Expanded( child: ElevatedButton( onPressed: () { // TODO: 实现重新练习功能 Navigator.pop(context); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF2196F3), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(vertical: 16), elevation: 0, ), child: const Text( '重新练习', style: TextStyle( fontWeight: FontWeight.bold, ), ), ), ), ], ); } int _calculateScore() { int score = 70; // 基础分数 // 字数评分 if (widget.wordCount >= widget.task.wordLimit * 0.8 && widget.wordCount <= widget.task.wordLimit * 1.2) { score += 10; } else if (widget.wordCount >= widget.task.wordLimit * 0.6) { score += 5; } // 时间评分 final timeUsedMinutes = widget.timeUsed / 60; if (timeUsedMinutes <= widget.task.timeLimit) { score += 10; } // 内容长度评分 if (widget.content.length > 100) { score += 10; } return score.clamp(0, 100); } Map _generateFeedback(int score) { return { 'content': score >= 80 ? '内容丰富,主题明确,论述清晰。' : '内容需要更加充实,建议增加具体的例子和细节。', 'contentScore': score >= 80 ? 85 : 75, 'grammar': score >= 80 ? '语法使用准确,句式多样。' : '语法基本正确,建议注意时态和语态的使用。', 'grammarScore': score >= 80 ? 88 : 78, 'vocabulary': score >= 80 ? '词汇运用恰当,表达准确。' : '词汇使用基本准确,可以尝试使用更多高级词汇。', 'vocabularyScore': score >= 80 ? 82 : 72, 'structure': score >= 80 ? '文章结构清晰,逻辑性强。' : '文章结构基本合理,建议加强段落之间的连接。', 'structureScore': score >= 80 ? 86 : 76, }; } Color _getDifficultyColor(WritingDifficulty difficulty) { switch (difficulty) { case WritingDifficulty.beginner: return const Color(0xFF4CAF50); case WritingDifficulty.elementary: return const Color(0xFF8BC34A); case WritingDifficulty.intermediate: return const Color(0xFFFF9800); case WritingDifficulty.upperIntermediate: return const Color(0xFFFF5722); case WritingDifficulty.advanced: return const Color(0xFFF44336); } } }