|
|
|
|
@ -3,6 +3,7 @@ import 'package:practice_engine/practice_engine.dart';
|
|
|
|
|
import '../routes.dart';
|
|
|
|
|
import '../widgets/status_chip.dart';
|
|
|
|
|
import '../services/deck_storage.dart';
|
|
|
|
|
import '../utils/top_snackbar.dart';
|
|
|
|
|
|
|
|
|
|
class AttemptResultScreen extends StatefulWidget {
|
|
|
|
|
const AttemptResultScreen({super.key});
|
|
|
|
|
@ -82,6 +83,42 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Question? _currentQuestionInDeck(String questionId) {
|
|
|
|
|
if (_deck == null) return null;
|
|
|
|
|
try {
|
|
|
|
|
return _deck!.questions.firstWhere((q) => q.id == questionId);
|
|
|
|
|
} catch (_) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _toggleFlag(String questionId) {
|
|
|
|
|
if (_deck == null) return;
|
|
|
|
|
setState(() {
|
|
|
|
|
_deck = DeckService.toggleQuestionFlag(deck: _deck!, questionId: questionId);
|
|
|
|
|
});
|
|
|
|
|
_deckStorage.saveDeckSync(_deck!);
|
|
|
|
|
final q = _currentQuestionInDeck(questionId);
|
|
|
|
|
showTopSnackBar(
|
|
|
|
|
context,
|
|
|
|
|
q?.isFlagged == true ? 'Question flagged for review' : 'Question unflagged',
|
|
|
|
|
backgroundColor: q?.isFlagged == true ? Colors.orange : null,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _markNeedsPractice(String questionId) {
|
|
|
|
|
if (_deck == null) return;
|
|
|
|
|
setState(() {
|
|
|
|
|
_deck = DeckService.markQuestionAsNeedsPractice(deck: _deck!, questionId: questionId);
|
|
|
|
|
});
|
|
|
|
|
_deckStorage.saveDeckSync(_deck!);
|
|
|
|
|
showTopSnackBar(
|
|
|
|
|
context,
|
|
|
|
|
'Marked as needs practice',
|
|
|
|
|
backgroundColor: Colors.blue,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
if (_deck == null || _result == null) {
|
|
|
|
|
@ -217,6 +254,8 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
..._result!.incorrectQuestions.map((answerResult) {
|
|
|
|
|
final currentQ = _currentQuestionInDeck(answerResult.question.id);
|
|
|
|
|
final isFlagged = currentQ?.isFlagged ?? false;
|
|
|
|
|
return Card(
|
|
|
|
|
margin: const EdgeInsets.only(bottom: 8),
|
|
|
|
|
child: Padding(
|
|
|
|
|
@ -250,6 +289,25 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
|
|
|
children: [
|
|
|
|
|
TextButton.icon(
|
|
|
|
|
onPressed: () => _markNeedsPractice(answerResult.question.id),
|
|
|
|
|
icon: const Icon(Icons.replay, size: 20),
|
|
|
|
|
label: const Text('Needs practice'),
|
|
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () => _toggleFlag(answerResult.question.id),
|
|
|
|
|
icon: Icon(
|
|
|
|
|
isFlagged ? Icons.flag : Icons.outlined_flag,
|
|
|
|
|
color: isFlagged ? Colors.red : null,
|
|
|
|
|
),
|
|
|
|
|
tooltip: isFlagged ? 'Unflag' : 'Flag for review',
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
@ -265,6 +323,8 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
..._result!.allResults.map((answerResult) {
|
|
|
|
|
final currentQ = _currentQuestionInDeck(answerResult.question.id);
|
|
|
|
|
final isFlagged = currentQ?.isFlagged ?? false;
|
|
|
|
|
return Card(
|
|
|
|
|
margin: const EdgeInsets.only(bottom: 8),
|
|
|
|
|
child: ListTile(
|
|
|
|
|
@ -274,7 +334,26 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|
|
|
|
? 'Correct'
|
|
|
|
|
: 'Incorrect - Selected: ${answerResult.userAnswerIndices.map((idx) => answerResult.question.answers[idx]).join(', ')}',
|
|
|
|
|
),
|
|
|
|
|
trailing: StatusChip(statusChange: answerResult.statusChange),
|
|
|
|
|
trailing: Row(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
|
|
|
|
StatusChip(statusChange: answerResult.statusChange),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () => _markNeedsPractice(answerResult.question.id),
|
|
|
|
|
icon: const Icon(Icons.replay, size: 22),
|
|
|
|
|
tooltip: 'Mark as needs practice',
|
|
|
|
|
),
|
|
|
|
|
IconButton(
|
|
|
|
|
onPressed: () => _toggleFlag(answerResult.question.id),
|
|
|
|
|
icon: Icon(
|
|
|
|
|
isFlagged ? Icons.flag : Icons.outlined_flag,
|
|
|
|
|
color: isFlagged ? Colors.red : null,
|
|
|
|
|
size: 22,
|
|
|
|
|
),
|
|
|
|
|
tooltip: isFlagged ? 'Unflag' : 'Flag for review',
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
|