login_form.dart
I check lang baiunknown
dart
a month ago
20 kB
2
Indexable
Never
import 'dart:async'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:web_application_team_space/Designs/Colors/palettes.dart'; import '../../../Users/Admin/Admin_hompage.dart'; import '../../../Users/Human Resource/HR_homepage.dart'; import '../../Resoponsive/login_color_constant.dart'; class LoginForm extends StatefulWidget { const LoginForm({super.key}); @override _LoginFormState createState() => _LoginFormState(); } class _LoginFormState extends State<LoginForm> { final _formKey = GlobalKey<FormState>(); final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); final TextEditingController _resetEmailController = TextEditingController(); bool _obscureText = true; bool _isLoading = false; String? _errorMessage; String? _emailError; String? _passwordError; String? _resetEmailError; int _attemptCounter = 0; final int _maxAttempts = 3; DateTime? _lockoutEndTime; Timer? _timer; @override void initState() { super.initState(); _loadLockoutData(); } @override void dispose() { _timer?.cancel(); super.dispose(); } Future<void> _loadLockoutData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String? lockoutEndTimeStr = prefs.getString('lockoutEndTime'); int? attemptCounter = prefs.getInt('attemptCounter'); if (lockoutEndTimeStr != null) { DateTime lockoutEndTime = DateTime.parse(lockoutEndTimeStr); if (DateTime.now().isBefore(lockoutEndTime)) { setState(() { _lockoutEndTime = lockoutEndTime; _attemptCounter = attemptCounter ?? 0; }); _startLockoutTimer(); _showLockoutDialog(); } else { _clearLockoutData(); } } } Future<void> _saveLockoutData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString('lockoutEndTime', _lockoutEndTime.toString()); await prefs.setInt('attemptCounter', _attemptCounter); } Future<void> _clearLockoutData() async { SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.remove('lockoutEndTime'); prefs.remove('attemptCounter'); setState(() { _lockoutEndTime = null; _attemptCounter = 0; }); } void _startLockoutTimer() { _timer?.cancel(); _timer = Timer.periodic(Duration(seconds: 1), (timer) { if (mounted) { setState(() {}); if (_lockoutEndTime == null || DateTime.now().isAfter(_lockoutEndTime!)) { timer.cancel(); _clearLockoutData(); if (Navigator.canPop(context)) { Navigator.of(context).pop(); // Close the dialog when the lockout ends } } } }); } void _showLockoutDialog() { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return WillPopScope( onWillPop: () async => false, // Prevent the dialog from closing child: AlertDialog( backgroundColor: Colors.white, contentPadding: EdgeInsets.symmetric(horizontal: 24.0, vertical: 20.0), content: Column( mainAxisSize: MainAxisSize.min, children: [ Center( child: Image.asset( 'assets/images/warning.gif', height: 60, // Adjust height as needed width: 60, // Adjust width as needed ), ), SizedBox(height: 20), Text( "Too Many Attempts", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 18, // Adjust font size as needed ), textAlign: TextAlign.center, ), SizedBox(height: 20), Text( "Please wait for the timer to expire before retrying.", textAlign: TextAlign.center, ), SizedBox(height: 20), StreamBuilder<int>( stream: Stream.periodic(Duration(seconds: 1), (_) { return _lockoutEndTime != null ? _lockoutEndTime!.difference(DateTime.now()).inSeconds : 0; }), builder: (context, snapshot) { return Text( "${snapshot.data ?? 0} seconds remaining", style: TextStyle(fontWeight: FontWeight.bold), ); }, ), ], ), ), ); }, ); } Future<DocumentSnapshot> getUserDocumentWithRetry(String userId, {int retryCount = 3}) async { int attempt = 0; while (attempt < retryCount) { try { return await FirebaseFirestore.instance .collection('users') .doc(userId) .get(); } catch (e) { attempt++; if (attempt >= retryCount) { rethrow; } await Future.delayed(Duration(seconds: attempt * attempt)); } } throw Exception('Failed to retrieve document after $retryCount attempts'); } String getErrorMessage(String errorCode) { switch (errorCode) { case 'firebase_auth/invalid-email': return 'The email address is not valid.'; case 'firebase_auth/user-disabled': return 'The user account has been disabled.'; case 'firebase_auth/user-not-found': return 'No user found with this email.'; case 'firebase_auth/wrong-password': return 'Incorrect password. Please try again.'; case 'firebase_auth/missing-email': return 'Email address is required.'; case 'firebase_auth/missing-password': return 'Password is required.'; default: return 'An unknown error occurred. Please try again.'; } } Future<void> _login(BuildContext context) async { setState(() { _emailError = _emailController.text.isEmpty ? "Email address is required." : null; _passwordError = _passwordController.text.isEmpty ? "Password is required." : null; _errorMessage = null; }); if (_emailError != null || _passwordError != null) { return; } if (_lockoutEndTime != null && DateTime.now().isBefore(_lockoutEndTime!)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Please wait until ${_lockoutEndTime!.minute}:${_lockoutEndTime!.second} to retry'), ), ); return; } setState(() { _isLoading = true; }); try { UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword( email: _emailController.text, password: _passwordController.text, ); User? user = userCredential.user; if (user != null) { DocumentSnapshot userDoc = await getUserDocumentWithRetry(user.uid); String role = userDoc['role']; SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool('isLoggedIn', true); await prefs.setString('userRole', role); if (role == 'Admin') { Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const AdminHompage())); } else if (role == 'Human Resource') { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => const HumanResourceHomepage())); } _clearLockoutData(); // Clear lockout data on successful login } } catch (e) { _attemptCounter++; int reattemptsLeft = _maxAttempts - _attemptCounter; if (_attemptCounter >= _maxAttempts) { _lockoutEndTime = DateTime.now().add(Duration(minutes: 1)); _attemptCounter = 0; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( 'Too many attempts. Please wait for 1 minute before retrying.'), backgroundColor: Colors.red, ), ); await _saveLockoutData(); _startLockoutTimer(); _showLockoutDialog(); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Please check your Email or Password.\nYou have $reattemptsLeft reattempts left.', style: TextStyle(color: Colors.white), ), backgroundColor: Colors.red, ), ); } setState(() { _errorMessage = getErrorMessage(e.toString()); }); } finally { setState(() { _isLoading = false; }); } } Future<void> _resetPassword() async { String email = _resetEmailController.text; if (email.isEmpty) { setState(() { _resetEmailError = "Please enter your email address."; }); return; } setState(() { _resetEmailError = null; }); try { await FirebaseAuth.instance.sendPasswordResetEmail(email: email); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), title: Text('Password Reset'), content: Text('A password reset link has been sent to $email.'), actions: <Widget>[ TextButton( child: Text('OK', style: TextStyle(color: kPrimaryColor)), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } catch (e) { setState(() { _resetEmailError = getErrorMessage(e.toString()); }); } } void _showResetPasswordDialog() { setState(() { _resetEmailError = null; }); showDialog( context: context, builder: (BuildContext context) { return StatefulBuilder( builder: (context, setState) { return AlertDialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10.0), ), title: Column( children: [ Image.asset('assets/images/bpsc.png', height: 100), SizedBox(height: 10), Text('Forgot your password?'), ], ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Enter your Email and we’ll help you reset your password.'), SizedBox(height: 10), TextField( controller: _resetEmailController, keyboardType: TextInputType.emailAddress, decoration: InputDecoration( hintText: "Enter Email ", border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide(color: kPrimaryColor), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: BorderSide(color: kPrimaryColor), ), errorText: _resetEmailError, ), ), ], ), actions: <Widget>[ TextButton( child: Text('Cancel', style: TextStyle(color: Colors.black)), style: TextButton.styleFrom( foregroundColor: Colors.white, backgroundColor: Palettes.blueVeryPale, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), ), onPressed: () { Navigator.of(context).pop(); }, ), ElevatedButton( onPressed: () { setState(() { _resetEmailError = null; }); _resetPassword(); }, child: Text('Reset'), style: TextButton.styleFrom( foregroundColor: Colors.white, backgroundColor: Palettes.green, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), )), ], ); }, ); }, ); } @override Widget build(BuildContext context) { bool isLockedOut = _lockoutEndTime != null && DateTime.now().isBefore(_lockoutEndTime!); return Stack( children: [ Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: _emailController, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, cursorColor: kPrimaryColor, decoration: InputDecoration( hintText: "Your email", prefixIcon: const Padding( padding: EdgeInsets.all(defaultPadding), child: Icon(Icons.person, color: Colors.black), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: const BorderSide(color: Colors.black), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: const BorderSide(color: kPrimaryColor), ), errorText: _emailError, ), ), Padding( padding: const EdgeInsets.symmetric(vertical: defaultPadding), child: TextFormField( controller: _passwordController, textInputAction: TextInputAction.done, obscureText: _obscureText, cursorColor: kPrimaryColor, decoration: InputDecoration( hintText: "Your password", prefixIcon: const Padding( padding: EdgeInsets.all(defaultPadding), child: Icon(Icons.lock, color: Colors.black), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: const BorderSide(color: Colors.black), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10.0), borderSide: const BorderSide(color: kPrimaryColor), ), suffixIcon: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 1, height: 24, color: Colors.grey, ), const SizedBox(width: 10), GestureDetector( onTap: () { setState(() { _obscureText = !_obscureText; }); }, child: Icon( _obscureText ? Icons.visibility : Icons.visibility_off, color: Colors.black, ), ), const SizedBox(width: 10), ], ), errorText: _passwordError, ), ), ), Align( alignment: Alignment.centerLeft, child: TextButton( onPressed: _showResetPasswordDialog, style: ButtonStyle( overlayColor: MaterialStateProperty.all(Colors.transparent), ), child: const Text( "Forgot Password?", style: TextStyle( color: kPrimaryColor, fontWeight: FontWeight.bold), ), ), ), const SizedBox(height: defaultPadding), SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: isLockedOut ? null : () => _login(context), // Disable button if locked out style: ElevatedButton.styleFrom( backgroundColor: isLockedOut ? Colors.grey : Palettes.green, // Change color if locked out foregroundColor: Palettes.white, padding: const EdgeInsets.symmetric( horizontal: 40, vertical: 15), textStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), elevation: 2, ), icon: const Icon(Icons.login), label: const Text("Login"), ), ), // if (_errorMessage != null) ...[ // Padding( // padding: const EdgeInsets.only(top: defaultPadding), // child: Text( // _errorMessage!, // style: const TextStyle(color: Colors.red), // ), // ), // ], ], ), ), if (_isLoading) const Center( child: CircularProgressIndicator(), ), ], ); } }
Leave a Comment