Clinicas Parceiras e curiosidades retiradas.
This commit is contained in:
@@ -33,10 +33,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0xFFFFE6F1),
|
||||
Color(0xFFFFC9DF),
|
||||
],
|
||||
colors: [Color(0xFFFFE6F1), Color(0xFFFFC9DF)],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -64,22 +61,67 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(
|
||||
0xFF2F9E94,
|
||||
).withValues(alpha: 0.18),
|
||||
blurRadius: 24,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.medical_services_rounded,
|
||||
size: 38,
|
||||
color: Color(0xFF2F9E94),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
const Text(
|
||||
'Check-Teeth Kids',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Color(0xFFFF55A7),
|
||||
height: 1.0,
|
||||
letterSpacing: -0.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Cuidar do sorriso começa aqui.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: const Color(0xFF2F9E94).withValues(alpha: 0.9),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'Acompanhe a saúde oral do seu filho com\ninformação segura e prevenção inteligente.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
height: 1.35,
|
||||
color: Colors.black.withValues(alpha: 0.52),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
SizedBox(
|
||||
width: size.width * 0.78,
|
||||
child: _PrimaryButton(
|
||||
@@ -87,39 +129,14 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
onPressed: _openRegister,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
const SizedBox(height: 12),
|
||||
SizedBox(
|
||||
width: size.width * 0.78,
|
||||
child: _PrimaryButton(
|
||||
child: _SecondaryButton(
|
||||
label: 'Entrar',
|
||||
onPressed: _openLogin,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
height: 1.25,
|
||||
color: Colors.black.withValues(alpha: 0.55),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
children: const [
|
||||
TextSpan(
|
||||
text: 'Cuidar do sorriso começa aqui.\n',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF2F9E94),
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text:
|
||||
'Acompanhe a saúde oral do seu filho com\ninformação segura e prevenção inteligente.',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -150,6 +167,32 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
class _SecondaryButton extends StatelessWidget {
|
||||
const _SecondaryButton({required this.label, required this.onPressed});
|
||||
|
||||
final String label;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const Color teal = Color(0xFF2F9E94);
|
||||
return SizedBox(
|
||||
height: 44,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: teal,
|
||||
side: const BorderSide(color: teal, width: 1.6),
|
||||
shape: const StadiumBorder(),
|
||||
backgroundColor: Colors.white.withValues(alpha: 0.5),
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w800, fontSize: 15),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Text(label),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PrimaryButton extends StatelessWidget {
|
||||
const _PrimaryButton({required this.label, required this.onPressed});
|
||||
|
||||
@@ -162,25 +205,28 @@ class _PrimaryButton extends StatelessWidget {
|
||||
return SizedBox(
|
||||
height: 44,
|
||||
child: FilledButton(
|
||||
style: FilledButton.styleFrom(
|
||||
style:
|
||||
FilledButton.styleFrom(
|
||||
backgroundColor: teal,
|
||||
foregroundColor: Colors.white,
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w800, fontSize: 15),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 15,
|
||||
),
|
||||
).copyWith(
|
||||
animationDuration: const Duration(milliseconds: 180),
|
||||
splashFactory: InkSparkle.splashFactory,
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
(states) {
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>((states) {
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return Colors.white.withValues(alpha: 0.14);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered) || states.contains(WidgetState.focused)) {
|
||||
if (states.contains(WidgetState.hovered) ||
|
||||
states.contains(WidgetState.focused)) {
|
||||
return Colors.white.withValues(alpha: 0.08);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
}),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Text(label),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,25 @@ import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
|
||||
typedef QuizNextBuilder = Route<void> Function(BuildContext context, int nextScore);
|
||||
typedef QuizNextBuilder =
|
||||
Route<void> Function(BuildContext context, int nextScore);
|
||||
|
||||
enum QuizAnswerType { text, image, number, yesNo }
|
||||
|
||||
class QuizAnswer {
|
||||
const QuizAnswer({required this.title, required this.description, required this.weight});
|
||||
const QuizAnswer({
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.weight,
|
||||
this.imagePath,
|
||||
this.value,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String description;
|
||||
final int weight;
|
||||
final String? imagePath;
|
||||
final String? value;
|
||||
}
|
||||
|
||||
class QuizQuestionScreen extends StatefulWidget {
|
||||
@@ -24,6 +35,7 @@ class QuizQuestionScreen extends StatefulWidget {
|
||||
this.onFinished,
|
||||
this.isFinal = false,
|
||||
this.showBackButton = false,
|
||||
this.answerType = QuizAnswerType.text,
|
||||
});
|
||||
|
||||
final String title;
|
||||
@@ -34,6 +46,7 @@ class QuizQuestionScreen extends StatefulWidget {
|
||||
final VoidCallback? onFinished;
|
||||
final bool isFinal;
|
||||
final bool showBackButton;
|
||||
final QuizAnswerType answerType;
|
||||
|
||||
@override
|
||||
State<QuizQuestionScreen> createState() => _QuizQuestionScreenState();
|
||||
@@ -41,11 +54,30 @@ class QuizQuestionScreen extends StatefulWidget {
|
||||
|
||||
class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
int? _selected;
|
||||
TextEditingController? _numberController;
|
||||
int? _numberValue;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.answerType == QuizAnswerType.number) {
|
||||
_numberController = TextEditingController();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_numberController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
final bool canProceed = _selected != null;
|
||||
bool canProceed = _selected != null;
|
||||
if (widget.answerType == QuizAnswerType.number) {
|
||||
canProceed = _numberValue != null && _numberValue! >= 0;
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
@@ -57,10 +89,7 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0xFFFFE6F1),
|
||||
Color(0xFFFFC9DF),
|
||||
],
|
||||
colors: [Color(0xFFFFE6F1), Color(0xFFFFC9DF)],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -119,7 +148,11 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Escolha apenas uma opção',
|
||||
widget.answerType == QuizAnswerType.number
|
||||
? 'Insira o número'
|
||||
: widget.answerType == QuizAnswerType.yesNo
|
||||
? 'Escolha uma opção'
|
||||
: 'Escolha apenas uma opção',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.black.withValues(alpha: 0.55),
|
||||
@@ -132,10 +165,13 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: ListView.separated(
|
||||
child: widget.answerType == QuizAnswerType.number
|
||||
? _buildNumberInput()
|
||||
: ListView.separated(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
itemCount: widget.answers.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 12),
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(height: 12),
|
||||
itemBuilder: (context, i) {
|
||||
return _QuizAnswerTile(
|
||||
answer: widget.answers[i],
|
||||
@@ -154,21 +190,38 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
width: size.width * 0.62,
|
||||
height: 46,
|
||||
child: FilledButton(
|
||||
style: FilledButton.styleFrom(
|
||||
style:
|
||||
FilledButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2F9E94),
|
||||
foregroundColor: Colors.white,
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w900),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
).copyWith(
|
||||
animationDuration: const Duration(milliseconds: 180),
|
||||
animationDuration: const Duration(
|
||||
milliseconds: 180,
|
||||
),
|
||||
splashFactory: InkSparkle.splashFactory,
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
overlayColor:
|
||||
WidgetStateProperty.resolveWith<Color?>(
|
||||
(states) {
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return Colors.white.withValues(alpha: 0.14);
|
||||
if (states.contains(
|
||||
WidgetState.pressed,
|
||||
)) {
|
||||
return Colors.white.withValues(
|
||||
alpha: 0.14,
|
||||
);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered) || states.contains(WidgetState.focused)) {
|
||||
return Colors.white.withValues(alpha: 0.08);
|
||||
if (states.contains(
|
||||
WidgetState.hovered,
|
||||
) ||
|
||||
states.contains(
|
||||
WidgetState.focused,
|
||||
)) {
|
||||
return Colors.white.withValues(
|
||||
alpha: 0.08,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -177,18 +230,37 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
onPressed: !canProceed
|
||||
? null
|
||||
: () {
|
||||
final picked = widget.answers[_selected!];
|
||||
final nextScore = widget.currentScore + picked.weight;
|
||||
int nextScore = widget.currentScore;
|
||||
if (widget.answerType ==
|
||||
QuizAnswerType.number) {
|
||||
nextScore =
|
||||
widget.currentScore +
|
||||
(_numberValue ?? 0);
|
||||
} else {
|
||||
final picked =
|
||||
widget.answers[_selected!];
|
||||
nextScore =
|
||||
widget.currentScore + picked.weight;
|
||||
}
|
||||
|
||||
if (widget.isFinal) {
|
||||
widget.onFinished?.call();
|
||||
Navigator.of(context).popUntil((r) => r.isFirst);
|
||||
final finishedRoute = widget.nextRoute(
|
||||
context,
|
||||
nextScore,
|
||||
);
|
||||
Navigator.of(
|
||||
context,
|
||||
).pushReplacement(finishedRoute);
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(context).push(widget.nextRoute(context, nextScore));
|
||||
Navigator.of(context).push(
|
||||
widget.nextRoute(context, nextScore),
|
||||
);
|
||||
},
|
||||
child: Text(widget.isFinal ? 'Concluir' : 'Avançar'),
|
||||
child: Text(
|
||||
widget.isFinal ? 'Concluir' : 'Avançar',
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.showBackButton) ...[
|
||||
@@ -197,27 +269,45 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
width: size.width * 0.62,
|
||||
height: 42,
|
||||
child: FilledButton(
|
||||
style: FilledButton.styleFrom(
|
||||
style:
|
||||
FilledButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2F9E94),
|
||||
foregroundColor: Colors.white,
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w900),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
).copyWith(
|
||||
animationDuration: const Duration(milliseconds: 180),
|
||||
animationDuration: const Duration(
|
||||
milliseconds: 180,
|
||||
),
|
||||
splashFactory: InkSparkle.splashFactory,
|
||||
overlayColor: WidgetStateProperty.resolveWith<Color?>(
|
||||
(states) {
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
return Colors.white.withValues(alpha: 0.14);
|
||||
overlayColor:
|
||||
WidgetStateProperty.resolveWith<
|
||||
Color?
|
||||
>((states) {
|
||||
if (states.contains(
|
||||
WidgetState.pressed,
|
||||
)) {
|
||||
return Colors.white.withValues(
|
||||
alpha: 0.14,
|
||||
);
|
||||
}
|
||||
if (states.contains(WidgetState.hovered) || states.contains(WidgetState.focused)) {
|
||||
return Colors.white.withValues(alpha: 0.08);
|
||||
if (states.contains(
|
||||
WidgetState.hovered,
|
||||
) ||
|
||||
states.contains(
|
||||
WidgetState.focused,
|
||||
)) {
|
||||
return Colors.white.withValues(
|
||||
alpha: 0.08,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).maybePop(),
|
||||
onPressed: () =>
|
||||
Navigator.of(context).maybePop(),
|
||||
child: const Text('Voltar'),
|
||||
),
|
||||
),
|
||||
@@ -234,10 +324,67 @@ class _QuizQuestionScreenState extends State<QuizQuestionScreen> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNumberInput() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.70),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: Colors.black.withValues(alpha: 0.12),
|
||||
width: 1.0,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.06),
|
||||
blurRadius: 18,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: TextField(
|
||||
controller: _numberController,
|
||||
keyboardType: TextInputType.number,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Color(0xFF2F9E94),
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: '0',
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.grey,
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 20),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_numberValue = int.tryParse(value);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _QuizAnswerTile extends StatelessWidget {
|
||||
const _QuizAnswerTile({required this.answer, required this.selected, required this.onTap});
|
||||
const _QuizAnswerTile({
|
||||
required this.answer,
|
||||
required this.selected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final QuizAnswer answer;
|
||||
final bool selected;
|
||||
@@ -245,8 +392,12 @@ class _QuizAnswerTile extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final borderColor = selected ? const Color(0xFF2F9E94) : Colors.black.withValues(alpha: 0.12);
|
||||
final bg = selected ? Colors.white.withValues(alpha: 0.88) : Colors.white.withValues(alpha: 0.70);
|
||||
final borderColor = selected
|
||||
? const Color(0xFF2F9E94)
|
||||
: Colors.black.withValues(alpha: 0.12);
|
||||
final bg = selected
|
||||
? Colors.white.withValues(alpha: 0.88)
|
||||
: Colors.white.withValues(alpha: 0.70);
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 220),
|
||||
@@ -310,7 +461,9 @@ class _QuizAnswerTile extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
crossFadeState: selected ? CrossFadeState.showSecond : CrossFadeState.showFirst,
|
||||
crossFadeState: selected
|
||||
? CrossFadeState.showSecond
|
||||
: CrossFadeState.showFirst,
|
||||
duration: const Duration(milliseconds: 220),
|
||||
firstCurve: Curves.easeIn,
|
||||
secondCurve: Curves.easeOut,
|
||||
|
||||
@@ -19,331 +19,580 @@ class _QuizRandomScreenState extends State<QuizRandomScreen> {
|
||||
final List<QuizQuestion> _allQuestions = [
|
||||
QuizQuestion(
|
||||
id: 1,
|
||||
title: 'Quiz 1/15',
|
||||
question: 'Qual é o tempo ideal para escovar os dentes?',
|
||||
title: 'Quiz 1/26',
|
||||
question:
|
||||
'Qual das seguintes imagens se assemelha à face do seu filho/a?',
|
||||
answerType: QuizAnswerType.image,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Cerca de 2 minutos',
|
||||
description: 'O recomendado é escovar por aproximadamente 2 minutos, cobrindo todas as superfícies dos dentes e a linha da gengiva sem pressa.',
|
||||
title: 'Opção A',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à face do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/face_a.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só 30 segundos, se fizer rápido',
|
||||
description: 'Muito pouco tempo costuma deixar placa bacteriana para trás, principalmente nos dentes de trás e perto da gengiva.',
|
||||
weight: 5,
|
||||
title: 'Opção B',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à face do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/face_b.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: '5 minutos com força para "limpar bem"',
|
||||
description: 'Tempo demais e força excessiva podem irritar a gengiva e desgastar o esmalte. Prefira movimentos suaves e tempo adequado.',
|
||||
weight: 3,
|
||||
title: 'Opção C',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à face do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/face_c.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Opção D',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à face do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/face_d.png',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 2,
|
||||
title: 'Quiz 2/15',
|
||||
question: 'Quando devo trocar a escova de dentes?',
|
||||
title: 'Quiz 2/26',
|
||||
question:
|
||||
'Qual das seguintes imagens se assemelha à boca do seu filho/a?',
|
||||
answerType: QuizAnswerType.image,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'A cada 3 meses (ou antes se estragar)',
|
||||
description: 'O ideal é trocar a cada ~3 meses. Se as cerdas abrirem antes, troque antes. Cerdas abertas limpam pior.',
|
||||
title: 'Opção A',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth_a.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só quando a escova "quebrar"',
|
||||
description: 'Esperar demais reduz a eficiência da escovação e pode acumular microrganismos na escova.',
|
||||
weight: 5,
|
||||
title: 'Opção B',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth_b.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Todo mês, obrigatoriamente',
|
||||
description: 'Não é regra fixa. Um mês pode ser cedo demais se a escova estiver em bom estado. O principal é o estado das cerdas.',
|
||||
weight: 3,
|
||||
title: 'Opção C',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth_c.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Opção D',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth_d.png',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 3,
|
||||
title: 'Quiz 3/15',
|
||||
question: 'Qual a quantidade ideal de pasta de dente para crianças?',
|
||||
title: 'Quiz 3/26',
|
||||
question:
|
||||
'Qual das seguintes imagens se assemelha às olheiras do seu filho/a?',
|
||||
answerType: QuizAnswerType.image,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Um grão de arroz (pequenos) / ervilha (maiores)',
|
||||
description: 'Para crianças pequenas, um "grão de arroz" já basta. Conforme cresce, pode ser do tamanho de uma ervilha. Isso ajuda a evitar excesso de flúor ingerido.',
|
||||
title: 'Opção A',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha às olheiras do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/dark_circles_a.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Cobrir toda a escova com pasta',
|
||||
description: 'Muito produto não significa melhor limpeza. Em crianças, aumenta o risco de engolir pasta em excesso.',
|
||||
weight: 5,
|
||||
title: 'Opção B',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha às olheiras do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/dark_circles_b.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Nenhuma pasta, só água',
|
||||
description: 'A pasta com flúor (na quantidade correta) ajuda a prevenir cáries. Em geral, água sozinha não oferece a mesma proteção.',
|
||||
weight: 3,
|
||||
title: 'Opção C',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha às olheiras do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/dark_circles_c.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Opção D',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha às olheiras do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/dark_circles_d.png',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 4,
|
||||
title: 'Quiz 4/15',
|
||||
question: 'Qual é o melhor horário para usar fio dental?',
|
||||
title: 'Quiz 4/26',
|
||||
question:
|
||||
'Qual das seguintes imagens se assemelha ao queixo do seu filho/a com a boca fechada?',
|
||||
answerType: QuizAnswerType.image,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Uma vez ao dia, com calma (geralmente à noite)',
|
||||
description: 'O importante é a frequência diária. À noite costuma ser mais fácil, pois remove restos e placa antes de dormir.',
|
||||
title: 'Opção A',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao queixo do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/chin_a.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só quando algo fica preso',
|
||||
description: 'O fio dental não serve apenas para tirar restos visíveis; ele remove placa bacteriana entre os dentes onde a escova não alcança.',
|
||||
weight: 5,
|
||||
title: 'Opção B',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao queixo do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/chin_b.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Depois de toda refeição (obrigatório)',
|
||||
description: 'Pode ser útil em alguns casos, mas não é obrigatório para todos. O essencial é fazer bem feito ao menos 1x ao dia.',
|
||||
weight: 3,
|
||||
title: 'Opção C',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao queixo do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/chin_c.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Opção D',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao queixo do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/chin_d.png',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 5,
|
||||
title: 'Quiz 5/15',
|
||||
question: 'O que ajuda mais a prevenir cáries no dia a dia?',
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Escovar + flúor + reduzir açúcar frequente',
|
||||
description: 'A prevenção é um conjunto: boa higiene com flúor e menos "beliscos" açucarados ao longo do dia.',
|
||||
weight: 2,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só enxaguante bucal',
|
||||
description: 'Enxaguante pode ajudar em alguns casos, mas não substitui escovação e fio dental.',
|
||||
weight: 3,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Evitar completamente dentista',
|
||||
description: 'Consultas regulares são importantes para prevenção e orientação. O dentista também identifica problemas bem no começo.',
|
||||
weight: 5,
|
||||
),
|
||||
],
|
||||
title: 'Quiz 5/26',
|
||||
question: 'Quantos dentes tem o seu filho/a em cima na boca?',
|
||||
answerType: QuizAnswerType.number,
|
||||
answers: const [],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 6,
|
||||
title: 'Quiz 6/15',
|
||||
question: 'Qual tipo de escova é mais recomendada para crianças?',
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Escova macia com cabeça pequena',
|
||||
description: 'Escovas macias protegem a gengiva sensível das crianças e a cabeça pequena alcança melhor todos os dentes.',
|
||||
weight: 2,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Escova dura para limpar melhor',
|
||||
description: 'Escovas duras podem machucar a gengiva e desgastar o esmalte dos dentes das crianças.',
|
||||
weight: 5,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Escova elétrica sempre é melhor',
|
||||
description: 'Escova elétrica pode ajudar, mas não é essencial. O mais importante é a técnica e frequência.',
|
||||
weight: 3,
|
||||
),
|
||||
],
|
||||
title: 'Quiz 6/26',
|
||||
question: 'Quantos dentes tem o seu filho/a em baixo na boca?',
|
||||
answerType: QuizAnswerType.number,
|
||||
answers: const [],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 7,
|
||||
title: 'Quiz 7/15',
|
||||
question: 'Qual alimento é mais prejudicial para os dentes?',
|
||||
title: 'Quiz 7/26',
|
||||
question:
|
||||
'Qual das seguintes imagens se assemelha à boca do seu filho/a?',
|
||||
answerType: QuizAnswerType.image,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Balas e chicletes pegajosos',
|
||||
description: 'Alimentos pegajosos ficam presos nos dentes por mais tempo, aumentando o risco de cáries.',
|
||||
title: 'Opção A',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth2_a.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Maçã e cenoura',
|
||||
description: 'Frutas e vegetais crus ajudam a limpar os dentes naturalmente e são saudáveis.',
|
||||
weight: 5,
|
||||
title: 'Opção B',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth2_b.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Água e leite',
|
||||
description: 'Água ajuda a limpar e leite tem cálcio. São opções saudáveis para os dentes.',
|
||||
weight: 3,
|
||||
title: 'Opção C',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth2_c.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Opção D',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha à boca do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/mouth2_d.png',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 8,
|
||||
title: 'Quiz 8/15',
|
||||
question: 'Quando deve ser a primeira visita ao dentista?',
|
||||
title: 'Quiz 8/26',
|
||||
question:
|
||||
'Qual das seguintes imagens se assemelha ao freio do seu filho/a?',
|
||||
answerType: QuizAnswerType.image,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Por volta dos 1 ano de idade',
|
||||
description: 'A primeira visita deve ser assim que o primeiro dentinho nascer ou até o primeiro aniversário.',
|
||||
title: 'Opção A',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao freio do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/frenulum_a.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só quando tiver todos os dentes',
|
||||
description: 'Esperar demais pode permitir que problemas comecem sem detecção precoce.',
|
||||
weight: 5,
|
||||
title: 'Opção B',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao freio do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/frenulum_b.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Apenas se sentir dor',
|
||||
description: 'Dor geralmente indica que o problema já está avançado. Prevenção é melhor que tratamento.',
|
||||
weight: 5,
|
||||
title: 'Opção C',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao freio do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/frenulum_c.png',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Opção D',
|
||||
description:
|
||||
'Selecione se a imagem se assemelha ao freio do seu filho/a',
|
||||
weight: 2,
|
||||
imagePath: 'assets/images/frenulum_d.png',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 9,
|
||||
title: 'Quiz 9/15',
|
||||
question: 'Até que idade é aceitável usar chupeta?',
|
||||
title: 'Quiz 9/26',
|
||||
question: 'O seu filho/a tem problemas respiratórios diagnosticados?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Até 2-3 anos no máximo',
|
||||
description: 'Após 2-3 anos, chupeta pode causar problemas na dentição e no desenvolvimento da fala.',
|
||||
title: 'Sim',
|
||||
description: 'Problemas respiratórios diagnosticados',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Até 6-7 anos',
|
||||
description: 'Essa idade já é muito tarde e pode causar problemas sérios na arcada dentária.',
|
||||
weight: 5,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não tem problema usar sempre',
|
||||
description: 'Uso prolongado pode causar má oclusão, problemas na fala e alterações faciais.',
|
||||
weight: 5,
|
||||
title: 'Não',
|
||||
description: 'Sem problemas respiratórios diagnosticados',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 10,
|
||||
title: 'Quiz 10/15',
|
||||
question: 'O flúor na água de abastecimento ajuda?',
|
||||
title: 'Quiz 10/26',
|
||||
question: 'O seu filho/a respira habitualmente pela boca?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim, reduz cáries em até 60%',
|
||||
description: 'Flúor na água é uma das medidas de saúde pública mais eficazes na prevenção de cáries.',
|
||||
title: 'Sim',
|
||||
description: 'Respira habitualmente pela boca',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não faz diferença nenhuma',
|
||||
description: 'Estudos comprovam que flúor na água reduz significativamente a incidência de cáries.',
|
||||
weight: 5,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'É perigoso e causa problemas',
|
||||
description: 'Nas concentrações corretas, flúor é seguro. O problema é o excesso, não o uso adequado.',
|
||||
weight: 4,
|
||||
title: 'Não',
|
||||
description: 'Não respira habitualmente pela boca',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 11,
|
||||
title: 'Quiz 11/15',
|
||||
question: 'Por que a escovação noturna é tão importante?',
|
||||
title: 'Quiz 11/26',
|
||||
question: 'O seu filho/a ressona habitualmente durante a noite?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Menos saliva durante o sono',
|
||||
description: 'Durante a noite produzimos menos saliva, que protege os dentes. Escovação remove placa antes desse período vulnerável.',
|
||||
title: 'Sim',
|
||||
description: 'Ressonar habitualmente durante a noite',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'É igual aos outros horários',
|
||||
description: 'A noite é especial porque a produção de saliva diminui, aumentando o risco de cáries.',
|
||||
weight: 4,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só por tradição',
|
||||
description: 'Tem fundamento científico. A noite é o período mais crítico para formação de cáries.',
|
||||
weight: 5,
|
||||
title: 'Não',
|
||||
description: 'Não ressona habitualmente',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 12,
|
||||
title: 'Quiz 12/15',
|
||||
question: 'Qual bebida é mais ácida para os dentes?',
|
||||
title: 'Quiz 12/26',
|
||||
question: 'O seu filho/a sente habitualmente o nariz "tapado"?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Refrigerantes e sucos industrializados',
|
||||
description: 'Refrigerantes e sucos artificiais têm pH muito baixo, corroem o esmalte e causam erosão dental.',
|
||||
title: 'Sim',
|
||||
description: 'Sente habitualmente o nariz tapado',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Água e leite',
|
||||
description: 'Água tem pH neutro e leite é levemente ácido mas protege os dentes com cálcio.',
|
||||
weight: 5,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Chá sem açúcar',
|
||||
description: 'Chá pode manchar mas é muito menos ácido que refrigerantes e sucos artificiais.',
|
||||
weight: 3,
|
||||
title: 'Não',
|
||||
description: 'Não sente habitualmente o nariz tapado',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 13,
|
||||
title: 'Quiz 13/15',
|
||||
question: 'É importante cuidar dos dentes de leite?',
|
||||
title: 'Quiz 13/26',
|
||||
question:
|
||||
'Durante o sono, o seu filho/a tem habitualmente interrupções da respiração?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim, são fundamentais para o desenvolvimento',
|
||||
description: 'Dentes de leite mantêm espaço para os permanentes, auxiliam na fala e mastigação.',
|
||||
title: 'Sim',
|
||||
description:
|
||||
'Tem habitualmente interrupções da respiração durante o sono',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não, vão cair de qualquer jeito',
|
||||
description: 'Dentes de leite doentes podem afetar os permanentes e causar problemas no desenvolvimento.',
|
||||
weight: 5,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só se doerem',
|
||||
description: 'Mesmo sem dor, problemas nos dentes de leite podem ter consequências sérias futuras.',
|
||||
weight: 4,
|
||||
title: 'Não',
|
||||
description: 'Não tem interrupções da respiração durante o sono',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 14,
|
||||
title: 'Quiz 14/15',
|
||||
question: 'Qual é a técnica correta de escovação?',
|
||||
title: 'Quiz 14/26',
|
||||
question: 'O seu filho/a range os dentes com frequência?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Movimentos circulares suaves',
|
||||
description: 'Movimentos circulares ou vibratórios suaves limpam sem machucar a gengiva e removem a placa eficientemente.',
|
||||
title: 'Sim',
|
||||
description: 'Range os dentes com frequência',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Força de um lado para o outro',
|
||||
description: 'Movimentos horizontais fortes podem machucar a gengiva e causar recessão gengival.',
|
||||
weight: 5,
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Só na frente dos dentes',
|
||||
description: 'Precisa escovar todas as faces: frente, atrás e superfície de mastigação.',
|
||||
weight: 4,
|
||||
title: 'Não',
|
||||
description: 'Não range os dentes com frequência',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 15,
|
||||
title: 'Quiz 15/15',
|
||||
question: 'Para que servem os selantes dentários?',
|
||||
title: 'Quiz 15/26',
|
||||
question: 'O seu filho/a habitualmente tem alergias sazonais?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Proteger sulcos dos dentes contra cáries',
|
||||
description: 'Selantes são uma resina que preenche sulcos e fissuras dos dentes, protegendo contra cáries.',
|
||||
title: 'Sim',
|
||||
description: 'Habitualmente tem alergias sazonais',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Clarear os dentes',
|
||||
description: 'Selantes não têm função estética de clareamento, apenas protetiva contra cáries.',
|
||||
weight: 5,
|
||||
title: 'Não',
|
||||
description: 'Não tem alergias sazonais',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 16,
|
||||
title: 'Quiz 16/26',
|
||||
question: 'O seu filho/a acorda com saliva seca na cara ou na almofada?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Acorda com saliva seca na cara ou na almofada',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Substituir a escovação',
|
||||
description: 'Selantes complementam a higiene, não substituem a escovação e o fio dental.',
|
||||
weight: 4,
|
||||
title: 'Não',
|
||||
description: 'Não acorda com saliva seca',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 17,
|
||||
title: 'Quiz 17/26',
|
||||
question: 'O seu filho/a teve ou costuma ter com frequência otites?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Teve ou costuma ter com frequência otites',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não teve ou não costuma ter otites',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 18,
|
||||
title: 'Quiz 18/26',
|
||||
question: 'O seu filho/a teve ou costuma ter com frequência amigdalites?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Teve ou costuma ter com frequência amigdalites',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não teve ou não costuma ter amigdalites',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 19,
|
||||
title: 'Quiz 19/26',
|
||||
question:
|
||||
'O seu filho/a teve ou costuma ter com frequência bronquiolites?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Teve ou costuma ter com frequência bronquiolites',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não teve ou não costuma ter bronquiolites',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 20,
|
||||
title: 'Quiz 20/26',
|
||||
question: 'O seu filho/a apresenta dificuldades a mastigar?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Apresenta dificuldades a mastigar',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não apresenta dificuldades a mastigar',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 21,
|
||||
title: 'Quiz 21/26',
|
||||
question: 'O seu filho/a habitualmente é lento a comer?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Habitualmente é lento a comer',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não é lento a comer',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 22,
|
||||
title: 'Quiz 22/26',
|
||||
question: 'O seu filho/a habitualmente prefere comer alimentos moles?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Habitualmente prefere comer alimentos moles',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não prefere alimentos moles',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 23,
|
||||
title: 'Quiz 23/26',
|
||||
question: 'Em bebé apenas foi alimentado por biberão?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Em bebé apenas foi alimentado por biberão',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não foi apenas alimentado por biberão',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 24,
|
||||
title: 'Quiz 24/26',
|
||||
question: 'O seu filho/a usa ou usou chupeta com frequência?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Usa ou usou chupeta com frequência',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não usa ou não usou chupeta com frequência',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
QuizQuestion(
|
||||
id: 25,
|
||||
title: 'Quiz 25/26',
|
||||
question: 'O seu filho/a chucha ou já chuchou o dedo com frequência?',
|
||||
answerType: QuizAnswerType.yesNo,
|
||||
answers: const [
|
||||
QuizAnswer(
|
||||
title: 'Sim',
|
||||
description: 'Chucha ou já chuchou o dedo com frequência',
|
||||
weight: 2,
|
||||
value: 'sim',
|
||||
),
|
||||
QuizAnswer(
|
||||
title: 'Não',
|
||||
description: 'Não chucha ou não chuchou o dedo com frequência',
|
||||
weight: 1,
|
||||
value: 'nao',
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -388,7 +637,8 @@ class _QuizRandomScreenState extends State<QuizRandomScreen> {
|
||||
}
|
||||
|
||||
final currentQuestion = _shuffledQuestions[_currentQuestionIndex];
|
||||
final isLastQuestion = _currentQuestionIndex == _shuffledQuestions.length - 1;
|
||||
final isLastQuestion =
|
||||
_currentQuestionIndex == _shuffledQuestions.length - 1;
|
||||
|
||||
return QuizQuestionScreen(
|
||||
title: currentQuestion.title,
|
||||
@@ -397,13 +647,13 @@ class _QuizRandomScreenState extends State<QuizRandomScreen> {
|
||||
currentScore: _currentScore,
|
||||
nextRoute: (context, nextScore) {
|
||||
_nextQuestion(nextScore - _currentScore);
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => const SizedBox.shrink(),
|
||||
);
|
||||
return MaterialPageRoute<void>(builder: (_) => const SizedBox.shrink());
|
||||
},
|
||||
isFinal: isLastQuestion,
|
||||
showBackButton: _currentQuestionIndex > 0,
|
||||
onFinished: isLastQuestion ? () {
|
||||
answerType: currentQuestion.answerType,
|
||||
onFinished: isLastQuestion
|
||||
? () {
|
||||
Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => QuizResultScreen(
|
||||
@@ -413,7 +663,8 @@ class _QuizRandomScreenState extends State<QuizRandomScreen> {
|
||||
),
|
||||
),
|
||||
);
|
||||
} : null,
|
||||
}
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -423,11 +674,13 @@ class QuizQuestion {
|
||||
final String title;
|
||||
final String question;
|
||||
final List<QuizAnswer> answers;
|
||||
final QuizAnswerType answerType;
|
||||
|
||||
QuizQuestion({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.question,
|
||||
required this.answers,
|
||||
this.answerType = QuizAnswerType.text,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,7 +6,12 @@ import 'dart:async';
|
||||
import 'quiz_prefs.dart';
|
||||
|
||||
class QuizResultScreen extends StatefulWidget {
|
||||
const QuizResultScreen({super.key, required this.finalScore, required this.maxScore, this.scopeId});
|
||||
const QuizResultScreen({
|
||||
super.key,
|
||||
required this.finalScore,
|
||||
required this.maxScore,
|
||||
this.scopeId,
|
||||
});
|
||||
|
||||
final int finalScore;
|
||||
final int maxScore;
|
||||
@@ -17,29 +22,47 @@ class QuizResultScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
late final Future<void> _saveResultFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_saveResultFuture = _saveResult();
|
||||
}
|
||||
|
||||
Future<void> _saveResult() async {
|
||||
QuizPrefs.markQuizSeen();
|
||||
final scope = (widget.scopeId ?? '').trim();
|
||||
if (scope.isNotEmpty) {
|
||||
QuizPrefs.saveLastResultForScope(scopeId: scope, score: widget.finalScore, maxScore: widget.maxScore);
|
||||
await QuizPrefs.saveLastResultForScope(
|
||||
scopeId: scope,
|
||||
score: widget.finalScore,
|
||||
maxScore: widget.maxScore,
|
||||
);
|
||||
} else {
|
||||
final uid = FirebaseAuth.instance.currentUser?.uid;
|
||||
if (uid != null && uid.trim().isNotEmpty) {
|
||||
QuizPrefs.saveLastResultForUser(userId: uid, score: widget.finalScore, maxScore: widget.maxScore);
|
||||
await QuizPrefs.saveLastResultForUser(
|
||||
userId: uid,
|
||||
score: widget.finalScore,
|
||||
maxScore: widget.maxScore,
|
||||
);
|
||||
} else {
|
||||
QuizPrefs.saveLastResult(score: widget.finalScore, maxScore: widget.maxScore);
|
||||
await QuizPrefs.saveLastResult(
|
||||
score: widget.finalScore,
|
||||
maxScore: widget.maxScore,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final uid = FirebaseAuth.instance.currentUser?.uid;
|
||||
final userId = (uid ?? '').trim();
|
||||
if (userId.isNotEmpty && scope.isNotEmpty && scope.startsWith('${userId}_')) {
|
||||
if (userId.isNotEmpty &&
|
||||
scope.isNotEmpty &&
|
||||
scope.startsWith('${userId}_')) {
|
||||
final childId = scope.substring(userId.length + 1).trim();
|
||||
if (childId.isNotEmpty) {
|
||||
unawaited(
|
||||
FirebaseFirestore.instance
|
||||
await FirebaseFirestore.instance
|
||||
.collection('users')
|
||||
.doc(userId)
|
||||
.collection('children')
|
||||
@@ -48,8 +71,8 @@ class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
'lastScore': widget.finalScore,
|
||||
'lastMaxScore': widget.maxScore,
|
||||
'lastQuizAt': FieldValue.serverTimestamp(),
|
||||
}, SetOptions(merge: true)).catchError((_) {}),
|
||||
);
|
||||
}, SetOptions(merge: true))
|
||||
.catchError((_) {});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,10 +89,7 @@ class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0xFFFFE6F1),
|
||||
Color(0xFFFFC9DF),
|
||||
],
|
||||
colors: [Color(0xFFFFE6F1), Color(0xFFFFC9DF)],
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
@@ -84,7 +104,8 @@ class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () => Navigator.of(context).popUntil((r) => r.isFirst),
|
||||
onPressed: () =>
|
||||
Navigator.of(context).popUntil((r) => r.isFirst),
|
||||
child: const Text(''),
|
||||
),
|
||||
),
|
||||
@@ -118,8 +139,12 @@ class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
child: CircularProgressIndicator(
|
||||
value: progress,
|
||||
strokeWidth: 12,
|
||||
backgroundColor: Colors.black.withValues(alpha: 0.10),
|
||||
valueColor: const AlwaysStoppedAnimation(Color(0xFF2F9E94)),
|
||||
backgroundColor: Colors.black
|
||||
.withValues(alpha: 0.10),
|
||||
valueColor:
|
||||
const AlwaysStoppedAnimation(
|
||||
Color(0xFF2F9E94),
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
@@ -137,7 +162,9 @@ class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
Text(
|
||||
'${clamped.toInt()}/${widget.maxScore}',
|
||||
style: TextStyle(
|
||||
color: Colors.black.withValues(alpha: 0.60),
|
||||
color: Colors.black.withValues(
|
||||
alpha: 0.60,
|
||||
),
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
@@ -171,7 +198,9 @@ class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
child: Text(
|
||||
'Descarregar relatório (em breve)',
|
||||
style: TextStyle(
|
||||
color: const Color(0xFFFF55A7).withValues(alpha: 0.95),
|
||||
color: const Color(
|
||||
0xFFFF55A7,
|
||||
).withValues(alpha: 0.95),
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
@@ -189,9 +218,12 @@ class _QuizResultScreenState extends State<QuizResultScreen> {
|
||||
backgroundColor: const Color(0xFF2F9E94),
|
||||
foregroundColor: Colors.white,
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w900),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
onPressed: () => Navigator.of(context).popUntil((r) => r.isFirst),
|
||||
),
|
||||
onPressed: () =>
|
||||
Navigator.of(context).popUntil((r) => r.isFirst),
|
||||
child: const Text('Avançar'),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user