812 lines
28 KiB
Dart
Executable File
812 lines
28 KiB
Dart
Executable File
import 'package:flutter/material.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:animate_do/animate_do.dart';
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; // <--- 🌟 NOVO: Importação para ícones sociais
|
|
import 'dart:ui';
|
|
|
|
import 'package:kzeduca_app/services/i18n_service.dart';
|
|
import 'package:kzeduca_app/services/firebase_service.dart';
|
|
import 'package:kzeduca_app/screens/dashboard_screen.dart';
|
|
import 'package:kzeduca_app/services/user_state_service.dart';
|
|
|
|
// Constantes de Cores Inspiradas no Dashboard
|
|
const Color kAppBackground = Color(0xFF1F1237); // Roxo bem escuro, quase preto
|
|
const Color kAppCardSurface = Color(0xFF2C1E4F); // Roxo escuro para a superfície do card
|
|
const Color kAppAccentPurple = Color(0xFF6A1B9A); // Roxo vibrante para acentos
|
|
const Color kAppAccentGold = Color(0xFFFFD700); // Dourado forte (como no dashboard)
|
|
const Color kAppAccentLightPurple = Color(0xFF8E24AA); // Roxo mais claro para contraste
|
|
const Color kAppTextColor = Color(0xFFE0E0E0); // Cinza claro para texto
|
|
const Color kAppHintColor = Color(0xFFB0A8C0); // Roxo acinzentado para placeholders
|
|
|
|
class AuthScreen extends StatefulWidget {
|
|
const AuthScreen({super.key});
|
|
|
|
@override
|
|
State<AuthScreen> createState() => _AuthScreenState();
|
|
}
|
|
|
|
class _AuthScreenState extends State<AuthScreen> with SingleTickerProviderStateMixin {
|
|
final TextEditingController _emailController = TextEditingController();
|
|
final TextEditingController _passwordController = TextEditingController();
|
|
final TextEditingController _usernameController = TextEditingController();
|
|
|
|
late final AnimationController _animationController;
|
|
|
|
bool _isLogin = true;
|
|
bool _isResettingPassword = false;
|
|
|
|
String _error = '';
|
|
bool _isLoading = false;
|
|
bool _isPasswordVisible = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_animationController = AnimationController(
|
|
vsync: this,
|
|
duration: const Duration(seconds: 4),
|
|
)..repeat(reverse: true);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_emailController.dispose();
|
|
_passwordController.dispose();
|
|
_usernameController.dispose();
|
|
_animationController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _showErrorDialog(String message) {
|
|
final i18n = Provider.of<I18nService>(context, listen: false);
|
|
if (mounted) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
title: Text(i18n.t('error_prefix'), style: TextStyle(color: kAppTextColor)),
|
|
content: Text(message, style: TextStyle(color: kAppTextColor)),
|
|
backgroundColor: kAppCardSurface,
|
|
actions: <Widget>[
|
|
TextButton(
|
|
child: Text('Ok', style: TextStyle(color: kAppAccentPurple)),
|
|
onPressed: () {
|
|
Navigator.of(ctx).pop();
|
|
},
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// NOVO MÉTODO: Autenticação com Google
|
|
Future<void> _authenticateWithGoogle() async {
|
|
final i18n = Provider.of<I18nService>(context, listen: false);
|
|
final firebaseService = Provider.of<FirebaseService>(context, listen: false);
|
|
|
|
if (!mounted) return;
|
|
|
|
setState(() {
|
|
_error = '';
|
|
_isLoading = true;
|
|
});
|
|
|
|
try {
|
|
await firebaseService.signInWithGoogle();
|
|
|
|
await Provider.of<UserStateService>(context, listen: false).loadUser();
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
Navigator.of(context).pushAndRemoveUntil(
|
|
MaterialPageRoute(builder: (context) => const DashboardScreen()),
|
|
(Route<dynamic> route) => false,
|
|
);
|
|
}
|
|
} on FirebaseAuthException catch (e) {
|
|
String errorMessage = i18n.t('auth_error');
|
|
if (e.code == 'google-login-cancelled') {
|
|
errorMessage = i18n.t('google_login_cancelled'); // Assumindo que você tem essa chave i18n
|
|
} else {
|
|
errorMessage = i18n.t('google_auth_failed'); // Assumindo que você tem essa chave i18n
|
|
}
|
|
_showErrorDialog(errorMessage);
|
|
if(mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
_showErrorDialog(i18n.t('auth_error'));
|
|
if(mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============== FUNÇÃO DE REINICIALIZAÇÃO DA PALAVRA-PASSE ==============
|
|
Future<void> _sendPasswordResetEmail() async {
|
|
final i18n = Provider.of<I18nService>(context, listen: false);
|
|
|
|
final email = _emailController.text.trim();
|
|
|
|
if (email.isEmpty) {
|
|
_showErrorDialog(i18n.t('email_required_for_reset'));
|
|
return;
|
|
}
|
|
|
|
if (!mounted) return;
|
|
|
|
setState(() {
|
|
_isLoading = true;
|
|
});
|
|
|
|
try {
|
|
await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
|
|
|
|
if (mounted) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (ctx) => AlertDialog(
|
|
title: Text(i18n.t('password_reset_title'), style: TextStyle(color: kAppTextColor)),
|
|
content: Text(i18n.t('password_reset_sent').replaceAll('{email}', email), style: TextStyle(color: kAppTextColor)),
|
|
backgroundColor: kAppCardSurface,
|
|
actions: <Widget>[
|
|
TextButton(
|
|
child: Text('Ok', style: TextStyle(color: kAppAccentPurple)),
|
|
onPressed: () {
|
|
Navigator.of(ctx).pop();
|
|
setState(() {
|
|
_isResettingPassword = false;
|
|
_emailController.clear();
|
|
});
|
|
},
|
|
)
|
|
],
|
|
),
|
|
);
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} on FirebaseAuthException catch (e) {
|
|
String errorMessage = i18n.t('reset_password_error');
|
|
if (e.code == 'user-not-found' || e.code == 'invalid-email') {
|
|
errorMessage = i18n.t('email_invalid_or_not_found');
|
|
}
|
|
_showErrorDialog(errorMessage);
|
|
if(mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
_showErrorDialog(i18n.t('auth_error'));
|
|
if(mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
// ======================================================================
|
|
|
|
Future<void> _authenticate() async {
|
|
final i18n = Provider.of<I18nService>(context, listen: false);
|
|
final firebaseService = Provider.of<FirebaseService>(context, listen: false);
|
|
// ... (Lógica inalterada) ...
|
|
if (!mounted) return;
|
|
|
|
setState(() {
|
|
_error = '';
|
|
_isLoading = true;
|
|
});
|
|
|
|
try {
|
|
if (_isLogin) {
|
|
await firebaseService.signInWithEmailAndPassword(
|
|
_emailController.text,
|
|
_passwordController.text,
|
|
);
|
|
} else {
|
|
if (_usernameController.text.trim().isEmpty) {
|
|
_showErrorDialog(i18n.t('username_required'));
|
|
if (mounted) {
|
|
setState(() { _isLoading = false; });
|
|
}
|
|
return;
|
|
}
|
|
await firebaseService.registerWithEmailAndPassword(
|
|
_emailController.text,
|
|
_passwordController.text,
|
|
_usernameController.text.trim(),
|
|
);
|
|
}
|
|
|
|
await Provider.of<UserStateService>(context, listen: false).loadUser();
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
Navigator.of(context).pushAndRemoveUntil(
|
|
MaterialPageRoute(builder: (context) => const DashboardScreen()),
|
|
(Route<dynamic> route) => false,
|
|
);
|
|
}
|
|
|
|
} on FirebaseAuthException catch (e) {
|
|
String errorMessage = i18n.t('auth_error');
|
|
if (e.code == 'weak-password') {
|
|
errorMessage = i18n.t('password_weak');
|
|
} else if (e.code == 'email-already-in-use') {
|
|
errorMessage = i18n.t('email_already_in_use');
|
|
} else if (e.code == 'invalid-email') {
|
|
errorMessage = i18n.t('email_invalid');
|
|
} else if (e.code == 'user-not-found' || e.code == 'wrong-password') {
|
|
errorMessage = i18n.t('login_error');
|
|
} else if (e.code == 'operation-not-allowed') {
|
|
errorMessage = i18n.t('auth_operation_not_allowed_email_password');
|
|
}
|
|
_showErrorDialog(errorMessage);
|
|
if(mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
_showErrorDialog(i18n.t('auth_error'));
|
|
if(mounted) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============== WIDGET: Interface de Redefinição de Senha ==============
|
|
Widget _buildResetPasswordUI(I18nService i18n) {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
FadeInDown(
|
|
duration: const Duration(milliseconds: 800),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
i18n.t('reset_password_title'),
|
|
style: const TextStyle(
|
|
fontFamily: 'Poppins',
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w700,
|
|
color: kAppTextColor,
|
|
letterSpacing: 0.8,
|
|
shadows: [
|
|
Shadow(color: Colors.black45, blurRadius: 4, offset: Offset(2, 2)),
|
|
]
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
i18n.t('reset_password_instructions'),
|
|
style: TextStyle(
|
|
fontFamily: 'Poppins',
|
|
fontSize: 14,
|
|
color: kAppTextColor.withOpacity(0.8),
|
|
letterSpacing: 0.4,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
_buildTextField(
|
|
controller: _emailController,
|
|
hintText: i18n.t('email_placeholder'),
|
|
icon: Icons.email,
|
|
keyboardType: TextInputType.emailAddress,
|
|
),
|
|
const SizedBox(height: 12),
|
|
_buildSignInButton(
|
|
i18n.t('send_reset_link_button'),
|
|
onPressed: _sendPasswordResetEmail,
|
|
backgroundColor: kAppAccentPurple, // Botão Dourado
|
|
),
|
|
const SizedBox(height: 8),
|
|
TextButton(
|
|
onPressed: () {
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_isResettingPassword = false;
|
|
_emailController.clear();
|
|
});
|
|
},
|
|
child: Text(
|
|
i18n.t('back_to_login'),
|
|
style: TextStyle(
|
|
color: kAppTextColor.withOpacity(0.7),
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 0.3,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
// ======================================================================
|
|
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final i18n = Provider.of<I18nService>(context);
|
|
|
|
// Aumentamos a altura para acomodar o botão do Google e o separador
|
|
double cardHeight = _isResettingPassword ? 430 : (_isLogin ? 540 : 600);
|
|
|
|
return Scaffold(
|
|
backgroundColor: kAppCardSurface,
|
|
body: Stack(
|
|
children: [
|
|
// 1. Fundo com gradiente sutil (roxo escuro para mais claro)
|
|
Positioned.fill(
|
|
child: AnimatedBuilder(
|
|
animation: _animationController,
|
|
builder: (context, child) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
gradient: RadialGradient(
|
|
center: Alignment(_animationController.value * 2 - 1, _animationController.value * 2 - 1),
|
|
radius: 1.0,
|
|
colors: [
|
|
kAppAccentPurple.withOpacity(0.1), // Roxo sutil
|
|
kAppBackground,
|
|
kAppBackground,
|
|
],
|
|
stops: const [0.0, 0.4, 1.0],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
// 2. Conteúdo Central
|
|
Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
FadeInUp(
|
|
duration: const Duration(milliseconds: 800),
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
// 2a. Container Externo (Borda Dourada Metálica com gradiente roxo sutil)
|
|
AnimatedBuilder(
|
|
animation: _animationController,
|
|
builder: (context, child) {
|
|
return Container(
|
|
width: 330,
|
|
height: cardHeight + 4,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(16),
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: const [
|
|
Color(0xFF27a2ff),
|
|
Color(0xFFff2770), // Roxo no gradiente
|
|
Color(0xFF27a2ff),
|
|
],
|
|
stops: [
|
|
_animationController.value - 0.5,
|
|
_animationController.value,
|
|
_animationController.value + 0.5,
|
|
],
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.4),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 0),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
// 2b. Container Interno (Superfície Roxo Escuro)
|
|
Container(
|
|
width: 326,
|
|
height: cardHeight,
|
|
padding: const EdgeInsets.fromLTRB(24, 60, 24, 24),
|
|
decoration: BoxDecoration(
|
|
color: kAppCardSurface,
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: Color(0xFF8A2BE2).withOpacity(0.3),
|
|
width: 0.5,
|
|
),
|
|
boxShadow: const [
|
|
BoxShadow(
|
|
color: Colors.black54,
|
|
blurRadius: 8,
|
|
offset: Offset(0, 4),
|
|
),
|
|
BoxShadow(
|
|
color: Colors.white10,
|
|
blurRadius: 2,
|
|
offset: Offset(0, -1),
|
|
),
|
|
]
|
|
),
|
|
child: _isResettingPassword
|
|
? _buildResetPasswordUI(i18n)
|
|
: _buildAuthUI(i18n),
|
|
),
|
|
// 2c. Seletor de Idioma
|
|
Positioned(
|
|
top: 24,
|
|
right: 10,
|
|
child: FadeInDown(
|
|
duration: const Duration(milliseconds: 800),
|
|
child: Row(
|
|
children: [
|
|
_buildLanguageButton('PT', 'pt', i18n),
|
|
const SizedBox(width: 8),
|
|
_buildLanguageButton('EN', 'en', i18n),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// WIDGET: Botão de Idioma Refatorado (Estilo Luxuoso com Cores do App)
|
|
Widget _buildLanguageButton(String text, String localeCode, I18nService i18n) {
|
|
bool isSelected = i18n.locale.languageCode == localeCode;
|
|
return InkWell(
|
|
onTap: () => i18n.setLocale(Locale(localeCode)),
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: isSelected ? Color(0xFF8320E0) : Colors.transparent, // Dourado quando selecionado
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(
|
|
color: isSelected ? kAppBackground : kAppTextColor.withOpacity(0.3),
|
|
width: 1.5,
|
|
),
|
|
boxShadow: isSelected ? [
|
|
BoxShadow(
|
|
color: kAppAccentPurple.withOpacity(0.5),
|
|
blurRadius: 8,
|
|
)
|
|
] : [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.3),
|
|
blurRadius: 3,
|
|
offset: const Offset(1, 1),
|
|
)
|
|
],
|
|
),
|
|
child: Text(
|
|
text,
|
|
style: TextStyle(
|
|
color: isSelected ? kAppBackground : kAppTextColor.withOpacity(0.7), // Texto escuro no dourado
|
|
fontSize: 12,
|
|
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
|
letterSpacing: 0.5,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
|
|
// WIDGET: UI de Login/Registro (Adição do Botão Google)
|
|
Widget _buildAuthUI(I18nService i18n) {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
FadeInDown(
|
|
duration: const Duration(milliseconds: 800),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'KzEduca',
|
|
style: TextStyle(
|
|
fontFamily: 'Poppins',
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w700,
|
|
color: Color(0xFF8A2BE2), // Título em Dourado
|
|
letterSpacing: 1.5,
|
|
shadows: [
|
|
Shadow(color: Colors.black87, blurRadius: 8, offset: Offset(2, 2)),
|
|
Shadow(color: kAppAccentPurple, blurRadius: 4, offset: Offset(0, 0)),
|
|
]
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
i18n.t('slogan'),
|
|
style: TextStyle(
|
|
fontFamily: 'Poppins',
|
|
fontSize: 14,
|
|
color: kAppTextColor.withOpacity(0.8),
|
|
letterSpacing: 0.4,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Expanded(
|
|
child: _buildTabButton(i18n.t('login'), true, _isLogin, Color(0xFF27a2ff), Color(0xFF8320E0)), // Botão de Login Dourado/Roxo
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: _buildTabButton(i18n.t('register'), false, !_isLogin, Color(0xFF8320E0), Color(0xFF27a2ff)), // Botão de Registro Roxo/Dourado
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
if (!_isLogin)
|
|
_buildTextField(
|
|
controller: _usernameController,
|
|
hintText: i18n.t('username_placeholder'),
|
|
icon: Icons.person,
|
|
),
|
|
_buildTextField(
|
|
controller: _emailController,
|
|
hintText: i18n.t('email_placeholder'),
|
|
icon: Icons.email,
|
|
keyboardType: TextInputType.emailAddress,
|
|
),
|
|
_buildTextField(
|
|
controller: _passwordController,
|
|
hintText: i18n.t('password_placeholder'),
|
|
icon: Icons.lock,
|
|
obscureText: !_isPasswordVisible,
|
|
isPasswordInput: true,
|
|
),
|
|
const SizedBox(height: 12),
|
|
// BOTÃO PRINCIPAL (E-MAIL/SENHA)
|
|
_buildSignInButton(
|
|
i18n.t(_isLogin ? 'enter' : 'register_button'),
|
|
onPressed: _authenticate,
|
|
backgroundColor: _isLogin ? Color(0xFF27a2ff) : Color(0xFF8320E0), // Botões de ação principais
|
|
),
|
|
|
|
const SizedBox(height: 12),
|
|
// Separador Sutil
|
|
Text(
|
|
i18n.t('or_login_with'), // Chave i18n para "ou entrar com"
|
|
style: TextStyle(color: kAppTextColor.withOpacity(0.6), fontSize: 12),
|
|
),
|
|
const SizedBox(height: 12),
|
|
|
|
// NOVO BOTÃO: GOOGLE SIGN-IN
|
|
_buildGoogleSignInButton(i18n),
|
|
|
|
if (_isLogin)
|
|
TextButton(
|
|
onPressed: _isLoading ? null : () {
|
|
setState(() {
|
|
_isResettingPassword = true;
|
|
_emailController.clear();
|
|
_passwordController.clear();
|
|
});
|
|
},
|
|
child: Text(
|
|
i18n.t('forgot_password'),
|
|
style: TextStyle(
|
|
color: kAppTextColor.withOpacity(0.7),
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 0.3,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// WIDGET ATUALIZADO: Botão Google Dedicado com FaIcon (FontAwesome)
|
|
Widget _buildGoogleSignInButton(I18nService i18n) {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton.icon(
|
|
onPressed: _isLoading ? null : _authenticateWithGoogle,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: kAppCardSurface, // Fundo roxo escuro para contraste
|
|
foregroundColor: kAppTextColor,
|
|
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
side: BorderSide(color: Color(0xFF27a2ff).withOpacity(0.7), width: 1.5), // Borda Dourada para o toque luxuoso
|
|
),
|
|
elevation: 6,
|
|
shadowColor: Color(0xFF8A2BE2).withOpacity(0.4),
|
|
),
|
|
icon: const FaIcon( // <--- AGORA USA FaIcon
|
|
FontAwesomeIcons.google, // <--- Ícone do Google
|
|
color: Color(0xFF27a2ff),
|
|
size: 20,
|
|
),
|
|
label: Text(
|
|
i18n.t('sign_in_with_google'), // Chave i18n para "Entrar com Google"
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 0.8,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// WIDGETS INALTERADOS: _buildTabButton, _buildTextField, _buildSignInButton...
|
|
|
|
// WIDGET: Botão de Aba Refatorado (Estilo Luxuoso com Cores do App)
|
|
Widget _buildTabButton(String text, bool isLoginButton, bool isActive, Color activeColor, Color inactiveBorderColor) {
|
|
return ElevatedButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
_isLogin = isLoginButton;
|
|
});
|
|
},
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: isActive ? activeColor : kAppCardSurface.withOpacity(0.8),
|
|
foregroundColor: isActive ? kAppBackground : kAppTextColor.withOpacity(0.7),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(24),
|
|
side: BorderSide(color: isActive ? activeColor.withOpacity(0.6) : inactiveBorderColor.withOpacity(0.3), width: 1),
|
|
),
|
|
elevation: isActive ? 8 : 2,
|
|
shadowColor: isActive ? activeColor.withOpacity(0.5) : Colors.black.withOpacity(0.2),
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
),
|
|
child: Text(
|
|
text,
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 0.8,
|
|
)
|
|
),
|
|
);
|
|
}
|
|
|
|
// WIDGET: Campo de Texto Refatorado (Estilo Luxuoso com Cores do App)
|
|
Widget _buildTextField({
|
|
required TextEditingController controller,
|
|
required String hintText,
|
|
required IconData icon,
|
|
bool obscureText = false,
|
|
TextInputType keyboardType = TextInputType.text,
|
|
bool isPasswordInput = false,
|
|
}) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16.0),
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.4),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
BoxShadow(
|
|
color: Colors.white.withOpacity(0.05),
|
|
blurRadius: 2,
|
|
offset: const Offset(0, -1),
|
|
),
|
|
]
|
|
),
|
|
child: TextField(
|
|
controller: controller,
|
|
obscureText: obscureText,
|
|
keyboardType: keyboardType,
|
|
style: const TextStyle(color: kAppTextColor, fontWeight: FontWeight.normal),
|
|
decoration: InputDecoration(
|
|
hintText: hintText,
|
|
hintStyle: TextStyle(color: kAppHintColor),
|
|
prefixIcon: Icon(icon, color: Color(0xFF8A2BE2), size: 20),
|
|
suffixIcon: isPasswordInput
|
|
? IconButton(
|
|
icon: Icon(
|
|
_isPasswordVisible ? Icons.visibility : Icons.visibility_off,
|
|
color: kAppHintColor,
|
|
),
|
|
onPressed: () {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isPasswordVisible = !_isPasswordVisible;
|
|
});
|
|
}
|
|
},
|
|
)
|
|
: null,
|
|
filled: true,
|
|
fillColor: kAppCardSurface,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: kAppAccentLightPurple.withOpacity(0.5), width: 0.5),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: Color(0xFF8A2BE2), width: 1.5),
|
|
),
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// WIDGET: Botão de Ação Principal Refatorado (Estilo Luxuoso com Cores do App)
|
|
Widget _buildSignInButton(String buttonText, {required Color backgroundColor, required VoidCallback onPressed}) {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton(
|
|
onPressed: _isLoading ? null : onPressed,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: backgroundColor,
|
|
foregroundColor: kAppBackground,
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
elevation: 10,
|
|
shadowColor: backgroundColor.withOpacity(0.6),
|
|
),
|
|
child: _isLoading
|
|
? const SizedBox(
|
|
height: 24,
|
|
width: 24,
|
|
child: CircularProgressIndicator(
|
|
color: kAppBackground,
|
|
strokeWidth: 2,
|
|
),
|
|
)
|
|
: Text(
|
|
buttonText,
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w900,
|
|
letterSpacing: 1.0,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |