From fb39e1f7222edfbd6a64dea6a4fec16f6a3b3728 Mon Sep 17 00:00:00 2001 From: Carlos Correia <240402@epvc.ptm> Date: Tue, 14 Jul 2026 22:19:26 +0100 Subject: [PATCH] CTK 1.0.0 --- lib/home_screen.dart | 24 ++++++------ lib/logged_home.dart | 30 +++++++++++---- lib/quiz/quiz1.dart | 64 ++++++++++++++++---------------- lib/quiz/quiz_video_guide.dart | 4 +- lib/screens/settings_screen.dart | 8 ++-- lib/screens/video_screen.dart | 2 +- lib/widgets/privacy_content.dart | 12 +++--- 7 files changed, 79 insertions(+), 65 deletions(-) diff --git a/lib/home_screen.dart b/lib/home_screen.dart index 9ce0a5e..b928b79 100644 --- a/lib/home_screen.dart +++ b/lib/home_screen.dart @@ -113,7 +113,7 @@ class _HomeScreenState extends State { final user = response.user; if (user == null) { - throw StateError('Usuário não encontrado após criar conta.'); + throw StateError('Utilizador não encontrado após criar a conta.'); } // Antes de persistir o perfil (o que já faz o AuthGate considerar a @@ -137,7 +137,7 @@ class _HomeScreenState extends State { if (!mounted) return; showPillSnackBar( context, - 'Esta conta não existe mais. Verifique o email ou crie uma nova conta.', + 'Esta conta já não existe. Verifique o email ou crie uma nova conta.', ); } on AuthException catch (e) { if (!mounted) return; @@ -146,7 +146,7 @@ class _HomeScreenState extends State { if (!mounted) return; showPillSnackBar( context, - 'Tempo esgotado. Verifique sua conexão e tente novamente.', + 'Tempo esgotado. Verifique a sua ligação e tente novamente.', ); } catch (e) { if (!mounted) return; @@ -159,14 +159,14 @@ class _HomeScreenState extends State { String _friendlyAuthError(AuthException e) { switch (e.code) { case 'invalid_credentials': - return 'Email ou senha incorretos.'; + return 'Email ou palavra-passe incorretos.'; case 'user_not_found': - return 'Usuário não encontrado.'; + return 'Utilizador não encontrado.'; case 'email_exists': case 'user_already_exists': return 'Este email já está em uso.'; case 'weak_password': - return 'Senha fraca. Use pelo menos 6 caracteres.'; + return 'Palavra-passe fraca. Utilize pelo menos 6 caracteres.'; default: return e.message; } @@ -376,14 +376,14 @@ class _AuthForm extends StatelessWidget { children: [ _AuthTextField( controller: nameController, - hintText: 'Digite seu nome', + hintText: 'Introduza o seu nome', icon: Icons.person_outline_rounded, textInputAction: TextInputAction.next, textCapitalization: TextCapitalization.sentences, inputFormatters: [CapitalizeFirstLetterFormatter()], validator: (v) { final value = (v ?? '').trim(); - if (value.isEmpty) return 'Informe seu nome'; + if (value.isEmpty) return 'Indique o seu nome'; if (value.length < 2) return 'Nome muito curto'; if (!_namePattern.hasMatch(value)) { return 'O nome não pode conter números'; @@ -398,13 +398,13 @@ class _AuthForm extends StatelessWidget { ), _AuthTextField( controller: emailController, - hintText: 'Digite seu email', + hintText: 'Introduza o seu email', icon: Icons.mail_outline_rounded, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, validator: (v) { final value = (v ?? '').trim(); - if (value.isEmpty) return 'Informe seu email'; + if (value.isEmpty) return 'Indique o seu email'; if (!value.contains('@')) return 'Email inválido'; return null; }, @@ -412,13 +412,13 @@ class _AuthForm extends StatelessWidget { const SizedBox(height: 12), _AuthTextField( controller: passwordController, - hintText: 'Digite sua senha', + hintText: 'Introduza a sua palavra-passe', icon: Icons.lock_outline_rounded, obscureText: true, textInputAction: TextInputAction.done, validator: (v) { final value = v ?? ''; - if (value.isEmpty) return 'Informe sua senha'; + if (value.isEmpty) return 'Indique a sua palavra-passe'; if (value.length < 6) return 'Mínimo de 6 caracteres'; return null; }, diff --git a/lib/logged_home.dart b/lib/logged_home.dart index f596911..a979a5c 100644 --- a/lib/logged_home.dart +++ b/lib/logged_home.dart @@ -813,6 +813,20 @@ class _InicioTab extends StatelessWidget { ), ), ), + const SizedBox(height: 14), + FadeSlideIn( + delay: const Duration(milliseconds: 150), + child: Center( + child: Text( + 'Mais funcionalidades em breve', + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 12.5, + color: Colors.black.withValues(alpha: 0.35), + ), + ), + ), + ), const SizedBox(height: 16), ], ), @@ -1095,7 +1109,7 @@ Future?> _requireFirstChild( ) async { final proceed = await showConfirmDialog( context, - title: 'Cadastre uma criança', + title: 'Registe uma criança', message: 'Antes de iniciar o quiz, adicione uma criança ao seu perfil.', confirmLabel: 'Adicionar criança', ); @@ -1636,7 +1650,7 @@ class _PerfilTabState extends State<_PerfilTab> { ), onPressed: () => Navigator.of(ctx).pop(ImageSource.camera), - child: const Text('Câmera'), + child: const Text('Câmara'), ), ), ), @@ -1729,7 +1743,7 @@ class _PerfilTabState extends State<_PerfilTab> { context, title: 'Remover criança', message: - 'Tem certeza que deseja remover "$childName"? Essa ação não pode ser desfeita.', + 'Tem a certeza que quer remover "$childName"? Esta ação não pode ser desfeita.', confirmLabel: 'Remover', confirmColor: const Color(0xFFFF55A7), ); @@ -2160,7 +2174,7 @@ class _PerfilTabState extends State<_PerfilTab> { : 'Criança ${i + 1}'; final subtitle = [ if (childAge != null) 'Idade: $childAge', - if (childGender.isNotEmpty) 'Gênero: $childGender', + if (childGender.isNotEmpty) 'Género: $childGender', ].join(' • '); final bool selected = i == selectedIndex; @@ -2389,7 +2403,7 @@ class _AddChildSheetState extends State<_AddChildSheet> { final formOk = _formKey.currentState?.validate() ?? false; setState(() { _birthDateError = _birthDate == null - ? 'Informe a data de nascimento' + ? 'Indique a data de nascimento' : null; }); if (!formOk || _birthDate == null) return; @@ -2442,7 +2456,7 @@ class _AddChildSheetState extends State<_AddChildSheet> { ), validator: (v) { final value = (v ?? '').trim(); - if (value.isEmpty) return 'Informe o nome'; + if (value.isEmpty) return 'Indique o nome'; if (value.length < 2) return 'Nome muito curto'; if (!_namePattern.hasMatch(value)) { return 'O nome não pode conter números'; @@ -2490,10 +2504,10 @@ class _AddChildSheetState extends State<_AddChildSheet> { DropdownMenuItem(value: 'Outro', child: Text('Outro')), ], onChanged: (v) => setState(() => _gender = v), - decoration: const InputDecoration(labelText: 'Gênero'), + decoration: const InputDecoration(labelText: 'Género'), validator: (v) { if (v == null || v.trim().isEmpty) { - return 'Selecione o gênero'; + return 'Selecione o género'; } return null; }, diff --git a/lib/quiz/quiz1.dart b/lib/quiz/quiz1.dart index ff17459..c76df8d 100644 --- a/lib/quiz/quiz1.dart +++ b/lib/quiz/quiz1.dart @@ -920,11 +920,11 @@ class Quiz19Screen extends StatelessWidget { 'Qual das seguintes imagens é mais parecida com o perfil do seu filho/a?', answers: [ QuizAnswer( - title: 'Perfil reto', - description: 'Perfil facial reto', - weight: 1, + title: 'Perfil convexo', + description: 'Perfil facial convexo', + weight: 2, hideTitle: true, - value: 'perfil_reto', + value: 'perfil_convexo', imageBuilder: (context) => _zoomCrop( 'assets/mockup_images/1.jpeg', alignment: const Alignment(0.1, 0.7), @@ -932,11 +932,11 @@ class Quiz19Screen extends StatelessWidget { ), ), QuizAnswer( - title: 'Perfil convexo', - description: 'Perfil facial convexo', - weight: 2, + title: 'Perfil reto', + description: 'Perfil facial reto', + weight: 1, hideTitle: true, - value: 'perfil_convexo', + value: 'perfil_reto', imageBuilder: (context) => _zoomCrop( 'assets/mockup_images/2.jpeg', alignment: const Alignment(0.1, 0.7), @@ -1199,20 +1199,20 @@ class Quiz24Screen extends StatelessWidget { question: 'Qual das seguintes imagens se assemelha à boca do seu filho/a?', answers: const [ - QuizAnswer( - title: 'Ligeiro desalinhamento', - description: 'Pequeno desalinhamento ou espaço entre alguns dentes', - weight: 1, - hideTitle: true, - value: 'apinhamento_leve', - imagePath: 'assets/mockup_images/16.png', - ), QuizAnswer( title: 'Apinhamento moderado', description: 'Dentes rodados/desalinhados de forma mais visível', weight: 2, hideTitle: true, value: 'apinhamento_moderado', + imagePath: 'assets/mockup_images/16.png', + ), + QuizAnswer( + title: 'Ligeiro desalinhamento', + description: 'Pequeno desalinhamento ou espaço entre alguns dentes', + weight: 1, + hideTitle: true, + value: 'apinhamento_leve', imagePath: 'assets/mockup_images/15.png', ), QuizAnswer( @@ -1297,20 +1297,20 @@ class Quiz26Screen extends StatelessWidget { question: 'Qual das seguintes imagens se assemelha ao freio lingual do seu filho/a?', answers: const [ - QuizAnswer( - title: 'Freio lingual correto', - description: 'Língua move-se livremente, sem restrição visível', - weight: 1, - hideTitle: true, - value: 'freio_lingual_correto', - imagePath: 'assets/mockup_images/17.png', - ), QuizAnswer( title: 'Freio lingual inadequado', description: 'Freio lingual curto/apertado (língua em coração)', weight: 2, hideTitle: true, value: 'freio_lingual_inadequado', + imagePath: 'assets/mockup_images/17.png', + ), + QuizAnswer( + title: 'Freio lingual correto', + description: 'Língua move-se livremente, sem restrição visível', + weight: 1, + hideTitle: true, + value: 'freio_lingual_correto', imagePath: 'assets/mockup_images/18.jpeg', ), ], @@ -1389,20 +1389,20 @@ class Quiz28Screen extends StatelessWidget { question: 'Qual das seguintes imagens se assemelha ao céu da boca do seu filho/a?', answers: const [ - QuizAnswer( - title: 'Posição em U saudável', - description: 'Palato largo, em forma de U', - weight: 1, - hideTitle: true, - value: 'palato_u', - imagePath: 'assets/mockup_images/26.png', - ), QuizAnswer( title: 'Posição profunda incorreta', description: 'Palato estreito/profundo, em forma de V', weight: 2, hideTitle: true, value: 'palato_v', + imagePath: 'assets/mockup_images/26.png', + ), + QuizAnswer( + title: 'Posição em U saudável', + description: 'Palato largo, em forma de U', + weight: 1, + hideTitle: true, + value: 'palato_u', imagePath: 'assets/mockup_images/27.png', ), ], diff --git a/lib/quiz/quiz_video_guide.dart b/lib/quiz/quiz_video_guide.dart index aca7ff6..1934f18 100644 --- a/lib/quiz/quiz_video_guide.dart +++ b/lib/quiz/quiz_video_guide.dart @@ -79,7 +79,7 @@ class QuizVideoGuideScreen extends StatefulWidget { required this.youtubeId, required this.onAdvance, this.caption = - 'Assista para compreender melhor as próximas perguntas do questionário.', + 'Veja o vídeo para compreender melhor as próximas perguntas do questionário.', this.extraHeading, this.extraSubtitle, }); @@ -144,7 +144,7 @@ class _QuizVideoGuideScreenState extends State { const SizedBox(height: 4), const FadeSlideIn( child: Text( - 'Antes de continuar, assista ao vídeo educativo...', + 'Antes de continuar, veja o vídeo educativo...', style: TextStyle( fontSize: 21, fontWeight: FontWeight.w900, diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index b1a7517..b801798 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -34,10 +34,10 @@ class _SettingsBodyState extends State { context, title: 'Apagar dados da conta', message: - 'Isso remove permanentemente a sua conta, perfil, crianças ' - 'cadastradas e fotos — incluindo o login, permitindo criar uma ' - 'nova conta com o mesmo e-mail depois. Essa ação não pode ser ' - 'desfeita. Deseja continuar?', + 'Isto remove permanentemente a sua conta, perfil, crianças ' + 'registadas e fotos — incluindo o login, permitindo criar uma ' + 'nova conta com o mesmo e-mail depois. Esta ação não pode ser ' + 'desfeita. Pretende continuar?', confirmLabel: 'Apagar', confirmColor: _accentPink, ); diff --git a/lib/screens/video_screen.dart b/lib/screens/video_screen.dart index 60cce4f..1917dc2 100644 --- a/lib/screens/video_screen.dart +++ b/lib/screens/video_screen.dart @@ -253,7 +253,7 @@ class _VideoScreenState extends State { elevation: 0, scrolledUnderElevation: 0, title: const Text( - 'Videos Educativos', + 'Vídeos Educativos', style: TextStyle(fontWeight: FontWeight.w900), ), ), diff --git a/lib/widgets/privacy_content.dart b/lib/widgets/privacy_content.dart index c60512b..8c1d9b6 100644 --- a/lib/widgets/privacy_content.dart +++ b/lib/widgets/privacy_content.dart @@ -16,8 +16,8 @@ const List kPrivacyConsentItems = [ id: 'health_data', text: 'Concordo com o processamento dos meus dados pessoais de saúde para ' - 'ter acesso aos recursos do aplicativo Check-Teeth Kids. Veja mais ' - 'na Política de Privacidade.', + 'ter acesso às funcionalidades da aplicação Check-Teeth Kids. Veja ' + 'mais na Política de Privacidade.', ), PrivacyConsentItem( id: 'privacy_policy', @@ -26,11 +26,11 @@ const List kPrivacyConsentItems = [ PrivacyConsentItem( id: 'tracking', text: - 'Autorizo o CK-T-KD a rastrear a minha atividade na app. Podem ' - 'receber dados como minha faixa etária, status da assinatura, ' + 'Autorizo o CK-T-KD a monitorizar a minha atividade na app. Podem ' + 'receber dados como a minha faixa etária, estado da subscrição, ' 'momento de abertura da app e identificadores técnicos, conforme ' - 'descrito na Política de Privacidade. Isso ajuda a CK-T-KD a ' - 'aprimorar as suas campanhas publicitárias. *', + 'descrito na Política de Privacidade. Isto ajuda o CK-T-KD a ' + 'melhorar as suas campanhas publicitárias. *', ), ];