CTK 1.0.2

This commit is contained in:
Carlos Correia
2026-07-15 17:46:14 +01:00
parent 9c21887fc7
commit 3214a07779
9 changed files with 211 additions and 150 deletions

View File

@@ -62,8 +62,7 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
static const String _kPendingQuizScopeKey = 'pending_quiz_scope_v1';
static const double _collapsedAppBarHeight = kToolbarHeight;
static const double _expandedAppBarHeight = 226;
static const double _nameOnlyAppBarHeight = 160;
static const double _expandedAppBarHeight = 256;
int _index = 0;
@@ -349,9 +348,11 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
// um NestedScrollView em vez do AppBar fixo usado nas outras abas,
// para que a altura acompanhe o scroll em vez de ficar sempre cheia
// (o que cortava o conteúdo do card do quiz contra a barra ao rolar).
final double expandedHeight = hasScore
? _expandedAppBarHeight
: _nameOnlyAppBarHeight;
// A app bar mantém sempre a mesma altura e mostra sempre os dois
// gauges — vazios (sem número, sem rótulo) quando ainda não há
// resultado, para o utilizador já perceber que aquele espaço é para
// o resultado do quiz mesmo antes de o fazer.
const double expandedHeight = _expandedAppBarHeight;
return Scaffold(
body: NestedScrollView(
@@ -385,7 +386,7 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
Positioned(
left: 0,
right: 0,
top: kToolbarHeight + 34,
top: kToolbarHeight + 50,
child: Center(
child: Text(
(_selectedChildName ?? '').trim(),
@@ -397,54 +398,35 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
),
),
),
)
else if ((_selectedChildName ?? '').trim().isNotEmpty)
Positioned(
left: 0,
right: 0,
top: kToolbarHeight,
bottom: 0,
child: Center(
child: Text(
_selectedChildName!.trim(),
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w800,
color: Colors.white.withValues(alpha: 0.92),
fontSize: 14,
),
),
),
),
//posição da app bar relativamente ao nome
if (hasScore)
Positioned(
left: 0,
right: 0,
top: kToolbarHeight + 60,
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_RiskArcGauge(
// Usa sempre o máximo atual do quiz (não
// o que foi gravado na última avaliação)
// para não mostrar um denominador antigo
// quando o número de perguntas muda.
value: result.signs,
max: kSignsMax,
label: 'Sinais',
),
const SizedBox(width: 18),
_RiskArcGauge(
value: result.factors,
max: kFactorsMax,
label: 'Fatores de risco',
),
],
),
Positioned(
left: 0,
right: 0,
top: kToolbarHeight + 96,
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_RiskArcGauge(
// Usa sempre o máximo atual do quiz (não
// o que foi gravado na última avaliação)
// para não mostrar um denominador antigo
// quando o número de perguntas muda.
value: hasScore ? result.signs : null,
max: hasScore ? kSignsMax : null,
label: 'Sinais de má\noclusão',
),
const SizedBox(width: 18),
_RiskArcGauge(
value: hasScore ? result.factors : null,
max: hasScore ? kFactorsMax : null,
label: 'Fatores de risco',
),
],
),
),
),
],
),
),
@@ -587,19 +569,23 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
}
class _RiskArcGauge extends StatelessWidget {
const _RiskArcGauge({
required this.value,
required this.max,
required this.label,
});
const _RiskArcGauge({required this.value, required this.max, this.label});
final int value;
final int max;
final String label;
/// Quando [value]/[max] são nulos (sem criança selecionada, ou criança
/// ainda sem quiz feito), o gauge mostra-se vazio — só o anel em branco,
/// sem número nem rótulo — em vez de desaparecer, para o espaço já
/// indicar onde o resultado vai aparecer assim que existir.
final int? value;
final int? max;
final String? label;
bool get _isEmpty => value == null || max == null;
@override
Widget build(BuildContext context) {
final progress = max > 0 ? (value / max).clamp(0, 1).toDouble() : 0.0;
final progress = _isEmpty || max! <= 0
? 0.0
: (value! / max!).clamp(0, 1).toDouble();
return TweenAnimationBuilder<double>(
duration: const Duration(milliseconds: 700),
@@ -621,38 +607,40 @@ class _RiskArcGauge extends StatelessWidget {
painter: _RiskArcGaugePainter(progress: animatedProgress),
),
),
Positioned(
top: 38,
left: 0,
right: 0,
child: Center(
child: Text(
'$value/$max',
style: const TextStyle(
color: Colors.white,
fontSize: 17,
fontWeight: FontWeight.w900,
height: 1,
if (!_isEmpty) ...[
Positioned(
top: 38,
left: 0,
right: 0,
child: Center(
child: Text(
'$value/$max',
style: const TextStyle(
color: Colors.white,
fontSize: 17,
fontWeight: FontWeight.w900,
height: 1,
),
),
),
),
),
Positioned(
top: 68,
left: 0,
right: 0,
child: Text(
label,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white.withValues(alpha: 0.85),
fontSize: 10.5,
fontWeight: FontWeight.w700,
height: 1.15,
letterSpacing: 0.1,
Positioned(
top: 68,
left: 0,
right: 0,
child: Text(
label ?? '',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white.withValues(alpha: 0.85),
fontSize: 10.5,
fontWeight: FontWeight.w700,
height: 1.15,
letterSpacing: 0.1,
),
),
),
),
],
],
),
);
@@ -1350,7 +1338,7 @@ class _HeroQuizCard extends StatelessWidget {
),
const SizedBox(height: 5),
Text(
'28 perguntas rápidas · menos de 3 minutos',
'29 perguntas rápidas · menos de 3 minutos',
style: TextStyle(
color: Colors.white.withValues(alpha: 0.92),
fontWeight: FontWeight.w600,