From 44937ca6d3027837ca3ecf11173d13662a27e6b5 Mon Sep 17 00:00:00 2001
From: 230404 <230404@epvc.pt>
Date: Tue, 6 Jan 2026 17:17:37 +0000
Subject: [PATCH] firebase esta feita acabar o register
---
analysis_options.yaml | 3 +
android/app/google-services.json | 48 ++++-
android/settings.gradle.kts | 3 +
devtools_options.yaml | 3 +
firebase.json | 9 +-
ios/Runner/GoogleService-Info.plist | 32 ++++
lib/controllers/login_controller.dart | 83 +++++++--
lib/controllers/register_controller.dart | 97 +++++++++++
lib/firebase_options.dart | 79 +++++++++
lib/main.dart | 14 +-
lib/pages/RegisterPage.dart | 76 ++++++++
lib/pages/login.dart | 115 ++++++------
lib/widgets/login_widgets.dart | 212 ++++++++---------------
lib/widgets/register_widgets.dart | 53 ++++++
14 files changed, 594 insertions(+), 233 deletions(-)
create mode 100644 devtools_options.yaml
create mode 100644 ios/Runner/GoogleService-Info.plist
create mode 100644 lib/controllers/register_controller.dart
create mode 100644 lib/firebase_options.dart
create mode 100644 lib/pages/RegisterPage.dart
create mode 100644 lib/widgets/register_widgets.dart
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 0d29021..bee01e3 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -7,6 +7,9 @@
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
+analyzer:
+ errors:
+ unused_element: ignore
include: package:flutter_lints/flutter.yaml
linter:
diff --git a/android/app/google-services.json b/android/app/google-services.json
index 280f900..e1adf43 100644
--- a/android/app/google-services.json
+++ b/android/app/google-services.json
@@ -1,9 +1,49 @@
{
"project_info": {
- "project_number": "123456789012",
- "project_id": "playmaker-basketball",
- "storage_bucket": "playmaker-basketball.appspot.com"
+ "project_number": "74256198630",
+ "firebase_url": "https://playmaker-9e0fc-default-rtdb.firebaseio.com",
+ "project_id": "playmaker-9e0fc",
+ "storage_bucket": "playmaker-9e0fc.firebasestorage.app"
},
- "client": [],
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:74256198630:android:145e08f6bc85ff13d18427",
+ "android_client_info": {
+ "package_name": "com.example.playmaker"
+ }
+ },
+ "oauth_client": [],
+ "api_key": [
+ {
+ "current_key": "AIzaSyDm7MBJQ6vZEE_gM1Ek5LH3Mf5ui2YHc2I"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": []
+ }
+ }
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:74256198630:android:092ff62687a5e51dd18427",
+ "android_client_info": {
+ "package_name": "com.playmaker.basketball"
+ }
+ },
+ "oauth_client": [],
+ "api_key": [
+ {
+ "current_key": "AIzaSyDm7MBJQ6vZEE_gM1Ek5LH3Mf5ui2YHc2I"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": []
+ }
+ }
+ }
+ ],
"configuration_version": "1"
}
\ No newline at end of file
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index fb605bc..ff284ff 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -20,6 +20,9 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
+ // START: FlutterFire Configuration
+ id("com.google.gms.google-services") version("4.3.15") apply false
+ // END: FlutterFire Configuration
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
diff --git a/devtools_options.yaml b/devtools_options.yaml
new file mode 100644
index 0000000..fa0b357
--- /dev/null
+++ b/devtools_options.yaml
@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
diff --git a/firebase.json b/firebase.json
index 602d1eb..138d198 100644
--- a/firebase.json
+++ b/firebase.json
@@ -1,8 +1 @@
-{
- "storage": {
- "rules": "storage.rules"
- },
- "database": {
- "rules": "database.rules.json"
- }
-}
+{"storage":{"rules":"storage.rules"},"database":{"rules":"database.rules.json"},"flutter":{"platforms":{"android":{"default":{"projectId":"playmaker-9e0fc","appId":"1:74256198630:android:145e08f6bc85ff13d18427","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"playmaker-9e0fc","configurations":{"android":"1:74256198630:android:145e08f6bc85ff13d18427","web":"1:74256198630:web:ba3d62a31608d686d18427","windows":"1:74256198630:web:6458f24490c3dc80d18427"}}}}}}
\ No newline at end of file
diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist
new file mode 100644
index 0000000..8738dfd
--- /dev/null
+++ b/ios/Runner/GoogleService-Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ API_KEY
+ AIzaSyDxtHmQfK9WVHVAdNEA9zomqgNkNlj5u0g
+ GCM_SENDER_ID
+ 74256198630
+ PLIST_VERSION
+ 1
+ BUNDLE_ID
+ com.example.playmaker
+ PROJECT_ID
+ playmaker-9e0fc
+ STORAGE_BUCKET
+ playmaker-9e0fc.firebasestorage.app
+ IS_ADS_ENABLED
+
+ IS_ANALYTICS_ENABLED
+
+ IS_APPINVITE_ENABLED
+
+ IS_GCM_ENABLED
+
+ IS_SIGNIN_ENABLED
+
+ GOOGLE_APP_ID
+ 1:74256198630:ios:a3a6ee8f075dd9d2d18427
+ DATABASE_URL
+ https://playmaker-9e0fc-default-rtdb.firebaseio.com
+
+
\ No newline at end of file
diff --git a/lib/controllers/login_controller.dart b/lib/controllers/login_controller.dart
index ca34074..01f7f2b 100644
--- a/lib/controllers/login_controller.dart
+++ b/lib/controllers/login_controller.dart
@@ -1,6 +1,9 @@
+import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class LoginController with ChangeNotifier {
+ final FirebaseAuth _auth = FirebaseAuth.instance;
+
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
@@ -20,26 +23,19 @@ class LoginController with ChangeNotifier {
}
String? validateEmail(String? value) {
- if (value == null || value.isEmpty) {
- return 'Por favor, insira o seu email';
- }
+ if (value == null || value.isEmpty) return 'Por favor, insira o seu email';
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
- if (!emailRegex.hasMatch(value)) {
- return 'Por favor, insira um email válido';
- }
+ if (!emailRegex.hasMatch(value)) return 'Por favor, insira um email válido';
return null;
}
String? validatePassword(String? value) {
- if (value == null || value.isEmpty) {
- return 'Por favor, insira a sua password';
- }
- if (value.length < 6) {
- return 'A password deve ter pelo menos 6 caracteres';
- }
+ if (value == null || value.isEmpty) return 'Por favor, insira a sua password';
+ if (value.length < 6) return 'A password deve ter pelo menos 6 caracteres';
return null;
}
+ // --- MÉTODO PARA ENTRAR (LOGIN) ---
Future login() async {
_emailError = validateEmail(emailController.text);
_passwordError = validatePassword(passwordController.text);
@@ -53,21 +49,76 @@ class LoginController with ChangeNotifier {
notifyListeners();
try {
- await Future.delayed(const Duration(seconds: 2));
-
+ await _auth.signInWithEmailAndPassword(
+ email: emailController.text.trim(),
+ password: passwordController.text.trim(),
+ );
_isLoading = false;
notifyListeners();
return true;
- } catch (e) {
+ } on FirebaseAuthException catch (e) {
_isLoading = false;
- _emailError = 'Erro no login. Tente novamente.';
+ _handleFirebaseError(e.code);
notifyListeners();
return false;
}
}
+ // --- MÉTODO PARA CRIAR CONTA (SIGN UP) ---
+ Future signUp() async {
+ _emailError = validateEmail(emailController.text);
+ _passwordError = validatePassword(passwordController.text);
+
+ if (_emailError != null || _passwordError != null) {
+ notifyListeners();
+ return false;
+ }
+
+ _isLoading = true;
+ notifyListeners();
+
+ try {
+ await _auth.createUserWithEmailAndPassword(
+ email: emailController.text.trim(),
+ password: passwordController.text.trim(),
+ );
+ _isLoading = false;
+ notifyListeners();
+ return true;
+ } on FirebaseAuthException catch (e) {
+ _isLoading = false;
+ _handleFirebaseError(e.code);
+ notifyListeners();
+ return false;
+ }
+ }
+
+ void _handleFirebaseError(String code) {
+ switch (code) {
+ case 'email-already-in-use':
+ _emailError = 'Este e-mail já está a ser utilizado.';
+ break;
+ case 'invalid-credential':
+ _emailError = 'E-mail ou password incorretos.';
+ break;
+ case 'user-not-found':
+ _emailError = 'Utilizador não encontrado.';
+ break;
+ case 'wrong-password':
+ _passwordError = 'Palavra-passe incorreta.';
+ break;
+ case 'weak-password':
+ _passwordError = 'A password é demasiado fraca.';
+ break;
+ default:
+ _emailError = 'Erro: $code';
+ }
+ }
+
+ @override
void dispose() {
emailController.dispose();
passwordController.dispose();
+ super.dispose();
}
}
\ No newline at end of file
diff --git a/lib/controllers/register_controller.dart b/lib/controllers/register_controller.dart
new file mode 100644
index 0000000..a94ae77
--- /dev/null
+++ b/lib/controllers/register_controller.dart
@@ -0,0 +1,97 @@
+
+import 'package:firebase_auth/firebase_auth.dart';
+import 'package:flutter/material.dart';
+
+class RegisterController with ChangeNotifier {
+ final FirebaseAuth _auth = FirebaseAuth.instance;
+
+ final TextEditingController emailController = TextEditingController();
+ final TextEditingController passwordController = TextEditingController();
+ final TextEditingController confirmPasswordController = TextEditingController();
+
+ bool _isLoading = false;
+ String? _emailError;
+ String? _passwordError;
+ String? _confirmPasswordError; // Novo!
+ String? get confirmPasswordError => _confirmPasswordError; // Novo!
+
+ bool get isLoading => _isLoading;
+ String? get emailError => _emailError;
+ String? get passwordError => _passwordError;
+
+ // Validações
+ String? validateEmail(String? value) {
+ if (value == null || value.isEmpty) return 'Por favor, insira o seu email';
+ final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
+ if (!emailRegex.hasMatch(value)) return 'Por favor, insira um email válido';
+ return null;
+ }
+
+ String? validatePassword(String? value) {
+ if (value == null || value.isEmpty) return 'Por favor, insira a sua password';
+ if (value.length < 6) return 'A password deve ter pelo menos 6 caracteres';
+ return null;
+ }
+ String? validateConfirmPassword(String? value) {
+ if (value == null || value.isEmpty) {
+ return 'Por favor, confirme a sua password';
+ }
+ if (value != passwordController.text) {
+ return 'As passwords não coincidem';
+ }
+ return null;
+ }
+
+
+ // MÉTODO PARA CRIAR CONTA (SIGN UP)
+ Future signUp() async {
+ _emailError = validateEmail(emailController.text);
+ _passwordError = validatePassword(passwordController.text);
+ _emailError = validateEmail(emailController.text);
+ _passwordError = validatePassword(passwordController.text);
+ _confirmPasswordError = validateConfirmPassword(confirmPasswordController.text); // Valida aqui!
+
+ if (_emailError != null || _passwordError != null || _confirmPasswordError != null) {
+ notifyListeners();
+ return false;
+ }
+
+ _isLoading = true;
+ notifyListeners();
+
+ try {
+ await _auth.createUserWithEmailAndPassword(
+ email: emailController.text.trim(),
+ password: passwordController.text.trim(),
+ );
+ _isLoading = false;
+ notifyListeners();
+ return true;
+ } on FirebaseAuthException catch (e) {
+ _isLoading = false;
+ _handleFirebaseError(e.code);
+ notifyListeners();
+ return false;
+ }
+ }
+
+ void _handleFirebaseError(String code) {
+ switch (code) {
+ case 'email-already-in-use':
+ _emailError = 'Este e-mail já está a ser utilizado.';
+ break;
+ case 'weak-password':
+ _passwordError = 'A password é demasiado fraca.';
+ break;
+ default:
+ _emailError = 'Erro ao registar: $code';
+ }
+ }
+
+ @override
+ void dispose() {
+ emailController.dispose();
+ passwordController.dispose();
+ super.dispose();
+ }
+}
\ No newline at end of file
diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart
new file mode 100644
index 0000000..c0a142a
--- /dev/null
+++ b/lib/firebase_options.dart
@@ -0,0 +1,79 @@
+// File generated by FlutterFire CLI.
+// ignore_for_file: type=lint
+import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
+import 'package:flutter/foundation.dart'
+ show defaultTargetPlatform, kIsWeb, TargetPlatform;
+
+/// Default [FirebaseOptions] for use with your Firebase apps.
+///
+/// Example:
+/// ```dart
+/// import 'firebase_options.dart';
+/// // ...
+/// await Firebase.initializeApp(
+/// options: DefaultFirebaseOptions.currentPlatform,
+/// );
+/// ```
+class DefaultFirebaseOptions {
+ static FirebaseOptions get currentPlatform {
+ if (kIsWeb) {
+ return web;
+ }
+ switch (defaultTargetPlatform) {
+ case TargetPlatform.android:
+ return android;
+ case TargetPlatform.iOS:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for ios - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.macOS:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for macos - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.windows:
+ return windows;
+ case TargetPlatform.linux:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for linux - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ default:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions are not supported for this platform.',
+ );
+ }
+ }
+
+ static const FirebaseOptions web = FirebaseOptions(
+ apiKey: 'AIzaSyBkHZtox18LRzXWYHEKVEXaYkkf8jv8Enk',
+ appId: '1:74256198630:web:ba3d62a31608d686d18427',
+ messagingSenderId: '74256198630',
+ projectId: 'playmaker-9e0fc',
+ authDomain: 'playmaker-9e0fc.firebaseapp.com',
+ databaseURL: 'https://playmaker-9e0fc-default-rtdb.firebaseio.com',
+ storageBucket: 'playmaker-9e0fc.firebasestorage.app',
+ measurementId: 'G-QQE1EZWZ8K',
+ );
+
+ static const FirebaseOptions android = FirebaseOptions(
+ apiKey: 'AIzaSyDm7MBJQ6vZEE_gM1Ek5LH3Mf5ui2YHc2I',
+ appId: '1:74256198630:android:145e08f6bc85ff13d18427',
+ messagingSenderId: '74256198630',
+ projectId: 'playmaker-9e0fc',
+ databaseURL: 'https://playmaker-9e0fc-default-rtdb.firebaseio.com',
+ storageBucket: 'playmaker-9e0fc.firebasestorage.app',
+ );
+
+ static const FirebaseOptions windows = FirebaseOptions(
+ apiKey: 'AIzaSyBkHZtox18LRzXWYHEKVEXaYkkf8jv8Enk',
+ appId: '1:74256198630:web:6458f24490c3dc80d18427',
+ messagingSenderId: '74256198630',
+ projectId: 'playmaker-9e0fc',
+ authDomain: 'playmaker-9e0fc.firebaseapp.com',
+ databaseURL: 'https://playmaker-9e0fc-default-rtdb.firebaseio.com',
+ storageBucket: 'playmaker-9e0fc.firebasestorage.app',
+ measurementId: 'G-D56MT819B0',
+ );
+}
diff --git a/lib/main.dart b/lib/main.dart
index c0fd229..8b6b066 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,7 +1,14 @@
import 'package:flutter/material.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'firebase_options.dart';
import 'pages/login.dart';
-void main() {
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ await Firebase.initializeApp(
+ options: DefaultFirebaseOptions.currentPlatform,
+ );
+
runApp(const MyApp());
}
@@ -11,14 +18,15 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
+ debugShowCheckedModeBanner: false, // Opcional: remove a faixa de debug
title: 'BasketTrack',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFFE74C3C),
),
useMaterial3: true,
- ),
- home: LoginPage(),
+ ),
+ home: const LoginPage(),
);
}
}
\ No newline at end of file
diff --git a/lib/pages/RegisterPage.dart b/lib/pages/RegisterPage.dart
new file mode 100644
index 0000000..b34df99
--- /dev/null
+++ b/lib/pages/RegisterPage.dart
@@ -0,0 +1,76 @@
+import 'package:flutter/material.dart';
+import 'package:playmaker/controllers/register_controller.dart';
+import '../Controllers/register_controller.dart';
+import '../widgets/register_widgets.dart'; // Import dos novos widgets
+import 'home.dart';
+
+class RegisterPage extends StatefulWidget {
+ const RegisterPage({super.key});
+
+ @override
+ State createState() => _RegisterPageState();
+}
+
+class _RegisterPageState extends State {
+ final RegisterController controller = RegisterController();
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(backgroundColor: Colors.white, elevation: 0, foregroundColor: Colors.black),
+ backgroundColor: Colors.white,
+ body: SafeArea(
+ child: ListenableBuilder(
+ listenable: controller,
+ builder: (context, child) {
+ return Center(
+ child: SingleChildScrollView(
+ padding: const EdgeInsets.all(32),
+ child: Column(
+ children: [
+ const RegisterHeader(), // Widget do Header
+ const SizedBox(height: 40),
+
+ RegisterFormFields(controller: controller), // Widget dos Campos
+
+ const SizedBox(height: 32),
+
+ // Botão de Finalizar
+ SizedBox(
+ width: double.infinity,
+ height: 60,
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ backgroundColor: const Color(0xFFE74C3C),
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
+ ),
+ onPressed: controller.isLoading ? null : () async {
+ final success = await controller.signUp();
+ if (success && mounted) {
+ Navigator.pushReplacement(
+ context,
+ MaterialPageRoute(builder: (context) => const HomeScreen()),
+ );
+ }
+ },
+ child: controller.isLoading
+ ? const CircularProgressIndicator(color: Colors.white)
+ : const Text("Criar Conta", style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/pages/login.dart b/lib/pages/login.dart
index d3aea7c..281fc72 100644
--- a/lib/pages/login.dart
+++ b/lib/pages/login.dart
@@ -1,5 +1,4 @@
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';
@@ -14,69 +13,61 @@
class _LoginPageState extends State {
final LoginController controller = LoginController();
- @override
- void dispose() {
- controller.dispose();
- super.dispose();
- }
+@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: () {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.white,
+ body: SafeArea(
+ child: ListenableBuilder(
+ listenable: controller,
+ builder: (context, child) {
+ return LayoutBuilder(
+ builder: (context, constraints) {
+ final screenWidth = constraints.maxWidth;
+
+ return Center(
+ child: SingleChildScrollView(
+ child: Container(
+ width: screenWidth > 800 ? 600.0 :
+ screenWidth > 600 ? 500.0 : 400.0,
+ padding: const EdgeInsets.all(32),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ 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(),
-
- ),
- );
- },
+ context,
+ MaterialPageRoute(builder: (context) => const HomeScreen()),
+ );
+ },
+ ),
+ const SizedBox(height: 16),
+
+ const CreateAccountButton(),
+ ],
),
- 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/widgets/login_widgets.dart b/lib/widgets/login_widgets.dart
index 5b32a08..2b679ba 100644
--- a/lib/widgets/login_widgets.dart
+++ b/lib/widgets/login_widgets.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:playmaker/Controllers/login_controller.dart';
+import 'package:playmaker/pages/RegisterPage.dart';
class BasketTrackHeader extends StatelessWidget {
const BasketTrackHeader({super.key});
@@ -54,121 +55,47 @@
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;
+ final verticalPadding = screenWidth > 600 ? 22.0 : 16.0;
- 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);
- }
- },
+ return Column(
+ children: [
+ TextField(
+ controller: controller.emailController,
+ decoration: InputDecoration(
+ labelText: 'E-mail',
+ prefixIcon: const Icon(Icons.email_outlined),
+ // O erro agora vem diretamente do controller
+ errorText: controller.emailError,
+ border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
+ contentPadding: EdgeInsets.symmetric(vertical: verticalPadding, horizontal: 16),
),
- 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,
- ),
- 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,
- ),
+ keyboardType: TextInputType.emailAddress,
+ ),
+ const SizedBox(height: 20),
+ TextField(
+ controller: controller.passwordController,
+ obscureText: controller.obscurePassword,
+ decoration: InputDecoration(
+ labelText: 'Palavra-passe',
+ prefixIcon: const Icon(Icons.lock_outlined),
+ errorText: controller.passwordError,
+ suffixIcon: IconButton(
+ icon: Icon(controller.obscurePassword
+ ? Icons.visibility_outlined
+ : Icons.visibility_off_outlined),
+ onPressed: controller.togglePasswordVisibility,
),
- obscureText: controller.obscurePassword,
- onChanged: (_) {
- if (controller.passwordError != null) {
- controller.validatePassword(controller.passwordController.text);
- }
- },
+ border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
+ contentPadding: EdgeInsets.symmetric(vertical: verticalPadding, horizontal: 16),
),
- 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
- ),
- ),
- ),
- ),
- ],
- );
- }
+ ),
+ ],
+ );
}
-
+}
class LoginButton extends StatelessWidget {
final LoginController controller;
final VoidCallback onLoginSuccess;
@@ -226,37 +153,42 @@
}
}
- class CreateAccountButton extends StatelessWidget {
- const CreateAccountButton({super.key});
+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
+ @override
+ Widget build(BuildContext context) {
+ final screenWidth = MediaQuery.of(context).size.width;
+ final buttonHeight = screenWidth > 600 ? 70.0 : 58.0;
+ final fontSize = screenWidth > 600 ? 22.0 : 18.0;
- 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
- ),
+ return SizedBox(
+ width: double.infinity,
+ height: buttonHeight,
+ child: OutlinedButton(
+ onPressed: () {
+ // Navega para a página de registo que criaste
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => const RegisterPage()),
+ );
+ },
+ style: OutlinedButton.styleFrom(
+ foregroundColor: const Color(0xFFE74C3C),
+ side: const BorderSide(color: Color(0xFFE74C3C), width: 2),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(14),
),
),
- );
- }
- }
+ child: Text(
+ 'Criar Conta',
+ style: TextStyle(
+ fontSize: fontSize,
+ fontWeight: FontWeight.w700,
+ ),
+ ),
+ ),
+ );
+ }
+}
+
\ No newline at end of file
diff --git a/lib/widgets/register_widgets.dart b/lib/widgets/register_widgets.dart
new file mode 100644
index 0000000..28f813e
--- /dev/null
+++ b/lib/widgets/register_widgets.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:playmaker/controllers/register_controller.dart';
+
+class RegisterFormFields extends StatelessWidget {
+ final RegisterController controller;
+
+ const RegisterFormFields({super.key, required this.controller});
+
+ @override
+ Widget build(BuildContext context) {
+ final verticalPadding = MediaQuery.of(context).size.width > 600 ? 22.0 : 16.0;
+
+ return Column(
+ children: [
+ // Campo Email
+ TextField(
+ controller: controller.emailController,
+ decoration: InputDecoration(
+ labelText: 'E-mail',
+ prefixIcon: const Icon(Icons.email_outlined),
+ errorText: controller.emailError,
+ border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
+ ),
+ ),
+ const SizedBox(height: 20),
+ // Campo Password
+ TextField(
+ controller: controller.passwordController,
+ obscureText: true,
+ decoration: InputDecoration(
+ labelText: 'Palavra-passe',
+ prefixIcon: const Icon(Icons.lock_outlined),
+ errorText: controller.passwordError,
+ border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
+ ),
+ ),
+ const SizedBox(height: 20),
+ // NOVO: Campo Confirmar Password
+ TextField(
+ controller: controller.confirmPasswordController,
+ obscureText: true,
+ decoration: InputDecoration(
+ labelText: 'Confirmar Palavra-passe',
+ prefixIcon: const Icon(Icons.lock_clock_outlined),
+ errorText: controller.confirmPasswordError, // Erro específico
+ border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
+ contentPadding: EdgeInsets.symmetric(vertical: verticalPadding, horizontal: 16),
+ ),
+ ),
+ ],
+ );
+ }
+}
\ No newline at end of file