quiz redirecionado aos videos | redesing da notificação

This commit is contained in:
Carlos Correia
2026-07-10 13:06:40 +01:00
parent 7ddc76d393
commit 30068d1501
11 changed files with 323 additions and 142 deletions

View File

@@ -0,0 +1,19 @@
import 'package:flutter/services.dart';
/// Capitaliza automaticamente a primeira letra à medida que o utilizador
/// escreve — usado nos campos de nome (criança e conta) para não depender
/// só da sugestão do teclado (que o utilizador pode ignorar ou que pode
/// não existir em todos os teclados).
class CapitalizeFirstLetterFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final text = newValue.text;
if (text.isEmpty) return newValue;
final capitalized = text[0].toUpperCase() + text.substring(1);
if (capitalized == text) return newValue;
return newValue.copyWith(text: capitalized, selection: newValue.selection);
}
}

View File

@@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
/// Notificação em formato de pílula flutuante (fundo branco, texto rosa,
/// cantos totalmente arredondados, com um pequeno "pop" de entrada), usada
/// em toda a app em vez do SnackBar padrão do Material (barra preta a
/// ocupar a largura toda). O SnackBar continua a ser o mecanismo por trás
/// — só o visual muda: fica transparente/sem elevação e o conteúdo real é
/// este contentor central que abraça o texto em vez de esticar de ponta a
/// ponta.
void showPillSnackBar(BuildContext context, String message) {
final messenger = ScaffoldMessenger.of(context);
messenger
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.transparent,
elevation: 0,
duration: const Duration(seconds: 2),
padding: EdgeInsets.zero,
content: Center(child: _AnimatedPill(message: message)),
),
);
}
/// Pequeno "pop" de entrada (escala + fade, com leve ultrapassagem via
/// [Curves.easeOutBack]) para a pílula de notificação — em vez de aparecer
/// estática, dá-lhe uma sensação de vida ao surgir. Corre uma única vez,
/// automaticamente, assim que este widget é construído (cada chamada a
/// [showPillSnackBar] cria uma instância nova).
class _AnimatedPill extends StatelessWidget {
const _AnimatedPill({required this.message});
final String message;
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 380),
curve: Curves.easeOutBack,
builder: (context, value, child) {
return Opacity(
opacity: value.clamp(0.0, 1.0),
child: Transform.scale(scale: value, child: child),
);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(999),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 8,
offset: const Offset(0, 3),
),
],
),
child: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color(0xFFFF55A7),
fontWeight: FontWeight.w800,
fontSize: 14,
),
),
),
);
}
}