import 'package:flutter/material.dart'; import 'package:practice_engine/practice_engine.dart'; import '../routes.dart'; import '../widgets/question_card.dart'; import '../widgets/answer_option.dart'; class AttemptScreen extends StatefulWidget { const AttemptScreen({super.key}); @override State createState() => _AttemptScreenState(); } class _AttemptScreenState extends State { Deck? _deck; Attempt? _attempt; AttemptService? _attemptService; int _currentQuestionIndex = 0; int? _selectedAnswerIndex; final Map _answers = {}; final Map _manualOverrides = {}; @override void initState() { super.initState(); _attemptService = AttemptService(); } @override void didChangeDependencies() { super.didChangeDependencies(); if (_deck == null) { // Get deck from route arguments final args = ModalRoute.of(context)?.settings.arguments; _deck = args is Deck ? args : _createSampleDeck(); _attempt = _attemptService!.createAttempt(deck: _deck!); } } Deck _createSampleDeck() { const config = DeckConfig(); final questions = List.generate(10, (i) { return Question( id: 'q$i', prompt: 'Sample Question $i?', answers: ['A', 'B', 'C', 'D'], correctAnswerIndex: i % 4, ); }); return Deck( id: 'sample', title: 'Sample', description: 'Sample', questions: questions, config: config, ); } Question get _currentQuestion => _attempt!.questions[_currentQuestionIndex]; bool get _isLastQuestion => _currentQuestionIndex == _attempt!.questions.length - 1; bool get _hasAnswer => _selectedAnswerIndex != null; void _selectAnswer(int index) { setState(() { _selectedAnswerIndex = index; }); if (_deck != null && _deck!.config.immediateFeedbackEnabled) { // Show feedback immediately } } void _submitAnswer() { if (_selectedAnswerIndex == null) return; _answers[_currentQuestion.id] = _selectedAnswerIndex!; if (_isLastQuestion) { _completeAttempt(); } else { setState(() { _currentQuestionIndex++; _selectedAnswerIndex = null; }); } } void _markAsKnown() { if (_deck == null) return; setState(() { _manualOverrides[_currentQuestion.id] = false; // Not needs practice _deck = DeckService.markQuestionAsKnown( deck: _deck!, questionId: _currentQuestion.id, ); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Question marked as Known'), backgroundColor: Colors.green, ), ); } void _markAsNeedsPractice() { if (_deck == null) return; setState(() { _manualOverrides[_currentQuestion.id] = true; _deck = DeckService.markQuestionAsNeedsPractice( deck: _deck!, questionId: _currentQuestion.id, ); }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Question marked as Needs Practice'), ), ); } void _completeAttempt() { if (_deck == null || _attempt == null || _attemptService == null) return; final result = _attemptService!.processAttempt( deck: _deck!, attempt: _attempt!, answers: _answers, manualOverrides: _manualOverrides, endTime: DateTime.now().millisecondsSinceEpoch, ); Navigator.pushReplacementNamed( context, Routes.attemptResult, arguments: { 'deck': result.updatedDeck, 'result': result.result, 'attempt': _attempt, }, ); } @override Widget build(BuildContext context) { if (_deck == null || _attempt == null) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar( title: Text('Attempt - ${_deck!.title}'), ), body: Column( children: [ // Progress Indicator LinearProgressIndicator( value: (_currentQuestionIndex + 1) / _attempt!.questions.length, minHeight: 4, ), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Question ${_currentQuestionIndex + 1} of ${_attempt!.questions.length}', style: Theme.of(context).textTheme.bodyMedium, ), if (_currentQuestion.isKnown) Chip( label: const Text('Known'), avatar: const Icon(Icons.check_circle, size: 18), ), ], ), ), // Question Card Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ QuestionCard(question: _currentQuestion), const SizedBox(height: 24), // Answer Options ...List.generate( _currentQuestion.answers.length, (index) => Padding( padding: const EdgeInsets.only(bottom: 8), child: AnswerOption( text: _currentQuestion.answers[index], isSelected: _selectedAnswerIndex == index, onTap: () => _selectAnswer(index), isCorrect: _deck != null && _deck!.config.immediateFeedbackEnabled && _selectedAnswerIndex == index ? index == _currentQuestion.correctAnswerIndex : null, ), ), ), const SizedBox(height: 24), // Manual Override Buttons Card( child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Manual Override', style: Theme.of(context).textTheme.titleSmall, ), const SizedBox(height: 12), Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: _markAsKnown, icon: const Icon(Icons.check_circle), label: const Text('Mark as Known'), ), ), const SizedBox(width: 8), Expanded( child: OutlinedButton.icon( onPressed: _markAsNeedsPractice, icon: const Icon(Icons.school), label: const Text('Needs Practice'), ), ), ], ), ], ), ), ), ], ), ), ), // Submit/Next Button Padding( padding: const EdgeInsets.all(16), child: FilledButton( onPressed: _hasAnswer ? _submitAnswer : null, style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), child: Text(_isLastQuestion ? 'Complete Attempt' : 'Next Question'), ), ), ], ), ); } }