Nova tela de login | Adaptaçao nova da AppBar | Animções novas
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
@@ -12,7 +13,10 @@ import 'quiz/quiz1.dart';
|
||||
import 'quiz/quiz_prefs.dart';
|
||||
import 'screens/settings_screen.dart';
|
||||
import 'screens/video_screen.dart';
|
||||
import 'widgets/animated_nav_icon.dart';
|
||||
import 'widgets/app_dialogs.dart';
|
||||
import 'widgets/entrance.dart';
|
||||
import 'widgets/tap_bounce.dart';
|
||||
|
||||
class LoggedHomeScreen extends StatefulWidget {
|
||||
const LoggedHomeScreen({super.key});
|
||||
@@ -28,6 +32,7 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
|
||||
|
||||
static const double _collapsedAppBarHeight = 104;
|
||||
static const double _expandedAppBarHeight = 180;
|
||||
static const double _nameOnlyAppBarHeight = 130;
|
||||
|
||||
int _index = 0;
|
||||
|
||||
@@ -207,8 +212,14 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
|
||||
final int? score = _lastScore;
|
||||
final int? maxScore = _lastMaxScore;
|
||||
final bool hasScore = score != null && maxScore != null && maxScore > 0;
|
||||
final int percent = hasScore ? ((score / maxScore) * 100).round() : 0;
|
||||
|
||||
final double appBarHeight = _index == 0
|
||||
? _expandedAppBarHeight
|
||||
? (hasScore ? _expandedAppBarHeight : _nameOnlyAppBarHeight)
|
||||
: _collapsedAppBarHeight;
|
||||
final double toolbarHeight = _index == 0 ? kToolbarHeight : appBarHeight;
|
||||
final String title = _index == 0
|
||||
@@ -224,10 +235,6 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
|
||||
|
||||
final shownName = _cachedUserName;
|
||||
|
||||
final int? score = _lastScore;
|
||||
final int? maxScore = _lastMaxScore;
|
||||
final bool hasScore = score != null && maxScore != null && maxScore > 0;
|
||||
final int percent = hasScore ? ((score / maxScore) * 100).round() : 0;
|
||||
final double bodyTopPadding = _index == 0 ? 0 : 10;
|
||||
|
||||
return Scaffold(
|
||||
@@ -251,47 +258,41 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
|
||||
opacity: 0.22,
|
||||
child: Transform.scale(scale: 1.25),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: toolbarHeight + 26,
|
||||
child: Center(
|
||||
child: RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
if (hasScore)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: toolbarHeight + 26,
|
||||
child: Center(
|
||||
child: Text(
|
||||
(_selectedChildName ?? '').trim(),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Colors.white.withValues(alpha: 0.92),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
else if ((_selectedChildName ?? '').trim().isNotEmpty)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: toolbarHeight,
|
||||
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,
|
||||
),
|
||||
children: [
|
||||
if (((_selectedChildName ?? '')
|
||||
.trim()
|
||||
.isNotEmpty))
|
||||
TextSpan(text: _selectedChildName!.trim()),
|
||||
if (((_selectedChildName ?? '')
|
||||
.trim()
|
||||
.isNotEmpty) &&
|
||||
hasScore)
|
||||
const WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
),
|
||||
child: Text(
|
||||
'•',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (hasScore)
|
||||
TextSpan(text: '$score/$maxScore'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (hasScore)
|
||||
Positioned(
|
||||
left: 0,
|
||||
@@ -308,63 +309,65 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
|
||||
child: _index == 0
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 10),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
onTap: () => setState(() => _index = 1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: 4,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: Colors.white.withValues(
|
||||
alpha: 0.25,
|
||||
child: TapBounce(
|
||||
scale: 0.96,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
onTap: () => setState(() => _index = 1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4,
|
||||
horizontal: 4,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 20,
|
||||
backgroundColor: Colors.white
|
||||
.withValues(alpha: 0.25),
|
||||
backgroundImage:
|
||||
(_cachedPhotoUrl ?? '').isNotEmpty
|
||||
? NetworkImage(_cachedPhotoUrl!)
|
||||
: null,
|
||||
child: (_cachedPhotoUrl ?? '').isEmpty
|
||||
? const Icon(
|
||||
Icons.person_rounded,
|
||||
color: Colors.white,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
backgroundImage:
|
||||
(_cachedPhotoUrl ?? '').isNotEmpty
|
||||
? NetworkImage(_cachedPhotoUrl!)
|
||||
: null,
|
||||
child: (_cachedPhotoUrl ?? '').isEmpty
|
||||
? const Icon(
|
||||
Icons.person_rounded,
|
||||
color: Colors.white,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
_greeting(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white.withValues(
|
||||
alpha: 0.85,
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
_greeting(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white.withValues(
|
||||
alpha: 0.85,
|
||||
),
|
||||
fontSize: 12,
|
||||
),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
shownName,
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 19,
|
||||
Text(
|
||||
shownName,
|
||||
textAlign: TextAlign.left,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
fontSize: 19,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -442,22 +445,35 @@ class _LoggedHomeScreenState extends State<LoggedHomeScreen>
|
||||
),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
currentIndex: _index,
|
||||
onTap: (i) => setState(() => _index = i),
|
||||
onTap: (i) {
|
||||
if (i == _index) return;
|
||||
HapticFeedback.selectionClick();
|
||||
setState(() => _index = i);
|
||||
},
|
||||
backgroundColor: const Color(0xFFFFE6F1),
|
||||
selectedItemColor: _teal,
|
||||
unselectedItemColor: Colors.black54,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
items: const [
|
||||
items: [
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.home_rounded),
|
||||
icon: AnimatedNavIcon(
|
||||
icon: Icons.home_rounded,
|
||||
selected: _index == 0,
|
||||
),
|
||||
label: 'Início',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.person_rounded),
|
||||
icon: AnimatedNavIcon(
|
||||
icon: Icons.person_rounded,
|
||||
selected: _index == 1,
|
||||
),
|
||||
label: 'Perfil',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.settings_rounded),
|
||||
icon: AnimatedNavIcon(
|
||||
icon: Icons.settings_rounded,
|
||||
selected: _index == 2,
|
||||
),
|
||||
label: 'Ajustes',
|
||||
),
|
||||
],
|
||||
@@ -485,7 +501,7 @@ class _RiskArcGauge extends StatelessWidget {
|
||||
width: 120,
|
||||
height: 60,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: CustomPaint(
|
||||
@@ -493,29 +509,19 @@ class _RiskArcGauge extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'$shown%',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w900,
|
||||
height: 1,
|
||||
),
|
||||
top: 38,
|
||||
left: 6,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: Text(
|
||||
'$shown%',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w900,
|
||||
height: 1,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: 0.92),
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -630,19 +636,30 @@ class _InicioTab extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_HeroQuizCard(
|
||||
childName: selectedChildName,
|
||||
onStartQuiz: () => _startQuiz(context),
|
||||
FadeSlideIn(
|
||||
child: TapBounce(
|
||||
scale: 0.97,
|
||||
child: _HeroQuizCard(
|
||||
childName: selectedChildName,
|
||||
onStartQuiz: () => _startQuiz(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_VideoLibraryCard(
|
||||
onOpenLibrary: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => const VideoScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
FadeSlideIn(
|
||||
delay: const Duration(milliseconds: 90),
|
||||
child: TapBounce(
|
||||
scale: 0.97,
|
||||
child: _VideoLibraryCard(
|
||||
onOpenLibrary: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (_) => const VideoScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
@@ -741,7 +758,9 @@ Future<Map<String, dynamic>?> _pickChildSheet(
|
||||
final label = age != null ? '$name • $age anos' : name;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Material(
|
||||
child: TapBounce(
|
||||
scale: 0.97,
|
||||
child: Material(
|
||||
color: Colors.white.withValues(alpha: 0.85),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: InkWell(
|
||||
@@ -770,6 +789,7 @@ Future<Map<String, dynamic>?> _pickChildSheet(
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -1327,7 +1347,8 @@ class _PerfilTabState extends State<_PerfilTab> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Material(
|
||||
FadeSlideIn(
|
||||
child: Material(
|
||||
elevation: 10,
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@@ -1337,7 +1358,9 @@ class _PerfilTabState extends State<_PerfilTab> {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
TapBounce(
|
||||
scale: 0.92,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
onTap: _updatingPhoto
|
||||
? null
|
||||
@@ -1415,6 +1438,7 @@ class _PerfilTabState extends State<_PerfilTab> {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -1453,6 +1477,7 @@ class _PerfilTabState extends State<_PerfilTab> {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4, bottom: 10),
|
||||
@@ -1554,9 +1579,15 @@ class _PerfilTabState extends State<_PerfilTab> {
|
||||
].join(' • ');
|
||||
final bool selected = i == selectedIndex;
|
||||
|
||||
return Padding(
|
||||
return FadeSlideIn(
|
||||
delay: Duration(
|
||||
milliseconds: 60 * i.clamp(0, 6),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: InkWell(
|
||||
child: TapBounce(
|
||||
scale: 0.97,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
onTap: () => widget.onChildSelected(
|
||||
i,
|
||||
@@ -1662,47 +1693,53 @@ class _PerfilTabState extends State<_PerfilTab> {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: FilledButton.icon(
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2F9E94),
|
||||
foregroundColor: Colors.white,
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
TapBounce(
|
||||
child: SizedBox(
|
||||
height: 48,
|
||||
child: FilledButton.icon(
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF2F9E94),
|
||||
foregroundColor: Colors.white,
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
onPressed: _addingChild
|
||||
? null
|
||||
: () => _addAnotherChild(context, uid),
|
||||
icon: const Icon(Icons.add_rounded),
|
||||
label: const Text('Adicionar criança'),
|
||||
),
|
||||
onPressed: _addingChild
|
||||
? null
|
||||
: () => _addAnotherChild(context, uid),
|
||||
icon: const Icon(Icons.add_rounded),
|
||||
label: const Text('Adicionar criança'),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
SizedBox(
|
||||
height: 46,
|
||||
child: OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFFF55A7),
|
||||
side: const BorderSide(
|
||||
color: Color(0xFFFF55A7),
|
||||
width: 1.4,
|
||||
),
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
TapBounce(
|
||||
child: SizedBox(
|
||||
height: 46,
|
||||
child: OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFFF55A7),
|
||||
side: const BorderSide(
|
||||
color: Color(0xFFFF55A7),
|
||||
width: 1.4,
|
||||
),
|
||||
shape: const StadiumBorder(),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
await supabase.auth.signOut();
|
||||
},
|
||||
icon: const Icon(Icons.logout_rounded),
|
||||
label: const Text('Sair'),
|
||||
),
|
||||
onPressed: () async {
|
||||
await supabase.auth.signOut();
|
||||
},
|
||||
icon: const Icon(Icons.logout_rounded),
|
||||
label: const Text('Sair'),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -1844,17 +1881,21 @@ class _AddChildSheetState extends State<_AddChildSheet> {
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
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),
|
||||
child: 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: _submit,
|
||||
child: const Text('Adicionar'),
|
||||
),
|
||||
onPressed: _submit,
|
||||
child: const Text('Adicionar'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user