diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index bb074e0..3c56346 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -29,6 +29,12 @@
+
+
+
+
+
+
diff --git a/lib/main.dart b/lib/main.dart
index 66dac4d..d9bdd64 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -2,17 +2,21 @@ import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'screens/auth_screen.dart';
+import 'screens/home_screen.dart';
void main() async {
-
WidgetsFlutterBinding.ensureInitialized();
- await Supabase.initialize(
- url: 'https://pkaerjfdwgquztmsrfhy.supabase.co',
- anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBrYWVyamZkd2dxdXp0bXNyZmh5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAyODU1NjMsImV4cCI6MjA3NTg2MTU2M30.tNl04Rn-GquTF_hse0ea8OKNo9cJKAGVDoXP3ZVLSRg',
- );
+ try {
+ await Supabase.initialize(
+ url: 'https://pkaerjfdwgquztmsrfhy.supabase.co',
+ anonKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBrYWVyamZkd2dxdXp0bXNyZmh5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAyODU1NjMsImV4cCI6MjA3NTg2MTU2M30.tNl04Rn-GquTF_hse0ea8OKNo9cJKAGVDoXP3ZVLSRg',
+ );
+ } catch (e) {
+ debugPrint('Supabase initialization error: $e');
+ }
- runApp(TaskTrackerApp());
+ runApp(const TaskTrackerApp());
}
final supabase = Supabase.instance.client;
@@ -81,6 +85,10 @@ class TaskTrackerApp extends StatelessWidget {
),
),
home: AuthScreen(),
+ routes: {
+ '/auth': (context) => const AuthScreen(),
+ '/home': (context) => const HomeScreen(),
+ },
);
}
}
diff --git a/lib/screens/auth_screen.dart b/lib/screens/auth_screen.dart
index 3b67622..ff74b7e 100644
--- a/lib/screens/auth_screen.dart
+++ b/lib/screens/auth_screen.dart
@@ -1,11 +1,15 @@
import 'dart:async';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'login_screen.dart';
import 'register_screen.dart';
+import '../services/auth_service.dart';
+
+import 'dart:io' show Platform;
class AuthScreen extends StatefulWidget {
const AuthScreen({super.key});
@@ -17,13 +21,18 @@ class AuthScreen extends StatefulWidget {
class _AuthScreenState extends State
with SingleTickerProviderStateMixin {
final SupabaseClient supabase = Supabase.instance.client;
+ final AuthService _authService = AuthService();
late TabController _tabController;
+ bool _isLoading = true;
@override
void initState() {
super.initState();
+ debugPrint('AuthScreen initState started');
_tabController = TabController(length: 2, vsync: this);
_setupAuthListener();
+ _checkAutoLogin();
+ debugPrint('AuthScreen initState completed');
}
@@ -37,23 +46,68 @@ class _AuthScreenState extends State
supabase.auth.onAuthStateChange.listen((data) {
final event = data.event;
if (event == AuthChangeEvent.signedIn) {
- Navigator.of(context).pushReplacement(
- MaterialPageRoute(
- builder: (context) => const Scaffold(
- body: Center(
- child: Text('Signed in'),
- ),
- ),
- ),
- );
+ setState(() {
+ _isLoading = false;
+ });
+ // Navigate to home screen
+ if (mounted) {
+ Navigator.of(context).pushReplacementNamed('/home');
+ }
+ } else if (event == AuthChangeEvent.signedOut) {
+ setState(() {
+ _isLoading = false;
+ });
}
});
}
-Future _googleSignIn() async {
+
+ Future _checkAutoLogin() async {
+ debugPrint('_checkAutoLogin started');
+ try {
+ // Add timeout to prevent hanging
+ await Future.any([
+ _performAutoLogin(),
+ Future.delayed(const Duration(seconds: 10)),
+ ]);
+ debugPrint('_checkAutoLogin completed');
+ } catch (e) {
+ debugPrint('_checkAutoLogin error: $e');
+ setState(() {
+ _isLoading = false;
+ });
+ }
+ }
+
+ Future _performAutoLogin() async {
+ debugPrint('_performAutoLogin started');
+ // Check if user is already authenticated
+ if (_authService.isAuthenticated) {
+ debugPrint('User already authenticated, navigating to home');
+ setState(() {
+ _isLoading = false;
+ });
+ // Navigate to home screen immediately
+ if (mounted) {
+ Navigator.of(context).pushReplacementNamed('/home');
+ }
+ return;
+ }
+
+ debugPrint('Attempting auto-login with saved credentials');
+ // Try auto-login with saved credentials
+ final success = await _authService.autoLogin();
+ debugPrint('Auto-login result: $success');
+ setState(() {
+ _isLoading = false;
+ });
+ // Let the auth listener handle navigation if successful
+ }
+
+Future googleSignInNative() async {
/// TODO: update the Web client ID with your own.
///
/// Web Client ID that you registered with Google Cloud.
- const webClientId = '311478513323-n2qut52hb4ms7g6g6r5ukni4mr49p6ff.apps.googleusercontent.com';
+ const webClientId = '311478513323-4vj6v6d254jbs8tt334u4874see8i4vj.apps.googleusercontent.com';
/// TODO: update the iOS client ID with your own.
///
@@ -69,7 +123,11 @@ Future _googleSignIn() async {
);
final googleUser = await signIn.signIn();
- final googleAuth = await googleUser!.authentication;
+ if (googleUser == null) {
+ throw Exception('Google sign-in was cancelled');
+ }
+
+ final googleAuth = await googleUser.authentication;
final accessToken = googleAuth.accessToken;
final idToken = googleAuth.idToken;
@@ -80,114 +138,86 @@ Future _googleSignIn() async {
throw Exception('No id token found');
}
- return supabase.auth.signInWithIdToken(
+ final response = await supabase.auth.signInWithIdToken(
provider: OAuthProvider.google,
idToken: idToken,
accessToken: accessToken,
);
-}
- Future login(String email, String password) async {
+ // Save user information after successful OAuth login
+ final user = response.user;
+ if (user != null) {
+ final userName = googleUser.displayName ?? user.userMetadata?['name'] ?? 'User';
+ final userEmail = googleUser.email;
+ await _authService.saveUserInfo(
+ name: userName,
+ email: userEmail,
+ );
}
- Future register(String email, String password, String name) async {
-
+ return response;
+}
+
+Future oauthLoginWeb(OAuthProvider provider) async {
+ await supabase.auth.signInWithOAuth(
+ provider,
+ redirectTo: kIsWeb ? null : 'com.Xperience.TaskTracker.tasktracker://callback',
+);
+}
+
+ Future login(String email, String password, bool rememberMe) async {
+ await _authService.loginWithPassword(
+ email: email,
+ password: password,
+ rememberMe: rememberMe,
+ );
+ }
+
+ Future register(String email, String password, String name, bool rememberMe) async {
+ await _authService.registerUser(
+ email: email,
+ password: password,
+ name: name,
+ rememberMe: rememberMe,
+ );
}
Future logout() async {
-
+ await _authService.logout();
}
- Future oAuthLogin() async {
- await _googleSignIn();
+ Future oAuthLogin(OAuthProvider provider) async {
+ try {
+ if(Platform.isAndroid && provider == OAuthProvider.google){
+ await googleSignInNative();
+ } else {
+ await oauthLoginWeb(provider);
+ }
+ } catch (e) {
+ // Handle OAuth login errors
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('OAuth login failed: ${e.toString()}'),
+ backgroundColor: Colors.red,
+ ),
+ );
+ }
+ }
}
- bool isLogged= false;
@override
Widget build(BuildContext context) {
-
final screenHeight = MediaQuery.of(context).size.height;
- // If user is logged in, show logout screen
- if (isLogged) {
+ // Show loading screen while checking authentication
+ if (_isLoading) {
return Scaffold(
backgroundColor: Colors.grey[50],
- body: SafeArea(
- child: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Container(
- width: 80,
- height: 80,
- decoration: BoxDecoration(
- color: Theme.of(context).primaryColor,
- borderRadius: BorderRadius.circular(20),
- boxShadow: [
- BoxShadow(
- color: Theme.of(context).primaryColor.withValues(alpha: 0.3),
- blurRadius: 20,
- offset: const Offset(0, 10),
- ),
- ],
- ),
- child: const Icon(
- Icons.task_alt,
- color: Colors.white,
- size: 40,
- ),
- ),
- const SizedBox(height: 24),
- Text(
- 'Welcome back!',
- style: GoogleFonts.poppins(
- fontSize: 28,
- fontWeight: FontWeight.bold,
- color: Colors.grey[800],
- ),
- ),
- const SizedBox(height: 8),
- Text(
- "username",
- style: GoogleFonts.poppins(
- fontSize: 20,
- fontWeight: FontWeight.w600,
- color: Theme.of(context).primaryColor,
- ),
- ),
- const SizedBox(height: 4),
- Text(
- "email or id",
- style: GoogleFonts.poppins(
- fontSize: 16,
- color: Colors.grey[600],
- ),
- ),
- const SizedBox(height: 40),
- ElevatedButton(
- onPressed: logout,
- style: ElevatedButton.styleFrom(
- backgroundColor: Colors.red,
- foregroundColor: Colors.white,
- padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- elevation: 2,
- ),
- child: Text(
- 'Logout',
- style: GoogleFonts.poppins(
- fontSize: 16,
- fontWeight: FontWeight.w600,
- ),
- ),
- ),
- ],
- ),
- ),
+ body: const Center(
+ child: CircularProgressIndicator(),
),
);
}
@@ -295,11 +325,11 @@ Future _googleSignIn() async {
children: [
LoginScreen(
onLogin: login,
- onOAuthLogin: () => oAuthLogin(),
+ onOAuthLogin: (provider) => oAuthLogin(provider),
),
RegisterScreen(
onRegister: register,
- onOAuthRegister: () => oAuthLogin(),
+ onOAuthRegister: (provider) => oAuthLogin(provider),
),
],
),
diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart
new file mode 100644
index 0000000..0e538ad
--- /dev/null
+++ b/lib/screens/home_screen.dart
@@ -0,0 +1,416 @@
+import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
+import '../services/auth_service.dart';
+import '../widgets/profile_avatar.dart';
+
+class HomeScreen extends StatefulWidget {
+ const HomeScreen({super.key});
+
+ @override
+ State createState() => _HomeScreenState();
+}
+
+class _HomeScreenState extends State {
+ final AuthService _authService = AuthService();
+ Map? _userInfo;
+
+ @override
+ void initState() {
+ super.initState();
+ _loadUserInfo();
+ }
+
+ Future _loadUserInfo() async {
+ final userInfo = await _authService.getUserInfo();
+ setState(() {
+ _userInfo = userInfo;
+ });
+ }
+
+ Future _handleLogout() async {
+ await _authService.logout();
+ if (mounted) {
+ Navigator.of(context).pushReplacementNamed('/auth');
+ }
+ }
+
+ void _showProfileMenu() {
+ showModalBottomSheet(
+ context: context,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
+ ),
+ builder: (context) => Container(
+ padding: const EdgeInsets.all(24),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ // Handle bar
+ Container(
+ width: 40,
+ height: 4,
+ decoration: BoxDecoration(
+ color: Colors.grey[300],
+ borderRadius: BorderRadius.circular(2),
+ ),
+ ),
+ const SizedBox(height: 24),
+
+ // Profile info
+ ProfileAvatar(
+ name: _userInfo?['name'],
+ size: 80,
+ ),
+ const SizedBox(height: 16),
+
+ Text(
+ _userInfo?['name'] ?? 'User',
+ style: GoogleFonts.poppins(
+ fontSize: 20,
+ fontWeight: FontWeight.w600,
+ color: Colors.grey[800],
+ ),
+ ),
+ const SizedBox(height: 4),
+
+ Text(
+ _userInfo?['email'] ?? 'user@example.com',
+ style: GoogleFonts.poppins(
+ fontSize: 14,
+ color: Colors.grey[600],
+ ),
+ ),
+ const SizedBox(height: 32),
+
+ // Menu items
+ ListTile(
+ leading: const Icon(Icons.person_outline, color: Color(0xFF6366F1)),
+ title: Text(
+ 'Edit Profile',
+ style: GoogleFonts.poppins(fontWeight: FontWeight.w500),
+ ),
+ onTap: () {
+ Navigator.pop(context);
+ // TODO: Navigate to edit profile
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Edit Profile coming soon!')),
+ );
+ },
+ ),
+
+ ListTile(
+ leading: const Icon(Icons.settings_outlined, color: Color(0xFF6366F1)),
+ title: Text(
+ 'Settings',
+ style: GoogleFonts.poppins(fontWeight: FontWeight.w500),
+ ),
+ onTap: () {
+ Navigator.pop(context);
+ // TODO: Navigate to settings
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Settings coming soon!')),
+ );
+ },
+ ),
+
+ const Divider(),
+
+ ListTile(
+ leading: const Icon(Icons.logout, color: Colors.red),
+ title: Text(
+ 'Logout',
+ style: GoogleFonts.poppins(
+ fontWeight: FontWeight.w500,
+ color: Colors.red,
+ ),
+ ),
+ onTap: () {
+ Navigator.pop(context);
+ _handleLogout();
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.grey[50],
+ body: SafeArea(
+ child: Column(
+ children: [
+ // Header
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withValues(alpha: 0.05),
+ blurRadius: 10,
+ offset: const Offset(0, 2),
+ ),
+ ],
+ ),
+ child: Row(
+ children: [
+ // Greeting
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Hello,',
+ style: GoogleFonts.poppins(
+ fontSize: 16,
+ color: Colors.grey[600],
+ ),
+ ),
+ const SizedBox(height: 2),
+ Text(
+ _userInfo?['name'] ?? 'User',
+ style: GoogleFonts.poppins(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ color: Colors.grey[800],
+ ),
+ ),
+ ],
+ ),
+ ),
+
+ // Profile Avatar
+ ProfileAvatar(
+ name: _userInfo?['name'],
+ size: 48,
+ onTap: _showProfileMenu,
+ ),
+ ],
+ ),
+ ),
+
+ // Main Content
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.all(24),
+ child: Column(
+ children: [
+ // Welcome Card
+ Container(
+ width: double.infinity,
+ padding: const EdgeInsets.all(24),
+ decoration: BoxDecoration(
+ gradient: const LinearGradient(
+ colors: [Color(0xFF6366F1), Color(0xFF8B5CF6)],
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ ),
+ borderRadius: BorderRadius.circular(16),
+ boxShadow: [
+ BoxShadow(
+ color: const Color(0xFF6366F1).withValues(alpha: 0.3),
+ blurRadius: 20,
+ offset: const Offset(0, 10),
+ ),
+ ],
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: Colors.white.withValues(alpha: 0.2),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: const Icon(
+ Icons.task_alt,
+ color: Colors.white,
+ size: 24,
+ ),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Welcome to TaskTracker!',
+ style: GoogleFonts.poppins(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ color: Colors.white,
+ ),
+ ),
+ const SizedBox(height: 4),
+ Text(
+ 'Let\'s get things done',
+ style: GoogleFonts.poppins(
+ fontSize: 14,
+ color: Colors.white.withValues(alpha: 0.9),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+
+ const SizedBox(height: 32),
+
+ // Quick Actions
+ Text(
+ 'Quick Actions',
+ style: GoogleFonts.poppins(
+ fontSize: 20,
+ fontWeight: FontWeight.bold,
+ color: Colors.grey[800],
+ ),
+ ),
+
+ const SizedBox(height: 16),
+
+ // Action Cards
+ Row(
+ children: [
+ Expanded(
+ child: _buildActionCard(
+ icon: Icons.add_task,
+ title: 'Add Task',
+ subtitle: 'Create new task',
+ color: const Color(0xFF10B981),
+ onTap: () {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Add Task coming soon!')),
+ );
+ },
+ ),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: _buildActionCard(
+ icon: Icons.list_alt,
+ title: 'View Tasks',
+ subtitle: 'See all tasks',
+ color: const Color(0xFF3B82F6),
+ onTap: () {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('View Tasks coming soon!')),
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+
+ const SizedBox(height: 16),
+
+ Row(
+ children: [
+ Expanded(
+ child: _buildActionCard(
+ icon: Icons.analytics_outlined,
+ title: 'Analytics',
+ subtitle: 'View progress',
+ color: const Color(0xFF8B5CF6),
+ onTap: () {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Analytics coming soon!')),
+ );
+ },
+ ),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: _buildActionCard(
+ icon: Icons.settings_outlined,
+ title: 'Settings',
+ subtitle: 'App settings',
+ color: const Color(0xFFF59E0B),
+ onTap: () {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Settings coming soon!')),
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildActionCard({
+ required IconData icon,
+ required String title,
+ required String subtitle,
+ required Color color,
+ required VoidCallback onTap,
+ }) {
+ return GestureDetector(
+ onTap: onTap,
+ child: Container(
+ padding: const EdgeInsets.all(20),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(16),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withValues(alpha: 0.05),
+ blurRadius: 10,
+ offset: const Offset(0, 2),
+ ),
+ ],
+ ),
+ child: Column(
+ children: [
+ Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: color.withValues(alpha: 0.1),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: Icon(
+ icon,
+ color: color,
+ size: 24,
+ ),
+ ),
+ const SizedBox(height: 12),
+ Text(
+ title,
+ style: GoogleFonts.poppins(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: Colors.grey[800],
+ ),
+ ),
+ const SizedBox(height: 4),
+ Text(
+ subtitle,
+ style: GoogleFonts.poppins(
+ fontSize: 12,
+ color: Colors.grey[600],
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart
index 1d4d2ba..09161d5 100644
--- a/lib/screens/login_screen.dart
+++ b/lib/screens/login_screen.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
+import 'package:supabase_flutter/supabase_flutter.dart';
import '../widgets/oauth_button.dart';
import '../widgets/custom_text_field.dart';
@@ -11,8 +12,8 @@ class LoginScreen extends StatefulWidget {
super.key,
});
- final Future Function(String email, String password) onLogin;
- final void Function() onOAuthLogin;
+ final Future Function(String email, String password, bool rememberMe) onLogin;
+ final void Function(OAuthProvider) onOAuthLogin;
@override
State createState() => _LoginScreenState();
@@ -24,6 +25,7 @@ class _LoginScreenState extends State {
final _passwordController = TextEditingController();
bool _isPasswordVisible = false;
bool _isLoading = false;
+ bool _rememberMe = false;
@override
void dispose() {
@@ -39,7 +41,7 @@ class _LoginScreenState extends State {
});
try {
- await widget.onLogin(_emailController.text, _passwordController.text);
+ await widget.onLogin(_emailController.text, _passwordController.text, _rememberMe);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
@@ -67,8 +69,8 @@ class _LoginScreenState extends State {
}
}
- void _handleOAuthLogin( ) {
- widget.onOAuthLogin();
+ void _handleOAuthLogin(OAuthProvider provider) {
+ widget.onOAuthLogin(provider);
}
@override
@@ -130,24 +132,44 @@ class _LoginScreenState extends State {
const SizedBox(height: 8),
- // Forgot Password
- Align(
- alignment: Alignment.centerRight,
- child: TextButton(
- onPressed: () {
- // TODO: Implement forgot password
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('Forgot password functionality will be implemented')),
- );
- },
- child: Text(
- 'Forgot Password?',
+ // Remember Me and Forgot Password Row
+ Row(
+ children: [
+ // Remember Me Checkbox
+ Checkbox(
+ value: _rememberMe,
+ onChanged: (value) {
+ setState(() {
+ _rememberMe = value ?? false;
+ });
+ },
+ activeColor: Theme.of(context).primaryColor,
+ ),
+ Text(
+ 'Remember me',
style: GoogleFonts.poppins(
- color: Theme.of(context).primaryColor,
- fontWeight: FontWeight.w500,
+ color: Colors.grey[600],
+ fontSize: 14,
),
),
- ),
+ const Spacer(),
+ // Forgot Password
+ TextButton(
+ onPressed: () {
+ // TODO: Implement forgot password
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('Forgot password functionality will be implemented')),
+ );
+ },
+ child: Text(
+ 'Forgot Password?',
+ style: GoogleFonts.poppins(
+ color: Theme.of(context).primaryColor,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ],
),
const SizedBox(height: 24),
@@ -218,23 +240,23 @@ class _LoginScreenState extends State {
OAuthButton(
provider: 'Google',
icon: Icons.g_mobiledata,
- onPressed: () => _handleOAuthLogin(),
+ onPressed: () => _handleOAuthLogin(OAuthProvider.google),
),
const SizedBox(height: 12),
- OAuthButton(
- provider: 'Facebook',
- icon: Icons.facebook,
- onPressed: () => _handleOAuthLogin(),
- ),
+ // OAuthButton(
+ // provider: 'Facebook',
+ // icon: Icons.facebook,
+ // onPressed: () => _handleOAuthLogin(OAuthProvider.facebook),
+ // ),
- const SizedBox(height: 12),
+ // const SizedBox(height: 12),
OAuthButton(
provider: 'GitHub',
icon: Icons.code,
- onPressed: () => _handleOAuthLogin(),
+ onPressed: () => _handleOAuthLogin(OAuthProvider.github),
),
],
),
diff --git a/lib/screens/register_screen.dart b/lib/screens/register_screen.dart
index fa757dc..df6905c 100644
--- a/lib/screens/register_screen.dart
+++ b/lib/screens/register_screen.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../widgets/oauth_button.dart';
import '../widgets/custom_text_field.dart';
+import 'package:supabase_flutter/supabase_flutter.dart';
class RegisterScreen extends StatefulWidget {
const RegisterScreen({
@@ -10,8 +11,8 @@ class RegisterScreen extends StatefulWidget {
super.key,
});
- final Future Function(String email, String password, String name) onRegister;
- final void Function() onOAuthRegister;
+ final Future Function(String email, String password, String name, bool rememberMe) onRegister;
+ final void Function(OAuthProvider) onOAuthRegister;
@override
State createState() => _RegisterScreenState();
@@ -27,6 +28,7 @@ class _RegisterScreenState extends State {
bool _isConfirmPasswordVisible = false;
bool _isLoading = false;
bool _agreeToTerms = false;
+ bool _rememberMe = false;
@override
void dispose() {
@@ -48,6 +50,7 @@ class _RegisterScreenState extends State {
_emailController.text,
_passwordController.text,
_nameController.text,
+ _rememberMe,
);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -80,8 +83,8 @@ class _RegisterScreenState extends State {
}
}
- void _handleOAuthRegister() {
- widget.onOAuthRegister();
+ void _handleOAuthRegister(OAuthProvider provider) {
+ widget.onOAuthRegister(provider);
}
@override
@@ -195,6 +198,30 @@ class _RegisterScreenState extends State {
const SizedBox(height: 16),
+ // Remember Me Checkbox
+ Row(
+ children: [
+ Checkbox(
+ value: _rememberMe,
+ onChanged: (value) {
+ setState(() {
+ _rememberMe = value ?? false;
+ });
+ },
+ activeColor: Theme.of(context).primaryColor,
+ ),
+ Text(
+ 'Remember me',
+ style: GoogleFonts.poppins(
+ color: Colors.grey[600],
+ fontSize: 14,
+ ),
+ ),
+ ],
+ ),
+
+ const SizedBox(height: 16),
+
// Terms and Conditions
Row(
children: [
@@ -306,23 +333,23 @@ class _RegisterScreenState extends State {
OAuthButton(
provider: 'Google',
icon: Icons.g_mobiledata,
- onPressed: () => _handleOAuthRegister(),
+ onPressed: () => _handleOAuthRegister(OAuthProvider.google),
),
const SizedBox(height: 12),
- OAuthButton(
- provider: 'Facebook',
- icon: Icons.facebook,
- onPressed: () => _handleOAuthRegister(),
- ),
+ // OAuthButton(
+ // provider: 'Facebook',
+ // icon: Icons.facebook,
+ // onPressed: () => _handleOAuthRegister(OAuthProvider.facebook),
+ // ),
- const SizedBox(height: 12),
+ // const SizedBox(height: 12),
OAuthButton(
provider: 'GitHub',
icon: Icons.code,
- onPressed: () => _handleOAuthRegister(),
+ onPressed: () => _handleOAuthRegister(OAuthProvider.github),
),
],
),
diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart
new file mode 100644
index 0000000..21cea84
--- /dev/null
+++ b/lib/services/auth_service.dart
@@ -0,0 +1,214 @@
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:supabase_flutter/supabase_flutter.dart';
+import 'package:flutter/foundation.dart';
+
+class AuthService {
+ static const String _emailKey = 'saved_email';
+ static const String _passwordKey = 'saved_password';
+ static const String _rememberMeKey = 'remember_me';
+ static const String _userNameKey = 'user_name';
+ static const String _userEmailKey = 'user_email';
+
+ final SupabaseClient _supabase = Supabase.instance.client;
+
+ /// Save login credentials to SharedPreferences
+ Future saveCredentials({
+ required String email,
+ required String password,
+ required bool rememberMe,
+ }) async {
+ final prefs = await SharedPreferences.getInstance();
+
+ if (rememberMe) {
+ await prefs.setString(_emailKey, email);
+ await prefs.setString(_passwordKey, password);
+ await prefs.setBool(_rememberMeKey, true);
+ } else {
+ // Clear saved credentials if user doesn't want to be remembered
+ await prefs.remove(_emailKey);
+ await prefs.remove(_passwordKey);
+ await prefs.setBool(_rememberMeKey, false);
+ }
+ }
+
+ /// Get saved login credentials
+ Future