explanation

master
gitea 4 weeks ago
parent cd3b75995d
commit 82be8dbdf3

@ -17,18 +17,21 @@ class DefaultDeck {
prompt: 'What is the capital city of Australia?', prompt: 'What is the capital city of Australia?',
answers: ['Sydney', 'Melbourne', 'Canberra', 'Perth'], answers: ['Sydney', 'Melbourne', 'Canberra', 'Perth'],
correctAnswerIndices: [2], correctAnswerIndices: [2],
explanation: 'Canberra was chosen as the capital in 1908 as a compromise between Sydney and Melbourne.',
), ),
Question( Question(
id: 'gk_2', id: 'gk_2',
prompt: 'Which planet is known as the Red Planet?', prompt: 'Which planet is known as the Red Planet?',
answers: ['Venus', 'Mars', 'Jupiter', 'Saturn'], answers: ['Venus', 'Mars', 'Jupiter', 'Saturn'],
correctAnswerIndices: [1], correctAnswerIndices: [1],
explanation: 'Mars appears red due to iron oxide (rust) on its surface.',
), ),
Question( Question(
id: 'gk_3', id: 'gk_3',
prompt: 'What is the largest ocean on Earth?', prompt: 'What is the largest ocean on Earth?',
answers: ['Atlantic Ocean', 'Indian Ocean', 'Arctic Ocean', 'Pacific Ocean'], answers: ['Atlantic Ocean', 'Indian Ocean', 'Arctic Ocean', 'Pacific Ocean'],
correctAnswerIndices: [3], correctAnswerIndices: [3],
explanation: 'The Pacific Ocean covers about 63 million square miles and is larger than all of Earth\'s land area combined.',
), ),
Question( Question(
id: 'gk_4', id: 'gk_4',
@ -203,6 +206,7 @@ class DefaultDeck {
prompt: 'What is the capital city of Australia?', prompt: 'What is the capital city of Australia?',
answers: ['Sydney', 'Melbourne', 'Canberra', 'Perth'], answers: ['Sydney', 'Melbourne', 'Canberra', 'Perth'],
correctAnswerIndices: [2], correctAnswerIndices: [2],
explanation: 'Canberra was chosen as the capital in 1908 as a compromise between Sydney and Melbourne.',
consecutiveCorrect: 3, consecutiveCorrect: 3,
isKnown: true, isKnown: true,
priorityPoints: 0, priorityPoints: 0,
@ -216,6 +220,7 @@ class DefaultDeck {
prompt: 'Which planet is known as the Red Planet?', prompt: 'Which planet is known as the Red Planet?',
answers: ['Venus', 'Mars', 'Jupiter', 'Saturn'], answers: ['Venus', 'Mars', 'Jupiter', 'Saturn'],
correctAnswerIndices: [1], correctAnswerIndices: [1],
explanation: 'Mars appears red due to iron oxide (rust) on its surface.',
consecutiveCorrect: 3, consecutiveCorrect: 3,
isKnown: true, isKnown: true,
priorityPoints: 0, priorityPoints: 0,
@ -227,6 +232,7 @@ class DefaultDeck {
Question( Question(
id: 'gk_3', id: 'gk_3',
prompt: 'What is the largest ocean on Earth?', prompt: 'What is the largest ocean on Earth?',
explanation: 'The Pacific Ocean covers about 63 million square miles and is larger than all of Earth\'s land area combined.',
answers: ['Atlantic Ocean', 'Indian Ocean', 'Arctic Ocean', 'Pacific Ocean'], answers: ['Atlantic Ocean', 'Indian Ocean', 'Arctic Ocean', 'Pacific Ocean'],
correctAnswerIndices: [3], correctAnswerIndices: [3],
consecutiveCorrect: 3, consecutiveCorrect: 3,

@ -209,6 +209,22 @@ class _AttemptResultScreenState extends State<AttemptResultScreen> {
), ),
); );
}), }),
if (q.explanation != null && q.explanation!.trim().isNotEmpty) ...[
const SizedBox(height: 16),
const Divider(),
const SizedBox(height: 8),
Text(
'Explanation',
style: Theme.of(context).textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
q.explanation!,
style: Theme.of(context).textTheme.bodyMedium,
),
],
], ],
), ),
), ),

@ -161,6 +161,7 @@ class _DeckEditScreenState extends State<DeckEditScreen> {
final list = questions.map((q) => { final list = questions.map((q) => {
'id': q.id, 'id': q.id,
'prompt': q.prompt, 'prompt': q.prompt,
if (q.explanation != null && q.explanation!.isNotEmpty) 'explanation': q.explanation,
'answers': q.answers, 'answers': q.answers,
'correctAnswerIndices': q.correctAnswerIndices, 'correctAnswerIndices': q.correctAnswerIndices,
}).toList(); }).toList();
@ -378,6 +379,16 @@ class _QuestionEditorCardState extends State<QuestionEditorCard> {
), ),
maxLines: 2, maxLines: 2,
), ),
const SizedBox(height: 12),
TextField(
controller: widget.editor.explanationController,
decoration: const InputDecoration(
labelText: 'Explanation (optional)',
helperText: 'Shown when viewing question details after an attempt',
border: OutlineInputBorder(),
),
maxLines: 3,
),
const SizedBox(height: 16), const SizedBox(height: 16),
// Answers // Answers
@ -474,12 +485,14 @@ class _QuestionEditorCardState extends State<QuestionEditorCard> {
class QuestionEditor { class QuestionEditor {
final TextEditingController promptController; final TextEditingController promptController;
final TextEditingController explanationController;
final List<TextEditingController> answerControllers; final List<TextEditingController> answerControllers;
Set<int> correctAnswerIndices; Set<int> correctAnswerIndices;
final String? originalId; final String? originalId;
QuestionEditor({ QuestionEditor({
required this.promptController, required this.promptController,
required this.explanationController,
required this.answerControllers, required this.answerControllers,
Set<int>? correctAnswerIndices, Set<int>? correctAnswerIndices,
this.originalId, this.originalId,
@ -488,6 +501,7 @@ class QuestionEditor {
factory QuestionEditor.fromQuestion(Question question) { factory QuestionEditor.fromQuestion(Question question) {
return QuestionEditor( return QuestionEditor(
promptController: TextEditingController(text: question.prompt), promptController: TextEditingController(text: question.prompt),
explanationController: TextEditingController(text: question.explanation ?? ''),
answerControllers: question.answers answerControllers: question.answers
.map((answer) => TextEditingController(text: answer)) .map((answer) => TextEditingController(text: answer))
.toList(), .toList(),
@ -499,6 +513,7 @@ class QuestionEditor {
factory QuestionEditor.empty() { factory QuestionEditor.empty() {
return QuestionEditor( return QuestionEditor(
promptController: TextEditingController(), promptController: TextEditingController(),
explanationController: TextEditingController(),
answerControllers: [ answerControllers: [
TextEditingController(), TextEditingController(),
TextEditingController(), TextEditingController(),
@ -521,9 +536,11 @@ class QuestionEditor {
} }
Question toQuestion() { Question toQuestion() {
final explanationText = explanationController.text.trim();
return Question( return Question(
id: originalId ?? DateTime.now().millisecondsSinceEpoch.toString(), id: originalId ?? DateTime.now().millisecondsSinceEpoch.toString(),
prompt: promptController.text.trim(), prompt: promptController.text.trim(),
explanation: explanationText.isEmpty ? null : explanationText,
answers: answerControllers.map((c) => c.text.trim()).toList(), answers: answerControllers.map((c) => c.text.trim()).toList(),
correctAnswerIndices: correctAnswerIndices.toList()..sort(), correctAnswerIndices: correctAnswerIndices.toList()..sort(),
); );
@ -531,6 +548,7 @@ class QuestionEditor {
void dispose() { void dispose() {
promptController.dispose(); promptController.dispose();
explanationController.dispose();
for (final controller in answerControllers) { for (final controller in answerControllers) {
controller.dispose(); controller.dispose();
} }

@ -64,6 +64,7 @@ class _DeckImportScreenState extends State<DeckImportScreen> {
return Question( return Question(
id: questionMap['id'] as String? ?? '', id: questionMap['id'] as String? ?? '',
prompt: questionMap['prompt'] as String? ?? '', prompt: questionMap['prompt'] as String? ?? '',
explanation: questionMap['explanation'] as String?,
answers: (questionMap['answers'] as List<dynamic>?) answers: (questionMap['answers'] as List<dynamic>?)
?.map((e) => e.toString()) ?.map((e) => e.toString())
.toList() ?? .toList() ??
@ -643,6 +644,7 @@ class _DeckImportScreenState extends State<DeckImportScreen> {
const Text('• answers: array of strings (required)'), const Text('• answers: array of strings (required)'),
const Text('• correctAnswerIndex: number (deprecated, use correctAnswerIndices)'), const Text('• correctAnswerIndex: number (deprecated, use correctAnswerIndices)'),
const Text('• correctAnswerIndices: array of numbers (for multiple correct answers)'), const Text('• correctAnswerIndices: array of numbers (for multiple correct answers)'),
const Text('• explanation: string (optional, shown when viewing question details)'),
const Text('• isKnown: boolean (optional)'), const Text('• isKnown: boolean (optional)'),
const Text('• consecutiveCorrect: number (optional)'), const Text('• consecutiveCorrect: number (optional)'),
const Text('• priorityPoints: number (optional)'), const Text('• priorityPoints: number (optional)'),

@ -156,8 +156,10 @@ class _FlaggedQuestionsScreenState extends State<FlaggedQuestionsScreen> {
if (id == null) continue; if (id == null) continue;
final existing = questionMap[id]; final existing = questionMap[id];
if (existing == null) continue; if (existing == null) continue;
final explanationText = editor.explanationController.text.trim();
final updated = existing.copyWith( final updated = existing.copyWith(
prompt: editor.promptController.text.trim(), prompt: editor.promptController.text.trim(),
explanation: explanationText.isEmpty ? null : explanationText,
answers: editor.answerControllers.map((c) => c.text.trim()).toList(), answers: editor.answerControllers.map((c) => c.text.trim()).toList(),
correctAnswerIndices: editor.correctAnswerIndices.toList()..sort(), correctAnswerIndices: editor.correctAnswerIndices.toList()..sort(),
); );
@ -203,6 +205,7 @@ class _FlaggedQuestionsScreenState extends State<FlaggedQuestionsScreen> {
final list = questions.map((q) => { final list = questions.map((q) => {
'id': q.id, 'id': q.id,
'prompt': q.prompt, 'prompt': q.prompt,
if (q.explanation != null && q.explanation!.isNotEmpty) 'explanation': q.explanation,
'answers': q.answers, 'answers': q.answers,
'correctAnswerIndices': q.correctAnswerIndices, 'correctAnswerIndices': q.correctAnswerIndices,
}).toList(); }).toList();

@ -66,6 +66,7 @@ class DeckStorage {
'questions': deck.questions.map((q) => { 'questions': deck.questions.map((q) => {
'id': q.id, 'id': q.id,
'prompt': q.prompt, 'prompt': q.prompt,
if (q.explanation != null && q.explanation!.isNotEmpty) 'explanation': q.explanation,
'answers': q.answers, 'answers': q.answers,
'correctAnswerIndices': q.correctAnswerIndices, 'correctAnswerIndices': q.correctAnswerIndices,
'consecutiveCorrect': q.consecutiveCorrect, 'consecutiveCorrect': q.consecutiveCorrect,
@ -133,6 +134,7 @@ class DeckStorage {
return Question( return Question(
id: questionMap['id'] as String? ?? '', id: questionMap['id'] as String? ?? '',
prompt: questionMap['prompt'] as String? ?? '', prompt: questionMap['prompt'] as String? ?? '',
explanation: questionMap['explanation'] as String?,
answers: (questionMap['answers'] as List<dynamic>?) answers: (questionMap['answers'] as List<dynamic>?)
?.map((e) => e.toString()) ?.map((e) => e.toString())
.toList() ?? .toList() ??

@ -6,6 +6,9 @@ class Question {
/// The question prompt. /// The question prompt.
final String prompt; final String prompt;
/// Optional explanation shown when viewing question details (e.g. after an attempt).
final String? explanation;
/// List of possible answers. /// List of possible answers.
final List<String> answers; final List<String> answers;
@ -42,6 +45,7 @@ class Question {
Question({ Question({
required this.id, required this.id,
required this.prompt, required this.prompt,
this.explanation,
required this.answers, required this.answers,
List<int>? correctAnswerIndices, List<int>? correctAnswerIndices,
@Deprecated('Use correctAnswerIndices instead') int? correctAnswerIndex, @Deprecated('Use correctAnswerIndices instead') int? correctAnswerIndex,
@ -60,6 +64,7 @@ class Question {
Question copyWith({ Question copyWith({
String? id, String? id,
String? prompt, String? prompt,
String? explanation,
List<String>? answers, List<String>? answers,
List<int>? correctAnswerIndices, List<int>? correctAnswerIndices,
@Deprecated('Use correctAnswerIndices instead') int? correctAnswerIndex, @Deprecated('Use correctAnswerIndices instead') int? correctAnswerIndex,
@ -74,6 +79,7 @@ class Question {
return Question( return Question(
id: id ?? this.id, id: id ?? this.id,
prompt: prompt ?? this.prompt, prompt: prompt ?? this.prompt,
explanation: explanation ?? this.explanation,
answers: answers ?? this.answers, answers: answers ?? this.answers,
correctAnswerIndices: correctAnswerIndices ?? this.correctAnswerIndices, correctAnswerIndices: correctAnswerIndices ?? this.correctAnswerIndices,
correctAnswerIndex: correctAnswerIndex ?? this.correctAnswerIndex, correctAnswerIndex: correctAnswerIndex ?? this.correctAnswerIndex,
@ -119,6 +125,7 @@ class Question {
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
id == other.id && id == other.id &&
prompt == other.prompt && prompt == other.prompt &&
explanation == other.explanation &&
answers.toString() == other.answers.toString() && answers.toString() == other.answers.toString() &&
correctAnswerIndices.toString() == other.correctAnswerIndices.toString() && correctAnswerIndices.toString() == other.correctAnswerIndices.toString() &&
consecutiveCorrect == other.consecutiveCorrect && consecutiveCorrect == other.consecutiveCorrect &&
@ -133,6 +140,7 @@ class Question {
int get hashCode => int get hashCode =>
id.hashCode ^ id.hashCode ^
prompt.hashCode ^ prompt.hashCode ^
(explanation?.hashCode ?? 0) ^
answers.hashCode ^ answers.hashCode ^
correctAnswerIndices.hashCode ^ correctAnswerIndices.hashCode ^
consecutiveCorrect.hashCode ^ consecutiveCorrect.hashCode ^

Loading…
Cancel
Save

Powered by TurnKey Linux.