flag and end of attempt
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:practice_engine/practice_engine.dart';
|
|||||||
import '../routes.dart';
|
import '../routes.dart';
|
||||||
import '../widgets/status_chip.dart';
|
import '../widgets/status_chip.dart';
|
||||||
import '../services/deck_storage.dart';
|
import '../services/deck_storage.dart';
|
||||||
|
import '../utils/top_snackbar.dart';
|
||||||
|
|
||||||
class AttemptResultScreen extends StatefulWidget {
|
class AttemptResultScreen extends StatefulWidget {
|
||||||
const AttemptResultScreen({super.key});
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (_deck == null || _result == null) {
|
if (_deck == null || _result == null) {
|
||||||
@@ -217,6 +254,8 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
..._result!.incorrectQuestions.map((answerResult) {
|
..._result!.incorrectQuestions.map((answerResult) {
|
||||||
|
final currentQ = _currentQuestionInDeck(answerResult.question.id);
|
||||||
|
final isFlagged = currentQ?.isFlagged ?? false;
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -250,6 +289,25 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|||||||
fontWeight: FontWeight.bold,
|
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),
|
const SizedBox(height: 12),
|
||||||
..._result!.allResults.map((answerResult) {
|
..._result!.allResults.map((answerResult) {
|
||||||
|
final currentQ = _currentQuestionInDeck(answerResult.question.id);
|
||||||
|
final isFlagged = currentQ?.isFlagged ?? false;
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@@ -274,7 +334,26 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
|
|||||||
? 'Correct'
|
? 'Correct'
|
||||||
: 'Incorrect - Selected: ${answerResult.userAnswerIndices.map((idx) => answerResult.question.answers[idx]).join(', ')}',
|
: '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',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user