Nova tela de login | Adaptaçao nova da AppBar | Animções novas

This commit is contained in:
Carlos Correia
2026-07-07 21:40:05 +01:00
parent a8e04ceeb2
commit 2ced93afdd
15 changed files with 1680 additions and 1322 deletions

View File

@@ -3,6 +3,9 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import '../widgets/entrance.dart';
import '../widgets/tap_bounce.dart';
class CuriosidadeScreen extends StatelessWidget {
const CuriosidadeScreen({super.key});
@@ -68,24 +71,48 @@ class CuriosidadeScreen extends StatelessWidget {
child: ListView(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 16),
children: [
_CuriosityTopicTile(
title: 'Tema X',
description: 'Aprenda dicas rápidas e simples para cuidar dos dentes no dia a dia.',
FadeSlideIn(
child: TapBounce(
scale: 0.97,
child: _CuriosityTopicTile(
title: 'Tema X',
description:
'Aprenda dicas rápidas e simples para cuidar dos dentes no dia a dia.',
),
),
),
const SizedBox(height: 12),
const _CuriosityTopicTile(
title: 'Tema Y',
description: 'Conteúdo em breve.',
FadeSlideIn(
delay: const Duration(milliseconds: 60),
child: const TapBounce(
scale: 0.97,
child: _CuriosityTopicTile(
title: 'Tema Y',
description: 'Conteúdo em breve.',
),
),
),
const SizedBox(height: 12),
const _CuriosityTopicTile(
title: 'Tema Z',
description: 'Conteúdo em breve.',
FadeSlideIn(
delay: const Duration(milliseconds: 120),
child: const TapBounce(
scale: 0.97,
child: _CuriosityTopicTile(
title: 'Tema Z',
description: 'Conteúdo em breve.',
),
),
),
const SizedBox(height: 12),
const _CuriosityTopicTile(
title: 'Tema U',
description: 'Conteúdo em breve.',
FadeSlideIn(
delay: const Duration(milliseconds: 180),
child: const TapBounce(
scale: 0.97,
child: _CuriosityTopicTile(
title: 'Tema U',
description: 'Conteúdo em breve.',
),
),
),
],
),
@@ -156,17 +183,21 @@ class _CuriosityTopicTile extends StatelessWidget {
),
),
const SizedBox(height: 14),
SizedBox(
height: 44,
child: FilledButton(
style: FilledButton.styleFrom(
backgroundColor: const Color(0xFF2F9E94),
foregroundColor: Colors.white,
shape: const StadiumBorder(),
textStyle: const TextStyle(fontWeight: FontWeight.w900),
TapBounce(
child: SizedBox(
height: 44,
child: FilledButton(
style: FilledButton.styleFrom(
backgroundColor: const Color(0xFF2F9E94),
foregroundColor: Colors.white,
shape: const StadiumBorder(),
textStyle: const TextStyle(
fontWeight: FontWeight.w900,
),
),
onPressed: () => Navigator.of(ctx).pop(),
child: const Text('Fechar'),
),
onPressed: () => Navigator.of(ctx).pop(),
child: const Text('Fechar'),
),
),
],

View File

@@ -12,10 +12,19 @@ class HelloSplashScreen extends StatefulWidget {
State<HelloSplashScreen> createState() => _HelloSplashScreenState();
}
class _HelloSplashScreenState extends State<HelloSplashScreen> with SingleTickerProviderStateMixin {
class _HelloSplashScreenState extends State<HelloSplashScreen> with TickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _opacity;
late final AnimationController _popController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 700),
);
late final Animation<double> _pop = CurvedAnimation(
parent: _popController,
curve: Curves.elasticOut,
);
Timer? _fadeTimer;
Timer? _doneTimer;
@@ -34,6 +43,7 @@ class _HelloSplashScreenState extends State<HelloSplashScreen> with SingleTicker
);
_controller.value = 1.0;
_popController.forward();
final int fadeMs = (widget.duration.inMilliseconds - 500).clamp(0, widget.duration.inMilliseconds);
_fadeTimer = Timer(Duration(milliseconds: fadeMs), () {
@@ -52,6 +62,7 @@ class _HelloSplashScreenState extends State<HelloSplashScreen> with SingleTicker
_fadeTimer?.cancel();
_doneTimer?.cancel();
_controller.dispose();
_popController.dispose();
super.dispose();
}
@@ -70,15 +81,18 @@ class _HelloSplashScreenState extends State<HelloSplashScreen> with SingleTicker
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
Text(
'Olá',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 64,
fontWeight: FontWeight.w900,
color: Colors.white,
height: 1.0,
children: [
ScaleTransition(
scale: _pop,
child: const Text(
'Olá',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 64,
fontWeight: FontWeight.w900,
color: Colors.white,
height: 1.0,
),
),
),
],

View File

@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import '../main.dart' show supabase;
import '../widgets/app_dialogs.dart';
import '../widgets/entrance.dart';
import '../widgets/tap_bounce.dart';
import 'terms_screen.dart';
const Color _teal = Color(0xFF2F9E94);
@@ -65,53 +67,78 @@ class _SettingsBodyState extends State<SettingsBody> {
return ListView(
padding: const EdgeInsets.all(16),
children: [
_SectionLabel('Conta'),
_SettingsCard(
children: [
_InfoTile(
icon: Icons.person_outline_rounded,
title: name.isEmpty ? 'Sem nome' : name,
subtitle: email,
),
const Divider(height: 1),
_ActionTile(
icon: Icons.logout_rounded,
title: 'Sair',
onTap: _signOut,
),
],
),
const SizedBox(height: 20),
_SectionLabel('Sobre'),
_SettingsCard(
children: [
_ActionTile(
icon: Icons.description_outlined,
title: 'Termos de Serviço',
onTap: () => Navigator.of(context).push(
MaterialPageRoute<void>(builder: (_) => const TermsScreen()),
FadeSlideIn(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_SectionLabel('Conta'),
_SettingsCard(
children: [
_InfoTile(
icon: Icons.person_outline_rounded,
title: name.isEmpty ? 'Sem nome' : name,
subtitle: email,
),
const Divider(height: 1),
_ActionTile(
icon: Icons.logout_rounded,
title: 'Sair',
onTap: _signOut,
),
],
),
),
const Divider(height: 1),
const _InfoTile(
icon: Icons.info_outline_rounded,
title: 'Versão do app',
subtitle: '1.0.0',
),
],
],
),
),
const SizedBox(height: 20),
_SectionLabel('Zona de risco'),
_SettingsCard(
children: [
_ActionTile(
icon: Icons.delete_forever_rounded,
title: 'Apagar dados da conta',
titleColor: _accentPink,
loading: _deletingAccount,
onTap: _deletingAccount ? null : _confirmDeleteAccountData,
),
],
FadeSlideIn(
delay: const Duration(milliseconds: 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_SectionLabel('Sobre'),
_SettingsCard(
children: [
_ActionTile(
icon: Icons.description_outlined,
title: 'Termos de Serviço',
onTap: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const TermsScreen(),
),
),
),
const Divider(height: 1),
const _InfoTile(
icon: Icons.info_outline_rounded,
title: 'Versão do app',
subtitle: '1.0.0',
),
],
),
],
),
),
const SizedBox(height: 20),
FadeSlideIn(
delay: const Duration(milliseconds: 160),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_SectionLabel('Zona de risco'),
_SettingsCard(
children: [
_ActionTile(
icon: Icons.delete_forever_rounded,
title: 'Apagar dados da conta',
titleColor: _accentPink,
loading: _deletingAccount,
onTap: _deletingAccount ? null : _confirmDeleteAccountData,
),
],
),
],
),
),
const SizedBox(height: 12),
],
@@ -193,20 +220,23 @@ class _ActionTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(icon, color: titleColor ?? _teal),
title: Text(
title,
style: TextStyle(fontWeight: FontWeight.w800, color: titleColor),
return TapBounce(
scale: 0.98,
child: ListTile(
leading: Icon(icon, color: titleColor ?? _teal),
title: Text(
title,
style: TextStyle(fontWeight: FontWeight.w800, color: titleColor),
),
trailing: loading
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.chevron_right_rounded),
onTap: onTap,
),
trailing: loading
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.chevron_right_rounded),
onTap: onTap,
);
}
}

View File

@@ -6,6 +6,9 @@ import 'package:lottie/lottie.dart';
import 'package:video_player/video_player.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import '../widgets/entrance.dart';
import '../widgets/tap_bounce.dart';
// Video data structure - easily editable for future updates.
// Episódios 1-7 tocam via YouTube (não listado); preencha youtubeId ao subir
// cada vídeo. Episódios 8-13 continuam embutidos no app (assets/videos).
@@ -276,8 +279,13 @@ class _VideoScreenState extends State<VideoScreen> {
),
itemCount: _filteredVideos.length,
itemBuilder: (context, index) {
return _VideoButton(
video: _filteredVideos[index],
return FadeSlideIn(
delay: Duration(
milliseconds: 40 * (index % 8),
),
child: _VideoButton(
video: _filteredVideos[index],
),
);
},
),
@@ -437,7 +445,9 @@ class _VideoButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Material(
return TapBounce(
scale: 0.95,
child: Material(
elevation: 8,
shadowColor: Colors.black.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(16),
@@ -484,6 +494,7 @@ class _VideoButton extends StatelessWidget {
),
),
),
),
);
}
}