melhorar o navigation e o separar controlls e widgetes e pages

main
230404 2025-12-03 10:31:47 +00:00
parent e16191fa9d
commit dc3a7840bb
11 changed files with 545 additions and 473 deletions

View File

@ -1,15 +0,0 @@
import 'dart:async';
class HomeController {
Future<void> loadHomeData() async {
await Future.delayed(const Duration(milliseconds: 500));
}
void refreshData() {
loadHomeData();
}
List<String> getFilterOptions() {
return ['Últimos 7 dias', 'Últimos 30 dias', 'Temporada completa'];
}
}

View File

@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
), ),
useMaterial3: true, useMaterial3: true,
), ),
home: LoginPage(), home: LoginPage(),
); );
} }
} }

View File

@ -1,173 +1,215 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'widgets/player_stat_card.dart';
import 'widgets/wins_losses_chart.dart';
import 'widgets/recent_game_card.dart';
import 'widgets/game_highlights.dart';
import 'widgets/bottom_nav_bar.dart';
import '../controllers/home_controller.dart';
class Home extends StatefulWidget { class HomeScreen extends StatelessWidget {
const Home({super.key}); const HomeScreen({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final HomeController _controller = HomeController();
int _currentIndex = 0;
@override
void initState() {
super.initState();
_controller.loadHomeData();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar( appBar: AppBar(
title: const Text( title: const Text('PlayMaker'),
'Home', backgroundColor: Colors.orange,
style: TextStyle( foregroundColor: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
backgroundColor: Colors.white,
elevation: 0,
centerTitle: true,
), ),
body: Padding( body: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Padding(
child: SingleChildScrollView( padding: const EdgeInsets.all(40.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// Team Header // Título
Padding( const Text(
padding: const EdgeInsets.only(top: 8.0, bottom: 16.0), 'PLAYMAKER',
child: Text( style: TextStyle(
'TEAM A', fontSize: 32,
style: TextStyle( fontWeight: FontWeight.bold,
fontSize: 18, color: Colors.orange,
fontWeight: FontWeight.w600, letterSpacing: 2,
color: Colors.grey[700], ),
),
const SizedBox(height: 40),
// Botão PRINCIPAL para iniciar jogo
SizedBox(
width: double.infinity,
height: 250,
child: ElevatedButton(
onPressed: () {
// Navegar para tela de novo jogo
print('Iniciar novo jogo!');
// Navigator.push(context, MaterialPageRoute(
// builder: (context) => NovoJogoScreen()
// ));
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 10,
shadowColor: Colors.orange.withOpacity(0.5),
),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.sports_basketball,
size: 80,
color: Colors.white,
),
SizedBox(height: 20),
Text(
'NOVO JOGO',
style: TextStyle(
fontSize: 38,
fontWeight: FontWeight.w900,
color: Colors.white,
letterSpacing: 2,
),
),
],
), ),
), ),
), ),
// Best Players Section const SizedBox(height: 40),
// Texto explicativo
const Text( const Text(
'Melhores Jogadores', 'Toque para iniciar uma nova partida',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, color: Colors.grey,
color: Colors.black, fontStyle: FontStyle.italic,
), ),
), ),
const SizedBox(height: 12),
const SizedBox(height: 50),
// Player Stats Cards
Row( // Seção de opções rápidas
children: [
Expanded(
child: PlayerStatCard(
title: 'Mais Pontos',
playerName: 'Michael Jordan',
statValue: '34.5',
statType: 'PPG',
icon: Icons.sports_basketball,
color: Colors.blue,
),
),
const SizedBox(width: 12),
Expanded(
child: PlayerStatCard(
title: 'Mais Assistências',
playerName: 'Magic Johnson',
statValue: '12.8',
statType: 'APG',
icon: Icons.share,
color: Colors.green,
),
),
const SizedBox(width: 12),
Expanded(
child: PlayerStatCard(
title: 'Mais Rebotes',
playerName: 'Dennis Rodman',
statValue: '15.3',
statType: 'RPG',
icon: Icons.vertical_align_center,
color: Colors.orange,
),
),
],
),
const SizedBox(height: 24),
// Wins vs Losses
WinsLossesChart(
wins: 12,
losses: 5,
totalGames: 17,
),
const SizedBox(height: 24),
// Recent History
const Text( const Text(
'Histórico Recente', 'Acesso Rápido',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.black, color: Colors.orange,
), ),
), ),
const SizedBox(height: 12),
RecentGameCard(
teamA: 'TEAM A',
teamB: 'TEAM B',
scoreA: 91,
scoreB: 88,
quarter: '',
timeLeft: '10:00',
date: '13/06/25',
),
const SizedBox(height: 24),
// Game Highlights
const Text(
'Destaques do Jogo',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const SizedBox(height: 12),
GameHighlights(
players: [
PlayerHighlight(name: 'Fred', stat: '13,0 rebotes'),
PlayerHighlight(name: 'Afonso', stat: '20,0 pontos'),
],
),
const SizedBox(height: 20), const SizedBox(height: 20),
// Grid de opções
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2,
childAspectRatio: 1.3,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
children: [
// Estatísticas
_buildQuickOption(
icon: Icons.insights,
title: 'Estatísticas',
subtitle: 'Ver desempenho',
color: Colors.green,
onTap: () {
print('Ver estatísticas');
// Pode navegar para estatísticas ou mudar para índice 3 no navigation
},
),
// Equipas
_buildQuickOption(
icon: Icons.people,
title: 'Minhas Equipas',
subtitle: 'Gerir equipas',
color: Colors.blue,
onTap: () {
print('Ver equipas');
// Pode navegar para equipas ou mudar para índice 2 no navigation
},
),
// Histórico
_buildQuickOption(
icon: Icons.history,
title: 'Histórico',
subtitle: 'Jogos anteriores',
color: Colors.purple,
onTap: () {
print('Ver histórico');
},
),
// Configurações
_buildQuickOption(
icon: Icons.settings,
title: 'Configurações',
subtitle: 'Ajustar app',
color: Colors.grey,
onTap: () {
print('Abrir configurações');
},
),
],
),
], ],
), ),
), ),
), ),
bottomNavigationBar: BottomNavBar( );
currentIndex: _currentIndex, }
onTap: (index) {
setState(() { // Widget para construir opções rápidas
_currentIndex = index; Widget _buildQuickOption({
}); required IconData icon,
}, required String title,
required String subtitle,
required Color color,
required VoidCallback onTap,
}) {
return Card(
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: InkWell(
borderRadius: BorderRadius.circular(15),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 40,
color: color,
),
const SizedBox(height: 10),
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: color,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 5),
Text(
subtitle,
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
),
textAlign: TextAlign.center,
),
],
),
),
), ),
); );
} }

View File

@ -1,79 +1,82 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../widgets/login_widgets.dart'; import 'package:playmaker/controllers/home_controller.dart';
import '../../Controllers/login_controller.dart'; import 'package:playmaker/pages/home.dart';
import '../widgets/login_widgets.dart';
import '../../Controllers/login_controller.dart';
class LoginPage extends StatefulWidget { class LoginPage extends StatefulWidget {
const LoginPage({super.key}); const LoginPage({super.key});
@override @override
State<LoginPage> createState() => _LoginPageState(); State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final LoginController controller = LoginController();
@override
void dispose() {
controller.dispose();
super.dispose();
} }
@override class _LoginPageState extends State<LoginPage> {
Widget build(BuildContext context) { final LoginController controller = LoginController();
return Scaffold(
backgroundColor: Colors.white, @override
body: SafeArea( void dispose() {
child: LayoutBuilder( controller.dispose();
builder: (context, constraints) { super.dispose();
final screenWidth = constraints.maxWidth; }
final screenHeight = constraints.maxHeight;
@override
return Center( Widget build(BuildContext context) {
child: Container( return Scaffold(
width: screenWidth > 800 ? 600.0 : backgroundColor: Colors.white,
screenWidth > 600 ? 500.0 : 400.0, body: SafeArea(
height: screenHeight, // USA A ALTURA TOTAL child: LayoutBuilder(
padding: const EdgeInsets.all(32), builder: (context, constraints) {
child: Column( final screenWidth = constraints.maxWidth;
mainAxisAlignment: MainAxisAlignment.center, // CENTRALIZA VERTICALMENTE final screenHeight = constraints.maxHeight;
children: [
const Expanded( // EXPANDE PARA USAR ESPAÇO return Center(
flex: 2, child: Container(
child: SizedBox(), width: screenWidth > 800 ? 600.0 :
), screenWidth > 600 ? 500.0 : 400.0,
height: screenHeight, // USA A ALTURA TOTAL
const BasketTrackHeader(), padding: const EdgeInsets.all(32),
const SizedBox(height: 40), child: Column(
mainAxisAlignment: MainAxisAlignment.center, // CENTRALIZA VERTICALMENTE
LoginFormFields(controller: controller), children: [
const SizedBox(height: 24), const Expanded( // EXPANDE PARA USAR ESPAÇO
flex: 2,
LoginButton( child: SizedBox(),
controller: controller, ),
onLoginSuccess: () {
ScaffoldMessenger.of(context).showSnackBar( const BasketTrackHeader(),
const SnackBar( const SizedBox(height: 40),
content: Text('Login bem-sucedido!'),
backgroundColor: Colors.green, LoginFormFields(controller: controller),
), const SizedBox(height: 24),
);
}, LoginButton(
), controller: controller,
const SizedBox(height: 16), onLoginSuccess: () {
Navigator.pushReplacement(
const CreateAccountButton(), context,
MaterialPageRoute(
const Expanded( // EXPANDE PARA USAR ESPAÇO builder: (context) => const HomeScreen(),
flex: 3,
child: SizedBox(), ),
), );
], },
),
const SizedBox(height: 16),
const CreateAccountButton(),
const Expanded( // EXPANDE PARA USAR ESPAÇO
flex: 3,
child: SizedBox(),
),
],
),
), ),
), );
); },
}, ),
), ),
), );
); }
} }
}

View File

@ -1 +0,0 @@
// TODO Implement this library.

View File

@ -1 +0,0 @@
// TODO Implement this library.

View File

@ -1 +0,0 @@
// TODO Implement this library.

View File

@ -1 +0,0 @@
// TODO Implement this library.

View File

@ -1 +0,0 @@
// TODO Implement this library.

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
class CustomNavBar extends StatelessWidget {
final int selectedIndex;
final Function(int) onItemSelected;
const CustomNavBar({
super.key,
required this.selectedIndex,
required this.onItemSelected,
});
@override
Widget build(BuildContext context) {
// Usar NavigationBar (Material 3) ao invés de BottomNavigationBar
return NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: onItemSelected,
backgroundColor: Theme.of(context).colorScheme.surface,
surfaceTintColor: Theme.of(context).colorScheme.surfaceTint,
elevation: 1,
height: 70,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home_filled),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.sports_soccer_outlined),
selectedIcon: Icon(Icons.sports_soccer),
label: 'Jogo',
),
NavigationDestination(
icon: Icon(Icons.people_outline),
selectedIcon: Icon(Icons.people),
label: 'Equipas',
),
NavigationDestination(
icon: Icon(Icons.insights_outlined),
selectedIcon: Icon(Icons.insights),
label: 'Status',
),
],
);
}
}

View File

@ -1,262 +1,262 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:playmaker/Controllers/login_controller.dart'; import 'package:playmaker/Controllers/login_controller.dart';
class BasketTrackHeader extends StatelessWidget { class BasketTrackHeader extends StatelessWidget {
const BasketTrackHeader({super.key}); const BasketTrackHeader({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width; final screenWidth = MediaQuery.of(context).size.width;
// TAMANHOS AUMENTADOS para tablets // TAMANHOS AUMENTADOS para tablets
final logoSize = screenWidth > 600 ? 400.0 : 300.0; // Aumentado final logoSize = screenWidth > 600 ? 400.0 : 300.0; // Aumentado
final titleFontSize = screenWidth > 600 ? 48.0 : 36.0; // Aumentado final titleFontSize = screenWidth > 600 ? 48.0 : 36.0; // Aumentado
final subtitleFontSize = screenWidth > 600 ? 22.0 : 18.0; // Aumentado final subtitleFontSize = screenWidth > 600 ? 22.0 : 18.0; // Aumentado
return Column( return Column(
children: [ children: [
Container( Container(
width: logoSize, width: logoSize,
height: logoSize, height: logoSize,
child: Image.asset( child: Image.asset(
'assets/playmaker-logo.png', 'assets/playmaker-logo.png',
fit: BoxFit.contain, fit: BoxFit.contain,
),
), ),
), SizedBox(height: screenWidth > 600 ? 1.0 : 1.0),
SizedBox(height: screenWidth > 600 ? 1.0 : 1.0),
Text(
Text( 'BasketTrack',
'BasketTrack', style: TextStyle(
style: TextStyle( fontSize: titleFontSize,
fontSize: titleFontSize, fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, color: Colors.grey[900],
color: Colors.grey[900], ),
), ),
), SizedBox(height: screenWidth > 600 ? 1.0 : 1.0),
SizedBox(height: screenWidth > 600 ? 1.0 : 1.0),
Text(
Text( 'Gere as tuas equipas e estatísticas',
'Gere as tuas equipas e estatísticas', style: TextStyle(
style: TextStyle( fontSize: subtitleFontSize,
fontSize: subtitleFontSize, color: Colors.grey[600],
color: Colors.grey[600], fontWeight: FontWeight.w500, // Adicionado peso da fonte
fontWeight: FontWeight.w500, // Adicionado peso da fonte ),
textAlign: TextAlign.center,
), ),
textAlign: TextAlign.center, ],
), );
], }
);
} }
}
class LoginFormFields extends StatelessWidget { class LoginFormFields extends StatelessWidget {
final LoginController controller; final LoginController controller;
const LoginFormFields({super.key, required this.controller}); const LoginFormFields({super.key, required this.controller});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width; final screenWidth = MediaQuery.of(context).size.width;
// TAMANHOS AUMENTADOS // TAMANHOS AUMENTADOS
final verticalPadding = screenWidth > 600 ? 26.0 : 20.0; // Aumentado final verticalPadding = screenWidth > 600 ? 26.0 : 20.0; // Aumentado
final spacing = screenWidth > 600 ? 28.0 : 20.0; // Aumentado final spacing = screenWidth > 600 ? 28.0 : 20.0; // Aumentado
final labelFontSize = screenWidth > 600 ? 18.0 : 16.0; // Aumentado final labelFontSize = screenWidth > 600 ? 18.0 : 16.0; // Aumentado
final textFontSize = screenWidth > 600 ? 18.0 : 16.0; // Aumentado final textFontSize = screenWidth > 600 ? 18.0 : 16.0; // Aumentado
return Column( return Column(
children: [ children: [
TextField( TextField(
controller: controller.emailController, controller: controller.emailController,
style: TextStyle(fontSize: textFontSize), // Tamanho do texto style: TextStyle(fontSize: textFontSize), // Tamanho do texto
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'E-mail', labelText: 'E-mail',
labelStyle: TextStyle(fontSize: labelFontSize), // Tamanho do label labelStyle: TextStyle(fontSize: labelFontSize), // Tamanho do label
prefixIcon: Icon(Icons.email_outlined, size: 24), // Ícone maior prefixIcon: Icon(Icons.email_outlined, size: 24), // Ícone maior
errorText: controller.emailError, errorText: controller.emailError,
errorStyle: TextStyle(fontSize: 14), // Tamanho do erro errorStyle: TextStyle(fontSize: 14), // Tamanho do erro
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[400]!), borderSide: BorderSide(color: Colors.grey[400]!),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[400]!),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFE74C3C), width: 2),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 18, // Aumentado
vertical: verticalPadding,
),
),
keyboardType: TextInputType.emailAddress,
onChanged: (_) {
if (controller.emailError != null) {
controller.validateEmail(controller.emailController.text);
}
},
),
SizedBox(height: spacing),
TextField(
controller: controller.passwordController,
style: TextStyle(fontSize: textFontSize), // Tamanho do texto
decoration: InputDecoration(
labelText: 'Palavra-passe',
labelStyle: TextStyle(fontSize: labelFontSize), // Tamanho do label
prefixIcon: Icon(Icons.lock_outlined, size: 24), // Ícone maior
suffixIcon: IconButton(
icon: Icon(
controller.obscurePassword
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
color: Colors.grey[600],
size: 24, // Ícone maior
), ),
onPressed: controller.togglePasswordVisibility, enabledBorder: OutlineInputBorder(
), borderRadius: BorderRadius.circular(12),
errorText: controller.passwordError, borderSide: BorderSide(color: Colors.grey[400]!),
errorStyle: TextStyle(fontSize: 14), // Tamanho do erro ),
border: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[400]!), borderSide: const BorderSide(color: Color(0xFFE74C3C), width: 2),
), ),
enabledBorder: OutlineInputBorder( contentPadding: EdgeInsets.symmetric(
borderRadius: BorderRadius.circular(12), horizontal: 18, // Aumentado
borderSide: BorderSide(color: Colors.grey[400]!), vertical: verticalPadding,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFE74C3C), width: 2),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 18, // Aumentado
vertical: verticalPadding,
),
),
obscureText: controller.obscurePassword,
onChanged: (_) {
if (controller.passwordError != null) {
controller.validatePassword(controller.passwordController.text);
}
},
),
SizedBox(height: screenWidth > 600 ? 20.0 : 14.0), // Aumentado
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), // Mais espaço
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Text(
'Recuperar Palavra-passe',
style: TextStyle(
fontSize: screenWidth > 600 ? 18.0 : 15.0, // Aumentado
color: const Color(0xFFE74C3C),
fontWeight: FontWeight.w600, // Mais negrito
), ),
), ),
keyboardType: TextInputType.emailAddress,
onChanged: (_) {
if (controller.emailError != null) {
controller.validateEmail(controller.emailController.text);
}
},
), ),
), SizedBox(height: spacing),
],
); TextField(
} controller: controller.passwordController,
} style: TextStyle(fontSize: textFontSize), // Tamanho do texto
decoration: InputDecoration(
class LoginButton extends StatelessWidget { labelText: 'Palavra-passe',
final LoginController controller; labelStyle: TextStyle(fontSize: labelFontSize), // Tamanho do label
final VoidCallback onLoginSuccess; prefixIcon: Icon(Icons.lock_outlined, size: 24), // Ícone maior
suffixIcon: IconButton(
const LoginButton({ icon: Icon(
super.key, controller.obscurePassword
required this.controller, ? Icons.visibility_outlined
required this.onLoginSuccess, : Icons.visibility_off_outlined,
}); color: Colors.grey[600],
size: 24, // Ícone maior
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
// BOTÕES MAIORES
final buttonHeight = screenWidth > 600 ? 70.0 : 58.0; // Aumentado
final fontSize = screenWidth > 600 ? 22.0 : 18.0; // Aumentado
return SizedBox(
width: double.infinity,
height: buttonHeight,
child: ElevatedButton(
onPressed: controller.isLoading ? null : () async {
final success = await controller.login();
if (success) {
onLoginSuccess();
}
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE74C3C),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14), // Bordas mais arredondadas
),
elevation: 3, // Sombra mais pronunciada
),
child: controller.isLoading
? SizedBox(
width: 28, // Aumentado
height: 28, // Aumentado
child: CircularProgressIndicator(
strokeWidth: 3, // Aumentado
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
), ),
) onPressed: controller.togglePasswordVisibility,
: Text( ),
'Entrar', errorText: controller.passwordError,
errorStyle: TextStyle(fontSize: 14), // Tamanho do erro
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[400]!),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[400]!),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFE74C3C), width: 2),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 18, // Aumentado
vertical: verticalPadding,
),
),
obscureText: controller.obscurePassword,
onChanged: (_) {
if (controller.passwordError != null) {
controller.validatePassword(controller.passwordController.text);
}
},
),
SizedBox(height: screenWidth > 600 ? 20.0 : 14.0), // Aumentado
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), // Mais espaço
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: Text(
'Recuperar Palavra-passe',
style: TextStyle( style: TextStyle(
fontSize: fontSize, fontSize: screenWidth > 600 ? 18.0 : 15.0, // Aumentado
fontWeight: FontWeight.w700, // color: const Color(0xFFE74C3C),
fontWeight: FontWeight.w600, // Mais negrito
), ),
), ),
), ),
); ),
],
);
}
} }
}
class CreateAccountButton extends StatelessWidget { class LoginButton extends StatelessWidget {
const CreateAccountButton({super.key}); final LoginController controller;
final VoidCallback onLoginSuccess;
@override const LoginButton({
Widget build(BuildContext context) { super.key,
final screenWidth = MediaQuery.of(context).size.width; required this.controller,
required this.onLoginSuccess,
// BOTÃO MAIOR });
final buttonHeight = screenWidth > 600 ? 70.0 : 58.0; // Aumentado
final fontSize = screenWidth > 600 ? 22.0 : 18.0; // Aumentado
return SizedBox( @override
width: double.infinity, Widget build(BuildContext context) {
height: buttonHeight, final screenWidth = MediaQuery.of(context).size.width;
child: OutlinedButton(
onPressed: () {}, // BOTÕES MAIORES
style: OutlinedButton.styleFrom( final buttonHeight = screenWidth > 600 ? 70.0 : 58.0; // Aumentado
foregroundColor: const Color(0xFFE74C3C), final fontSize = screenWidth > 600 ? 22.0 : 18.0; // Aumentado
side: const BorderSide(color: Color(0xFFE74C3C), width: 2), // Borda mais grossa
shape: RoundedRectangleBorder( return SizedBox(
borderRadius: BorderRadius.circular(14), // Bordas mais arredondadas width: double.infinity,
height: buttonHeight,
child: ElevatedButton(
onPressed: controller.isLoading ? null : () async {
final success = await controller.login();
if (success) {
onLoginSuccess();
}
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE74C3C),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14), // Bordas mais arredondadas
),
elevation: 3, // Sombra mais pronunciada
),
child: controller.isLoading
? SizedBox(
width: 28, // Aumentado
height: 28, // Aumentado
child: CircularProgressIndicator(
strokeWidth: 3, // Aumentado
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'Entrar',
style: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.w700, //
),
),
),
);
}
}
class CreateAccountButton extends StatelessWidget {
const CreateAccountButton({super.key});
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
// BOTÃO MAIOR
final buttonHeight = screenWidth > 600 ? 70.0 : 58.0; // Aumentado
final fontSize = screenWidth > 600 ? 22.0 : 18.0; // Aumentado
return SizedBox(
width: double.infinity,
height: buttonHeight,
child: OutlinedButton(
onPressed: () {},
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFFE74C3C),
side: const BorderSide(color: Color(0xFFE74C3C), width: 2), // Borda mais grossa
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14), // Bordas mais arredondadas
),
),
child: Text(
'Criar Conta',
style: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.w700, // Mais negrito
),
), ),
), ),
child: Text( );
'Criar Conta', }
style: TextStyle( }
fontSize: fontSize,
fontWeight: FontWeight.w700, // Mais negrito
),
),
),
);
}
}