import 'dart:async'; import 'dart:io'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:pulse/Notifier/auth_provider.dart'; import 'package:pulse/utils/AppColors.dart'; import 'package:pulse/utils/SharedpreferencesService.dart'; import 'package:pulse/utils/customSnackBar.dart'; import '../home_screen.dart'; import 'forgot_password_screen.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); bool rememberMe = false; bool _obscurePassword = true; double _opacity = 0.0; double _scale = 0.9; Timer? _connectivityTimer; bool _progressCheckCompleted = false; bool _hasInternet = true; @override void initState() { super.initState(); // Initialize connectivity check _initConnectivity(); // Start animations Future.delayed(const Duration(milliseconds: 300), () { setState(() { _opacity = 1.0; _scale = 1.0; }); }); //_checkSavedLogin(); } Future _initConnectivity() async { try { // Initial connectivity check await _checkConnectivity(); // Use periodic checks instead of stream _connectivityTimer = Timer.periodic(const Duration(seconds: 3), (timer) { _checkConnectivity(); }); } catch (e) { debugPrint("Connectivity initialization error: $e"); _updateConnectionStatus(false); } } Future _checkConnectivity() async { try { // Method 1: Using connectivity_plus final connectivity = Connectivity(); final results = await connectivity.checkConnectivity(); final hasInternet = results.any((result) => result != ConnectivityResult.none); // Method 2: Fallback with socket test if (hasInternet) { try { final result = await InternetAddress.lookup('google.com'); final socketCheck = result.isNotEmpty && result[0].rawAddress.isNotEmpty; _updateConnectionStatus(socketCheck); } catch (e) { _updateConnectionStatus(false); } } else { _updateConnectionStatus(false); } } catch (e) { debugPrint("Connectivity check error: $e"); _updateConnectionStatus(false); } } void _updateConnectionStatus(bool hasInternet) { if (mounted) { setState(() { _hasInternet = hasInternet; }); } if (!hasInternet) { _showNoInternetSnackbar(); } else { // Dismiss the warning snackbar if internet is restored ScaffoldMessenger.of(context).hideCurrentSnackBar(); if (!_progressCheckCompleted) { // _startLoginCheck(); } } } void _showNoInternetSnackbar() { if (mounted) { WidgetsBinding.instance.addPostFrameCallback((_) { // Hide any existing snackbar ScaffoldMessenger.of(context).hideCurrentSnackBar(); // Show custom snackbar with all required params CustomSnackBar.show( context: context, title: "Connection Error", message: "No internet connection! Please connect to the internet.", icon: Icons.wifi_off, backgroundColor: Colors.redAccent, ); }); } } /// Check if credentials exist in SharedPreferences // Future _checkSavedLogin() async { // final savedEmail = await prefs.getString("email"); // final savedPassword = await prefs.getString("password"); // // if (savedEmail != null && savedPassword != null) { // _emailController.text = savedEmail; // _passwordController.text = savedPassword; // rememberMe = true; // // // Auto-login after a brief delay to show UI // await Future.delayed(const Duration(milliseconds: 500)); // // final loginProvider = Provider.of(context, listen: false); // await loginProvider.loginUser( // context, // csrfToken: "2fb317d960000fe130fb0e99cee97aa9", // email: savedEmail, // password: savedPassword, // ); // // if (loginProvider.isLoggedIn) { // Navigator.pushReplacement( // context, // PageRouteBuilder( // pageBuilder: (context, animation, secondaryAnimation) => HomeScreen( // sessionId: loginProvider.sessionId!, // staffId: loginProvider.staffId.toString(), // ), // transitionsBuilder: (context, animation, secondaryAnimation, child) { // return FadeTransition( // opacity: animation, // child: child, // ); // }, // transitionDuration: const Duration(milliseconds: 500), // ), // ); // } // // } // } final prefs = SharedPreferencesService.instance; Future _login() async { if (_formKey.currentState!.validate()) { final email = _emailController.text.trim(); final password = _passwordController.text.trim(); final loginProvider = Provider.of(context, listen: false); await loginProvider.loginUser( context, csrfToken: "2fb317d960000fe130fb0e99cee97aa9", email: email, password: password, ); if (loginProvider.isLoggedIn) { // Save login info if remember me is checked if (loginProvider.isLoggedIn) { await prefs.saveString("email", email); await prefs.saveString("password", password); await prefs.saveString("sessionId", loginProvider.sessionId ?? ""); await prefs.saveString("staffId", loginProvider.staffId.toString()); } Navigator.pushReplacement( context, PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => HomeScreen( sessionId: loginProvider.sessionId!, staffId: loginProvider.staffId.toString(), ), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation, child: child, ); }, transitionDuration: const Duration(milliseconds: 500), ), ); } else { CustomSnackBar.show( context: context, title: "Login Status", message: loginProvider.message ?? "Login failed", icon: Icons.error, backgroundColor: AppColors.primaryColor, ); } } } @override Widget build(BuildContext context) { final loginProvider = Provider.of(context); return SafeArea( top: false, child: Scaffold( backgroundColor: const Color(0xFF0F172A), body: Stack( children: [ // Background gradient Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [ const Color(0xFF0F172A), const Color(0xFF354969), const Color(0xFFb7c4da), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, stops: const [0.0, 0.5, 1.0], ), ), ), // Background pattern Opacity( opacity: 0.03, child: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/images/light_logo.png"), repeat: ImageRepeat.repeat, scale: 15.0, ), ), ), ), SafeArea( child: ScrollConfiguration( behavior: ScrollBehavior() .copyWith( physics: BouncingScrollPhysics(), overscroll: true, ), // nice iOS-like bounce child: SingleChildScrollView( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.all(24), child: AnimatedOpacity( opacity: _opacity, duration: const Duration(milliseconds: 800), child: AnimatedScale( scale: _scale, duration: const Duration(milliseconds: 600), curve: Curves.easeOutBack, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 40), // Logo with animation Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.white.withOpacity(0.1), blurRadius: 20, spreadRadius: 5, ), ], ), child: Container( padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: const Color(0xFF1e293b), shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: Image.asset( "assets/images/light_logo.png", height: 80, filterQuality: FilterQuality.high, ), ), ), const SizedBox(height: 24), // App name ShaderMask( shaderCallback: (bounds) { return LinearGradient( colors: [ Colors.white, Colors.white.withOpacity(0.8), ], ).createShader(bounds); }, child: const Text( "PULSE ERP", style: TextStyle( fontSize: 32, fontWeight: FontWeight.w800, letterSpacing: 2.0, color: Colors.white, ), ), ), const SizedBox(height: 8), // Tagline Text( "Enterprise Resource Planning", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w300, color: Colors.white.withOpacity(0.7), letterSpacing: 1.2, ), ), const SizedBox(height: 30), // Login Card Container( padding: const EdgeInsets.all(32), decoration: BoxDecoration( color: Colors.white, // white background borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: Form( key: _formKey, child: Column( children: [ // Title const Text( "Welcome Back", style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, color: Colors.black, // black text ), ), const SizedBox(height: 8), const Text( "Login to continue", style: TextStyle( fontSize: 14, color: Colors.black54, // softer black ), ), const SizedBox(height: 32), // Email Field TextFormField( controller: _emailController, style: const TextStyle(color: Colors.black), // black input text decoration: InputDecoration( labelText: "Email Address", labelStyle: TextStyle(color: Colors.black54), prefixIcon: const Icon(Icons.email, color: Colors.black54), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.black26), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.black26), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF3B82F6)), // blue focus ), filled: true, fillColor: Colors.black.withOpacity(0.05), // light grey field bg ), validator: (value) { if (value == null || value.isEmpty) { return "The Email Address field is required."; } if (!value.contains('@')) { return "Please enter a valid email address."; } return null; }, ), const SizedBox(height: 20), // Password Field TextFormField( controller: _passwordController, obscureText: _obscurePassword, style: const TextStyle(color: Colors.black), decoration: InputDecoration( labelText: "Password", labelStyle: TextStyle(color: Colors.black54), prefixIcon: const Icon(Icons.lock, color: Colors.black54), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility : Icons.visibility_off, color: Colors.black54, ), onPressed: () { setState(() { _obscurePassword = !_obscurePassword; }); }, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.black26), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.black26), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Color(0xFF3B82F6)), ), filled: true, fillColor: Colors.black.withOpacity(0.05), ), validator: (value) { if (value == null || value.isEmpty) { return "The Password field is required."; } if (value.length < 6) { return "Password must be at least 6 characters."; } return null; }, ), const SizedBox(height: 2), // Remember Me // Row( // mainAxisAlignment: MainAxisAlignment.start, // children: [ // Transform.scale( // scale: 0.9, // child: Checkbox( // value: rememberMe, // onChanged: (val) { // setState(() { // rememberMe = val ?? false; // }); // }, // fillColor: MaterialStateProperty.resolveWith( // (Set states) { // if (states.contains(MaterialState.selected)) { // return const Color(0xFF3B82F6); // blue when checked // } // return Colors.black26; // }, // ), // ), // ), // const Text( // "Remember me", // style: TextStyle(color: Colors.black87), // ), // ], // ), const SizedBox(height: 22), // Login Button Material( color: Colors.transparent, child: InkWell( onTap: loginProvider.isLoading ? null : _login, borderRadius: BorderRadius.circular(12), child: Container( width: double.infinity, height: 56, decoration: BoxDecoration( gradient: LinearGradient( colors: loginProvider.isLoading ? [Colors.grey, Colors.grey.shade600] : [const Color(0xFF3B82F6), const Color(0xFF2563EB)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), boxShadow: loginProvider.isLoading ? null : [ BoxShadow( color: const Color(0xFF3B82F6).withOpacity(0.4), blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: Center( child: loginProvider.isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : const Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "Login", style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600, ), ), SizedBox(width: 8), Icon(Icons.arrow_forward, color: Colors.white, size: 18), ], ), ), ), ), ), const SizedBox(height: 24), // Divider Row( children: [ Expanded( child: Divider(color: Colors.black26), ), const Padding( padding: EdgeInsets.symmetric(horizontal: 12), child: Text( "Secure Login", style: TextStyle( color: Colors.black54, fontSize: 12, ), ), ), Expanded( child: Divider(color: Colors.black26), ), ], ), ], ), ), ), const SizedBox(height: 40), ], ), ), ), ), ), ), ], ), ), ); } @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } }