import 'package:flutter/material.dart'; import 'package:practice_engine/practice_engine.dart'; import '../routes.dart'; import '../widgets/status_chip.dart'; import '../services/deck_storage.dart'; class AttemptResultScreen extends StatefulWidget { const AttemptResultScreen({super.key}); @override State createState() => _AttemptResultScreenState(); } class _AttemptResultScreenState extends State { Deck? _deck; AttemptResult? _result; final DeckStorage _deckStorage = DeckStorage(); @override void initState() { super.initState(); } @override void didChangeDependencies() { super.didChangeDependencies(); if (_deck == null) { // Get data from route arguments final args = ModalRoute.of(context)?.settings.arguments as Map?; _deck = args?['deck'] as Deck? ?? _createSampleDeck(); _result = args?['result'] as AttemptResult? ?? _createSampleResult(); } } Deck _createSampleDeck() { return Deck( id: 'sample', title: 'Sample', description: 'Sample', questions: [], config: const DeckConfig(), ); } AttemptResult _createSampleResult() { return AttemptResult( totalQuestions: 10, correctCount: 7, percentageCorrect: 70.0, timeSpent: 300000, incorrectQuestions: [], allResults: [], ); } String _formatTime(int milliseconds) { final seconds = milliseconds ~/ 1000; final minutes = seconds ~/ 60; final remainingSeconds = seconds % 60; return '${minutes}m ${remainingSeconds}s'; } void _repeatSameAttempt() { if (_deck == null) return; Navigator.pushReplacementNamed(context, Routes.attempt, arguments: _deck); } void _newAttempt() { if (_deck == null) return; Navigator.pushReplacementNamed(context, Routes.attempt, arguments: _deck); } void _done() { if (_deck == null) return; // Save the updated deck to storage _deckStorage.saveDeckSync(_deck!); // Navigate back to deck list Navigator.pushNamedAndRemoveUntil( context, Routes.deckList, (route) => false, ); } @override Widget build(BuildContext context) { if (_deck == null || _result == null) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar( title: const Text('Attempt Results'), ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Summary Card Card( child: Padding( padding: const EdgeInsets.all(24), child: Column( children: [ Text( 'Results', style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 24), CircularProgressIndicator( value: _result!.percentageCorrect / 100, strokeWidth: 8, ), const SizedBox(height: 16), Text( '${_result!.percentageCorrect.toStringAsFixed(1)}%', style: Theme.of(context).textTheme.displayMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( '${_result!.correctCount} of ${_result!.totalQuestions} correct', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _StatItem( label: 'Time', value: _formatTime(_result!.timeSpent), icon: Icons.timer, ), _StatItem( label: 'Correct', value: '${_result!.correctCount}', icon: Icons.check_circle, ), _StatItem( label: 'Incorrect', value: '${_result!.totalQuestions - _result!.correctCount}', icon: Icons.cancel, ), ], ), ], ), ), ), const SizedBox(height: 24), // Incorrect Questions if (_result!.incorrectQuestions.isNotEmpty) ...[ Text( 'Incorrect Questions', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 12), ..._result!.incorrectQuestions.map((answerResult) { return Card( margin: const EdgeInsets.only(bottom: 8), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( answerResult.question.prompt, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), Row( children: [ StatusChip(statusChange: answerResult.statusChange), const Spacer(), Text( 'Your answer${answerResult.userAnswerIndices.length > 1 ? 's' : ''}: ${answerResult.userAnswerIndices.map((idx) => answerResult.question.answers[idx]).join(', ')}', style: TextStyle( color: Colors.red, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 8), Text( 'Correct answer${answerResult.question.correctIndices.length > 1 ? 's' : ''}: ${answerResult.question.correctIndices.map((idx) => answerResult.question.answers[idx]).join(', ')}', style: TextStyle( color: Colors.green, fontWeight: FontWeight.bold, ), ), ], ), ), ); }), const SizedBox(height: 24), ], // All Results with Status Text( 'Question Status Changes', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 12), ..._result!.allResults.map((answerResult) { return Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( title: Text(answerResult.question.prompt), subtitle: Text( answerResult.isCorrect ? 'Correct' : 'Incorrect - Selected: ${answerResult.userAnswerIndices.map((idx) => answerResult.question.answers[idx]).join(', ')}', ), trailing: StatusChip(statusChange: answerResult.statusChange), ), ); }), const SizedBox(height: 24), // Action Buttons FilledButton.icon( onPressed: _repeatSameAttempt, icon: const Icon(Icons.repeat), label: const Text('Repeat Same Attempt'), style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), ), const SizedBox(height: 12), OutlinedButton.icon( onPressed: _newAttempt, icon: const Icon(Icons.refresh), label: const Text('New Attempt'), ), const SizedBox(height: 12), OutlinedButton.icon( onPressed: _done, icon: const Icon(Icons.check), label: const Text('Done'), ), ], ), ), ); } } class _StatItem extends StatelessWidget { final String label; final String value; final IconData icon; const _StatItem({ required this.label, required this.value, required this.icon, }); @override Widget build(BuildContext context) { return Column( children: [ Icon(icon, size: 32), const SizedBox(height: 8), Text( value, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, ), ), Text( label, style: Theme.of(context).textTheme.bodySmall, ), ], ); } }