diff --git a/lib/controllers/home_controller.dart b/lib/controllers/home_controller.dart index eb43158..e69de29 100644 --- a/lib/controllers/home_controller.dart +++ b/lib/controllers/home_controller.dart @@ -1,15 +0,0 @@ -import 'dart:async'; - -class HomeController { - Future loadHomeData() async { - await Future.delayed(const Duration(milliseconds: 500)); - } - - void refreshData() { - loadHomeData(); - } - - List getFilterOptions() { - return ['Últimos 7 dias', 'Últimos 30 dias', 'Temporada completa']; - } -} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 7a5b4fa..c0fd229 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,7 +18,7 @@ class MyApp extends StatelessWidget { ), useMaterial3: true, ), - home: LoginPage(), + home: LoginPage(), ); } } \ No newline at end of file diff --git a/lib/pages/home.dart b/lib/pages/home.dart index 137fd5c..4c6d17b 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -1,173 +1,215 @@ 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 { - const Home({super.key}); - - @override - State createState() => _HomeState(); -} - -class _HomeState extends State { - final HomeController _controller = HomeController(); - int _currentIndex = 0; - - @override - void initState() { - super.initState(); - _controller.loadHomeData(); - } +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, appBar: AppBar( - title: const Text( - 'Home', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - backgroundColor: Colors.white, - elevation: 0, - centerTitle: true, + title: const Text('PlayMaker'), + backgroundColor: Colors.orange, + foregroundColor: Colors.white, ), - body: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: SingleChildScrollView( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(40.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ - // Team Header - Padding( - padding: const EdgeInsets.only(top: 8.0, bottom: 16.0), - child: Text( - 'TEAM A', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.grey[700], + // Título + const Text( + 'PLAYMAKER', + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: Colors.orange, + letterSpacing: 2, + ), + ), + 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( - 'Melhores Jogadores', + 'Toque para iniciar uma nova partida', style: TextStyle( fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black, + color: Colors.grey, + fontStyle: FontStyle.italic, ), ), - const SizedBox(height: 12), - - // Player Stats Cards - Row( - 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 SizedBox(height: 50), + + // Seção de opções rápidas const Text( - 'Histórico Recente', + 'Acesso Rápido', style: TextStyle( - fontSize: 16, + fontSize: 22, 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: '2º', - 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), + + // 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(() { - _currentIndex = index; - }); - }, + ); + } + + // Widget para construir opções rápidas + 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, + ), + ], + ), + ), ), ); } diff --git a/lib/pages/login.dart b/lib/pages/login.dart index 8bd7b90..d3aea7c 100644 --- a/lib/pages/login.dart +++ b/lib/pages/login.dart @@ -1,79 +1,82 @@ -import 'package:flutter/material.dart'; -import '../widgets/login_widgets.dart'; -import '../../Controllers/login_controller.dart'; + import 'package:flutter/material.dart'; + import 'package:playmaker/controllers/home_controller.dart'; + import 'package:playmaker/pages/home.dart'; + import '../widgets/login_widgets.dart'; + import '../../Controllers/login_controller.dart'; -class LoginPage extends StatefulWidget { - const LoginPage({super.key}); + class LoginPage extends StatefulWidget { + const LoginPage({super.key}); - @override - State createState() => _LoginPageState(); -} - -class _LoginPageState extends State { - final LoginController controller = LoginController(); - - @override - void dispose() { - controller.dispose(); - super.dispose(); + @override + State createState() => _LoginPageState(); } - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - body: SafeArea( - child: LayoutBuilder( - builder: (context, constraints) { - final screenWidth = constraints.maxWidth; - final screenHeight = constraints.maxHeight; - - return Center( - child: Container( - width: screenWidth > 800 ? 600.0 : - screenWidth > 600 ? 500.0 : 400.0, - height: screenHeight, // ← USA A ALTURA TOTAL - padding: const EdgeInsets.all(32), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, // ← CENTRALIZA VERTICALMENTE - children: [ - const Expanded( // ← EXPANDE PARA USAR ESPAÇO - flex: 2, - child: SizedBox(), - ), - - const BasketTrackHeader(), - const SizedBox(height: 40), - - LoginFormFields(controller: controller), - const SizedBox(height: 24), - - LoginButton( - controller: controller, - onLoginSuccess: () { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Login bem-sucedido!'), - backgroundColor: Colors.green, - ), - ); - }, - ), - const SizedBox(height: 16), - - const CreateAccountButton(), - - const Expanded( // ← EXPANDE PARA USAR ESPAÇO - flex: 3, - child: SizedBox(), - ), - ], + class _LoginPageState extends State { + final LoginController controller = LoginController(); + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: SafeArea( + child: LayoutBuilder( + builder: (context, constraints) { + final screenWidth = constraints.maxWidth; + final screenHeight = constraints.maxHeight; + + return Center( + child: Container( + width: screenWidth > 800 ? 600.0 : + screenWidth > 600 ? 500.0 : 400.0, + height: screenHeight, // ← USA A ALTURA TOTAL + padding: const EdgeInsets.all(32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, // ← CENTRALIZA VERTICALMENTE + children: [ + const Expanded( // ← EXPANDE PARA USAR ESPAÇO + flex: 2, + child: SizedBox(), + ), + + const BasketTrackHeader(), + const SizedBox(height: 40), + + LoginFormFields(controller: controller), + const SizedBox(height: 24), + + LoginButton( + controller: controller, + onLoginSuccess: () { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => const HomeScreen(), + + ), + ); + }, + ), + const SizedBox(height: 16), + + const CreateAccountButton(), + + const Expanded( // ← EXPANDE PARA USAR ESPAÇO + flex: 3, + child: SizedBox(), + ), + ], + ), ), - ), - ); - }, + ); + }, + ), ), - ), - ); - } -} \ No newline at end of file + ); + } + } \ No newline at end of file diff --git a/lib/pages/widgets/bottom_nav_bar.dart b/lib/pages/widgets/bottom_nav_bar.dart deleted file mode 100644 index b9f78bb..0000000 --- a/lib/pages/widgets/bottom_nav_bar.dart +++ /dev/null @@ -1 +0,0 @@ -// TODO Implement this library. \ No newline at end of file diff --git a/lib/pages/widgets/game_highlights.dart b/lib/pages/widgets/game_highlights.dart deleted file mode 100644 index b9f78bb..0000000 --- a/lib/pages/widgets/game_highlights.dart +++ /dev/null @@ -1 +0,0 @@ -// TODO Implement this library. \ No newline at end of file diff --git a/lib/pages/widgets/player_stat_card.dart b/lib/pages/widgets/player_stat_card.dart deleted file mode 100644 index b9f78bb..0000000 --- a/lib/pages/widgets/player_stat_card.dart +++ /dev/null @@ -1 +0,0 @@ -// TODO Implement this library. \ No newline at end of file diff --git a/lib/pages/widgets/recent_game_card.dart b/lib/pages/widgets/recent_game_card.dart deleted file mode 100644 index b9f78bb..0000000 --- a/lib/pages/widgets/recent_game_card.dart +++ /dev/null @@ -1 +0,0 @@ -// TODO Implement this library. \ No newline at end of file diff --git a/lib/pages/widgets/wins_losses_chart.dart b/lib/pages/widgets/wins_losses_chart.dart deleted file mode 100644 index b9f78bb..0000000 --- a/lib/pages/widgets/wins_losses_chart.dart +++ /dev/null @@ -1 +0,0 @@ -// TODO Implement this library. \ No newline at end of file diff --git a/lib/widgets/custom_nav_bar.dart b/lib/widgets/custom_nav_bar.dart new file mode 100644 index 0000000..130c585 --- /dev/null +++ b/lib/widgets/custom_nav_bar.dart @@ -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', + ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/widgets/login_widgets.dart b/lib/widgets/login_widgets.dart index a257219..5b32a08 100644 --- a/lib/widgets/login_widgets.dart +++ b/lib/widgets/login_widgets.dart @@ -1,262 +1,262 @@ -import 'package:flutter/material.dart'; -import 'package:playmaker/Controllers/login_controller.dart'; + import 'package:flutter/material.dart'; + import 'package:playmaker/Controllers/login_controller.dart'; -class BasketTrackHeader extends StatelessWidget { - const BasketTrackHeader({super.key}); + class BasketTrackHeader extends StatelessWidget { + const BasketTrackHeader({super.key}); - @override - Widget build(BuildContext context) { - final screenWidth = MediaQuery.of(context).size.width; - - // TAMANHOS AUMENTADOS para tablets - final logoSize = screenWidth > 600 ? 400.0 : 300.0; // ↑ Aumentado - final titleFontSize = screenWidth > 600 ? 48.0 : 36.0; // ↑ Aumentado - final subtitleFontSize = screenWidth > 600 ? 22.0 : 18.0; // ↑ Aumentado + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + + // TAMANHOS AUMENTADOS para tablets + final logoSize = screenWidth > 600 ? 400.0 : 300.0; // ↑ Aumentado + final titleFontSize = screenWidth > 600 ? 48.0 : 36.0; // ↑ Aumentado + final subtitleFontSize = screenWidth > 600 ? 22.0 : 18.0; // ↑ Aumentado - return Column( - children: [ - Container( - width: logoSize, - height: logoSize, - child: Image.asset( - 'assets/playmaker-logo.png', - fit: BoxFit.contain, + return Column( + children: [ + Container( + width: logoSize, + height: logoSize, + child: Image.asset( + 'assets/playmaker-logo.png', + fit: BoxFit.contain, + ), ), - ), - SizedBox(height: screenWidth > 600 ? 1.0 : 1.0), - - Text( - 'BasketTrack', - style: TextStyle( - fontSize: titleFontSize, - fontWeight: FontWeight.bold, - color: Colors.grey[900], + SizedBox(height: screenWidth > 600 ? 1.0 : 1.0), + + Text( + 'BasketTrack', + style: TextStyle( + fontSize: titleFontSize, + fontWeight: FontWeight.bold, + color: Colors.grey[900], + ), ), - ), - SizedBox(height: screenWidth > 600 ? 1.0 : 1.0), - - Text( - 'Gere as tuas equipas e estatísticas', - style: TextStyle( - fontSize: subtitleFontSize, - color: Colors.grey[600], - fontWeight: FontWeight.w500, // ↑ Adicionado peso da fonte + SizedBox(height: screenWidth > 600 ? 1.0 : 1.0), + + Text( + 'Gere as tuas equipas e estatísticas', + style: TextStyle( + fontSize: subtitleFontSize, + color: Colors.grey[600], + fontWeight: FontWeight.w500, // ↑ Adicionado peso da fonte + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, - ), - ], - ); + ], + ); + } } -} -class LoginFormFields extends StatelessWidget { - final LoginController controller; + class LoginFormFields extends StatelessWidget { + final LoginController controller; - const LoginFormFields({super.key, required this.controller}); + const LoginFormFields({super.key, required this.controller}); - @override - Widget build(BuildContext context) { - final screenWidth = MediaQuery.of(context).size.width; - - // TAMANHOS AUMENTADOS - final verticalPadding = screenWidth > 600 ? 26.0 : 20.0; // ↑ Aumentado - final spacing = screenWidth > 600 ? 28.0 : 20.0; // ↑ Aumentado - final labelFontSize = screenWidth > 600 ? 18.0 : 16.0; // ↑ Aumentado - final textFontSize = screenWidth > 600 ? 18.0 : 16.0; // ↑ Aumentado + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + + // TAMANHOS AUMENTADOS + final verticalPadding = screenWidth > 600 ? 26.0 : 20.0; // ↑ Aumentado + final spacing = screenWidth > 600 ? 28.0 : 20.0; // ↑ Aumentado + final labelFontSize = screenWidth > 600 ? 18.0 : 16.0; // ↑ Aumentado + final textFontSize = screenWidth > 600 ? 18.0 : 16.0; // ↑ Aumentado - return Column( - children: [ - TextField( - controller: controller.emailController, - style: TextStyle(fontSize: textFontSize), // ↑ Tamanho do texto - decoration: InputDecoration( - labelText: 'E-mail', - labelStyle: TextStyle(fontSize: labelFontSize), // ↑ Tamanho do label - prefixIcon: Icon(Icons.email_outlined, size: 24), // ↑ Ícone maior - errorText: controller.emailError, - 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, - ), - ), - 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 + return Column( + children: [ + TextField( + controller: controller.emailController, + style: TextStyle(fontSize: textFontSize), // ↑ Tamanho do texto + decoration: InputDecoration( + labelText: 'E-mail', + labelStyle: TextStyle(fontSize: labelFontSize), // ↑ Tamanho do label + prefixIcon: Icon(Icons.email_outlined, size: 24), // ↑ Ícone maior + errorText: controller.emailError, + errorStyle: TextStyle(fontSize: 14), // ↑ Tamanho do erro + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.grey[400]!), ), - onPressed: controller.togglePasswordVisibility, - ), - 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( - fontSize: screenWidth > 600 ? 18.0 : 15.0, // ↑ Aumentado - color: const Color(0xFFE74C3C), - fontWeight: FontWeight.w600, // ↑ Mais negrito + 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); + } + }, ), - ), - ], - ); - } -} - -class LoginButton extends StatelessWidget { - final LoginController controller; - final VoidCallback onLoginSuccess; - - const LoginButton({ - super.key, - required this.controller, - required this.onLoginSuccess, - }); - - @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(Colors.white), + 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 ), - ) - : Text( - 'Entrar', + onPressed: controller.togglePasswordVisibility, + ), + 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( - fontSize: fontSize, - fontWeight: FontWeight.w700, // + fontSize: screenWidth > 600 ? 18.0 : 15.0, // ↑ Aumentado + color: const Color(0xFFE74C3C), + fontWeight: FontWeight.w600, // ↑ Mais negrito ), ), - ), - ); + ), + ), + ], + ); + } } -} -class CreateAccountButton extends StatelessWidget { - const CreateAccountButton({super.key}); + class LoginButton extends StatelessWidget { + final LoginController controller; + final VoidCallback onLoginSuccess; - @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 + const LoginButton({ + super.key, + required this.controller, + required this.onLoginSuccess, + }); - 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 + @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(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 - ), - ), - ), - ); - } -} + ); + } + }