MVP Apresantação Final

This commit is contained in:
Carlos Correia
2026-07-14 20:59:16 +01:00
parent 5b25d9bef4
commit afce79b9b9
25 changed files with 503 additions and 96 deletions

View File

@@ -140,6 +140,10 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
bool _numberDontKnow = false;
bool _navigating = false;
/// Nas perguntas com imagem, ao avançar mostra-se por 2 segundos qual era
/// a imagem certa/errada antes de navegar — fins educativos.
bool _revealed = false;
/// O título ainda chega como texto livre ("Quiz 3/25") em vez de números
/// separados — extraímos daqui em vez de acrescentar mais dois parâmetros
/// obrigatórios a cada uma das 25 telas de pergunta.
@@ -180,6 +184,12 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
(_numberValue != null &&
_numberValue! >= 0 &&
_numberValue! <= _maxTeethCount));
} else if (widget.answerType == QuizAnswerType.yesNo &&
_selected != null &&
widget.answers[_selected!].value == 'nao_sei') {
// "Não sei" não é uma resposta válida para avançar — obriga a
// criança/responsável a decidir Sim ou Não antes de continuar.
canProceed = false;
}
final bool hasSuggestedVideo =
@@ -485,12 +495,15 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
3
? 2.3
: 1.5),
onTap: () =>
setState(
() =>
_selected =
i,
),
reveal: _revealed,
onTap: _revealed
? null
: () =>
setState(
() =>
_selected =
i,
),
),
),
],
@@ -582,6 +595,25 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
() => _navigating =
true,
);
if (widget.answerType ==
QuizAnswerType
.image) {
setState(
() => _revealed =
true,
);
await Future.delayed(
const Duration(
seconds: 2,
),
);
if (!context
.mounted) {
return;
}
}
QuizScore nextScore =
widget.currentScore;
if (widget.answerType !=
@@ -838,16 +870,24 @@ class _QuizAnswerTile extends StatelessWidget {
required this.selected,
required this.onTap,
this.imageAspectRatio = 4 / 3,
this.reveal = false,
});
final QuizAnswer answer;
final bool selected;
final VoidCallback onTap;
final VoidCallback? onTap;
final double imageAspectRatio;
/// Quando true, mostra um selo indicando se esta era a resposta certa ou
/// errada — ver [_QuizQuestionScreenState._revealed].
final bool reveal;
@override
Widget build(BuildContext context) {
final borderColor = selected
final bool isCorrect = answer.weight == 1;
final borderColor = reveal
? (isCorrect ? const Color(0xFF2F9E94) : const Color(0xFFFF55A7))
: selected
? const Color(0xFF2F9E94)
: Colors.black.withValues(alpha: 0.12);
final bg = selected
@@ -868,7 +908,7 @@ class _QuizAnswerTile extends StatelessWidget {
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: borderColor,
width: selected ? 1.4 : 1.0,
width: (reveal || selected) ? 1.6 : 1.0,
),
boxShadow: [
BoxShadow(
@@ -965,6 +1005,56 @@ class _QuizAnswerTile extends StatelessWidget {
),
),
),
if (reveal)
Positioned(
left: 8,
right: 8,
bottom: 8,
child: IgnorePointer(
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
decoration: BoxDecoration(
color: isCorrect
? const Color(0xFF2F9E94)
: const Color(0xFFFF55A7),
borderRadius: BorderRadius.circular(999),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.18),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isCorrect
? Icons.check_rounded
: Icons.close_rounded,
size: 14,
color: Colors.white,
),
const SizedBox(width: 4),
Text(
isCorrect ? 'Resposta certa' : 'Resposta inadequada',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 11.5,
),
),
],
),
),
),
),
),
],
),
);