Add incorrect answers on result page

Add visual improvement
This commit is contained in:
Jakob Kordež 2022-08-04 13:29:29 +02:00
parent 0ae87202d4
commit e0bbec0568
6 changed files with 238 additions and 95 deletions

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../models/category.dart';

View File

@ -36,8 +36,8 @@ class GeneratorTest extends GeneratorState {
final Duration timerDuration;
const GeneratorTest({
int questionCount = 60,
this.timerDuration = const Duration(minutes: 90),
questionCount = kDebugMode ? 6 : 60,
this.timerDuration = const Duration(minutes: kDebugMode ? 9 : 90),
}) : super(questionCount);
GeneratorTest copyWith({

View File

@ -14,6 +14,11 @@ class PracticeTab extends StatelessWidget {
Widget build(BuildContext context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Vaja',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 15),
const Text(
'Izberi pogročje in vpiši število vprašanj, ki jih želiš generirati. '
'Če želiš generirati vprašanja iz vseh področij, pusti polje za področje prazno.'),
@ -22,6 +27,7 @@ class PracticeTab extends StatelessWidget {
builder: (context, constraints) {
final mxW = constraints.maxWidth - 80;
return Wrap(
alignment: WrapAlignment.center,
spacing: 20,
crossAxisAlignment: WrapCrossAlignment.end,
children: [
@ -132,6 +138,11 @@ class TestTab extends StatelessWidget {
Widget build(BuildContext context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Preizkus uspeha',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 15),
const Text(
'Kandidati za radioamaterja razreda A opravljajo izpit, ki je '
'sestavljen iz 60 različnih vprašanj. Vsako vprašanje ima 3 možne odgovore, od katerih je '
@ -170,7 +181,7 @@ class TestTab extends StatelessWidget {
revealInstantly: false,
));
},
child: const Text('Začni'),
child: const Text('Naprej'),
),
),
],

View File

@ -22,6 +22,14 @@ class QuizState extends Equatable {
return c;
}
List<int>? get incorrectAnswers {
final ret = <int>[];
for (int i = 0; i < answers!.length; ++i) {
if (answers![i] != questions[i].correct) ret.add(i);
}
return ret;
}
QuizState({
required this.title,
required this.questions,

View File

@ -6,10 +6,12 @@ import 'cubit/quiz_cubit.dart';
class QuestionCard extends StatelessWidget {
final int qIndex;
final bool forResultScreen;
const QuestionCard({
Key? key,
required this.qIndex,
this.forResultScreen = false,
}) : super(key: key);
@override
@ -22,81 +24,92 @@ class QuestionCard extends StatelessWidget {
final theme = Theme.of(context);
final question = state.questions[qIndex];
return SizedCard(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${question.id}'.padLeft(3, '0'),
style: theme.textTheme.bodySmall,
),
Text(
'${qIndex + 1}. ${question.question}',
style: theme.textTheme.titleLarge,
),
const SizedBox(height: 10),
if (question.image != null)
Center(
child: Container(
constraints: const BoxConstraints(
maxHeight: 500,
maxWidth: 500,
),
padding: const EdgeInsets.only(bottom: 10),
child: question.answers != null
? Image.asset('images/${question.image}')
: HiddenImage(
img: question.image!,
isHidden: state.revealed![qIndex] == null,
onClick: () =>
context.read<QuizCubit>().answer(qIndex, 1),
),
final child = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${question.id}'.padLeft(3, '0'),
style: theme.textTheme.bodySmall,
),
Text(
'${qIndex + 1}. ${question.question}',
style: theme.textTheme.titleLarge,
),
const SizedBox(height: 10),
if (question.image != null)
Center(
child: Container(
constraints: const BoxConstraints(
maxHeight: 500,
maxWidth: 500,
),
padding: const EdgeInsets.only(bottom: 10),
child: question.answers != null
? Image.asset('images/${question.image}')
: HiddenImage(
img: question.image!,
isHidden: state.revealed![qIndex] == null,
onClick: () =>
context.read<QuizCubit>().answer(qIndex, 1),
),
),
if (question.answers != null)
Material(
color: Colors.white,
clipBehavior: Clip.antiAlias,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.grey.shade300),
),
child: ListView.separated(
shrinkWrap: true,
itemCount: question.answers!.length,
itemBuilder: (_, aIndex) {
if (state.revealInstantly) {
final revealed = state.revealed![qIndex] ?? {};
return AnswerTile(
text: question.answers![aIndex],
isCorrect: aIndex == question.correct,
isRevealed: revealed.contains(aIndex),
isEnabled: !revealed.contains(question.correct),
onClick: () => context
.read<QuizCubit>()
.answer(qIndex, aIndex),
);
}
),
if (question.answers != null)
Material(
color: Colors.white,
clipBehavior: Clip.antiAlias,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.grey.shade300),
),
child: ListView.separated(
shrinkWrap: true,
itemCount: question.answers!.length,
itemBuilder: (_, aIndex) {
if (state.revealInstantly) {
final revealed = state.revealed![qIndex] ?? {};
return AnswerTile(
text: question.answers![aIndex],
isCorrect: aIndex == question.correct,
isRevealed: false,
isEnabled: true,
isSelected: state.answers![qIndex] == aIndex,
isRevealed: revealed.contains(aIndex),
isEnabled: !revealed.contains(question.correct),
onClick: () =>
context.read<QuizCubit>().answer(qIndex, aIndex),
);
},
separatorBuilder: (_, __) => const Divider(height: 0),
),
)
],
),
}
if (forResultScreen) {
return AnswerTile(
text: question.answers![aIndex],
isCorrect: aIndex == question.correct,
isRevealed: true,
isEnabled: false,
isSelected: state.answers![qIndex] == aIndex,
);
}
return AnswerTile(
text: question.answers![aIndex],
isCorrect: aIndex == question.correct,
isRevealed: false,
isEnabled: true,
isSelected: state.answers![qIndex] == aIndex,
onClick: () =>
context.read<QuizCubit>().answer(qIndex, aIndex),
);
},
separatorBuilder: (_, __) => const Divider(height: 0),
),
)
],
);
if (forResultScreen) return child;
return SizedCard(child: child);
},
);
}
@ -190,22 +203,29 @@ class AnswerTile extends StatelessWidget {
}) : super(key: key);
@override
Widget build(BuildContext context) => ListTile(
leading: _icon(),
onTap: !isRevealed && isEnabled ? onClick : null,
title: Text(text),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
iconColor: isRevealed
? (isCorrect ? Colors.green.shade900 : Colors.red.shade900)
: null,
tileColor: isRevealed
? (isCorrect ? Colors.green.shade50 : Colors.red.shade50)
: null,
textColor: isRevealed
? (isCorrect ? Colors.green.shade900 : Colors.red.shade900)
: null,
selected: isSelected == true,
);
Widget build(BuildContext context) {
final tileColor = isRevealed
? (isCorrect ? Colors.green.shade50 : Colors.red.shade50)
: null;
final textColor = isRevealed
? (isCorrect ? Colors.green.shade900 : Colors.red.shade900)
: null;
return ListTile(
leading: _icon(),
onTap: !isRevealed && isEnabled ? onClick : null,
title: Text(text),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
iconColor: isRevealed
? (isCorrect ? Colors.green.shade900 : Colors.red.shade900)
: null,
tileColor: tileColor,
textColor: textColor,
selectedTileColor: tileColor,
selectedColor: textColor,
selected: isSelected == true,
);
}
Icon? _icon() {
if (isSelected == true) return const Icon(Icons.radio_button_checked);

View File

@ -150,17 +150,82 @@ class _TimerCountdown extends StatelessWidget {
class _StartPage extends StatelessWidget {
@override
Widget build(BuildContext context) => SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: SizedCard(
child: Center(
child: ElevatedButton(
child: const Text('Začni preizkus'),
onPressed: () => context.read<QuizCubit>().start(),
Widget build(BuildContext context) {
final tTheme = Theme.of(context).textTheme;
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: SizedCard(
child: Column(
children: [
Text(
'Preizkus uspeha',
style: tTheme.headlineSmall,
),
),
const SizedBox(height: 15),
LayoutBuilder(
builder: (context, constraints) {
final halfW =
BoxConstraints(minWidth: constraints.maxWidth / 2);
return BlocBuilder<QuizCubit, QuizState>(
builder: (context, state) {
return Wrap(
alignment: WrapAlignment.center,
children: [
ConstrainedBox(
constraints: halfW,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${state.count}',
style: tTheme.headlineMedium,
),
Text(
'vprašanj',
style: tTheme.titleMedium,
),
],
),
),
ConstrainedBox(
constraints: halfW,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${state.duration!.inMinutes}',
style: tTheme.headlineMedium,
),
Text(
'minut',
style: tTheme.titleMedium,
),
],
),
),
],
);
},
);
},
),
const SizedBox(height: 20),
const Text('Ob pritisku na spodnji gumb, se preizkus začne. '
'Ob izteku časa, se samodejno zaključi.'),
const SizedBox(height: 10),
Center(
child: ElevatedButton(
child: const Text('Začni preizkus'),
onPressed: () => context.read<QuizCubit>().start(),
),
),
],
),
);
),
);
}
}
class _ResultPage extends StatelessWidget {
@ -173,9 +238,9 @@ class _ResultPage extends StatelessWidget {
children: [
Text(
'Rezultat',
style: Theme.of(context).textTheme.titleLarge,
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 15),
const SizedBox(height: 10),
BlocBuilder<QuizCubit, QuizState>(
builder: (context, state) {
final correct = state.score!;
@ -199,6 +264,44 @@ class _ResultPage extends StatelessWidget {
onPressed: () => Navigator.pop(context),
),
),
BlocBuilder<QuizCubit, QuizState>(
builder: (context, state) {
final wrongs = state.incorrectAnswers!;
if (wrongs.isEmpty) {
return const SizedBox.shrink();
}
return ListView.separated(
padding: const EdgeInsets.only(top: 15),
shrinkWrap: true,
itemCount: wrongs.length + 2,
separatorBuilder: (_, __) => const SizedBox(height: 20),
itemBuilder: (context, index) {
if (index == 0) {
return Divider(
color: Colors.grey.shade200,
thickness: 2,
);
}
if (index == 1) {
return Center(
child: Text(
'Napačni odgovori',
style: Theme.of(context).textTheme.headlineSmall,
),
);
}
return QuestionCard(
qIndex: wrongs[index - 2],
forResultScreen: true,
);
},
);
},
)
],
),
),