// lib/screens/desktop_login_screen.dart import 'package:flutter/material.dart'; import '../features/auth/services/auth_service.dart'; import 'home_desktop_screen.dart'; import '../core/app_decoration.dart'; class DesktopLoginScreen extends StatefulWidget { const DesktopLoginScreen({Key? key}) : super(key: key); @override State createState() => _DesktopLoginScreenState(); } class _DesktopLoginScreenState extends State with SingleTickerProviderStateMixin { final TextEditingController _emailCtrl = TextEditingController(); final TextEditingController _passCtrl = TextEditingController(); final AuthService _auth = AuthService(); AnimationController? _controller; Animation? _leftWidthAnim; Animation? _leftTextOpacityAnim; Animation? _formOpacityAnim; Animation? _formOffsetAnim; final double _collapsedWidth = 220; final Duration _duration = const Duration(milliseconds: 700); final Curve _curve = Curves.easeInOutCubic; bool _isAnimating = false; bool _loading = false; // network loading flag @override void dispose() { _controller?.dispose(); _emailCtrl.dispose(); _passCtrl.dispose(); super.dispose(); } Future _startCollapseAnimation(double initialLeftWidth) async { _controller?.dispose(); _controller = AnimationController(vsync: this, duration: _duration); _leftWidthAnim = Tween(begin: initialLeftWidth, end: _collapsedWidth).animate( CurvedAnimation(parent: _controller!, curve: _curve), ); _leftTextOpacityAnim = Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation(parent: _controller!, curve: const Interval(0.0, 0.35, curve: Curves.easeOut)), ); _formOpacityAnim = Tween(begin: 1.0, end: 0.0).animate( CurvedAnimation(parent: _controller!, curve: const Interval(0.0, 0.55, curve: Curves.easeOut)), ); _formOffsetAnim = Tween(begin: 0.0, end: -60.0).animate( CurvedAnimation(parent: _controller!, curve: const Interval(0.0, 0.55, curve: Curves.easeOut)), ); setState(() => _isAnimating = true); await _controller!.forward(); await Future.delayed(const Duration(milliseconds: 120)); } Future _performLoginFlow(double initialLeftWidth) async { if (_isAnimating || _loading) return; setState(() { _loading = true; }); final email = _emailCtrl.text.trim(); final password = _passCtrl.text; if (email.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Enter email'))); setState(() => _loading = false); return; } if (password.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Enter password'))); setState(() => _loading = false); return; } try { // Capture user model returned by AuthService (AuthService already saves prefs) await _auth.login(email, password); // on success run opening animation await _startCollapseAnimation(initialLeftWidth); if (!mounted) return; Navigator.of(context).pushReplacement(PageRouteBuilder( pageBuilder: (context, a1, a2) => const HomeDesktopScreen(skipSidebarEntranceAnimation: true), transitionDuration: Duration.zero, reverseTransitionDuration: Duration.zero, )); } catch (e) { if (!mounted) return; final message = e.toString().replaceAll('Exception: ', ''); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); setState(() => _isAnimating = false); } finally { if (mounted) setState(() { _loading = false; }); } } void _openRegister() { Navigator.of(context).push(MaterialPageRoute(builder: (_) => const DesktopRegisterScreen())); } @override Widget build(BuildContext context) { final screenW = MediaQuery.of(context).size.width; final double safeInitialWidth = (screenW * 0.45).clamp(360.0, screenW * 0.65); final bool animAvailable = _controller != null && _leftWidthAnim != null; final Listenable animation = _controller ?? AlwaysStoppedAnimation(0.0); return Scaffold( body: SafeArea( child: AnimatedBuilder( animation: animation, builder: (context, child) { final leftWidth = animAvailable ? _leftWidthAnim!.value : safeInitialWidth; final leftTextOpacity = animAvailable && _leftTextOpacityAnim != null ? _leftTextOpacityAnim!.value : 1.0; final formOpacity = animAvailable && _formOpacityAnim != null ? _formOpacityAnim!.value : 1.0; final formOffset = animAvailable && _formOffsetAnim != null ? _formOffsetAnim!.value : 0.0; return Row( children: [ Container( width: leftWidth, height: double.infinity, // color: const Color(0xFF0B63D6), decoration: AppDecoration.blueGradient, padding: const EdgeInsets.symmetric(horizontal: 36, vertical: 28), child: Opacity( opacity: leftTextOpacity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), const Text('EVENTIFY', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 22)), const Spacer(), const Text('Welcome Back!', style: TextStyle(color: Colors.white, fontSize: 34, fontWeight: FontWeight.bold)), const SizedBox(height: 12), const Text( 'Sign in to access your dashboard, manage events, and stay connected.', style: TextStyle(color: Colors.white70, fontSize: 14), ), const Spacer(flex: 2), Opacity( opacity: leftWidth > (_collapsedWidth + 8) ? 1.0 : 0.0, child: const Padding( padding: EdgeInsets.only(bottom: 12.0), child: Text('© Eventify', style: TextStyle(color: Colors.white54)), ), ), ], ), ), ), Expanded( child: Transform.translate( offset: Offset(formOffset, 0), child: Opacity( opacity: formOpacity, child: Container( color: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 36), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 520), child: Card( elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 28.0, vertical: 28.0), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text('Sign In', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), textAlign: TextAlign.center), const SizedBox(height: 6), const Text('Please enter your details to continue', textAlign: TextAlign.center, style: TextStyle(color: Colors.black54)), const SizedBox(height: 22), TextField( controller: _emailCtrl, decoration: InputDecoration( prefixIcon: const Icon(Icons.email), labelText: 'Email Address', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), ), ), const SizedBox(height: 12), TextField( controller: _passCtrl, obscureText: true, decoration: InputDecoration( prefixIcon: const Icon(Icons.lock), labelText: 'Password', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [ Checkbox(value: true, onChanged: (_) {}), const Text('Remember me') ]), TextButton(onPressed: () {}, child: const Text('Forgot Password?')) ], ), const SizedBox(height: 8), SizedBox( height: 50, child: ElevatedButton( onPressed: (_isAnimating || _loading) ? null : () { final double initial = safeInitialWidth; _performLoginFlow(initial); }, style: ElevatedButton.styleFrom(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))), child: (_isAnimating || _loading) ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)) : const Text('Sign In', style: TextStyle(fontSize: 16)), ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextButton(onPressed: _openRegister, child: const Text("Don't have an account? Register")), TextButton(onPressed: () {}, child: const Text('Contact support')) ], ) ], ), ), ), ), ), ), ), ), ), ], ); }, ), ), ); } } class DesktopRegisterScreen extends StatefulWidget { const DesktopRegisterScreen({Key? key}) : super(key: key); @override State createState() => _DesktopRegisterScreenState(); } class _DesktopRegisterScreenState extends State { final TextEditingController _emailCtrl = TextEditingController(); final TextEditingController _phoneCtrl = TextEditingController(); final TextEditingController _passCtrl = TextEditingController(); final TextEditingController _confirmCtrl = TextEditingController(); final AuthService _auth = AuthService(); bool _loading = false; @override void dispose() { _emailCtrl.dispose(); _phoneCtrl.dispose(); _passCtrl.dispose(); _confirmCtrl.dispose(); super.dispose(); } Future _performRegister() async { final email = _emailCtrl.text.trim(); final phone = _phoneCtrl.text.trim(); final pass = _passCtrl.text; final confirm = _confirmCtrl.text; if (email.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Enter email'))); return; } if (phone.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Enter phone number'))); return; } if (pass.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Enter password'))); return; } if (pass != confirm) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Passwords do not match'))); return; } setState(() => _loading = true); try { await _auth.register( email: email, phoneNumber: phone, password: pass, ); if (!mounted) return; Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const HomeDesktopScreen(skipSidebarEntranceAnimation: true))); } catch (e) { if (!mounted) return; final message = e.toString().replaceAll('Exception: ', ''); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); } finally { if (mounted) setState(() => _loading = false); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Register')), body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(20), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 520), child: Card( child: Padding( padding: const EdgeInsets.all(18.0), child: Column( children: [ TextField(controller: _emailCtrl, decoration: const InputDecoration(labelText: 'Email')), const SizedBox(height: 8), TextField(controller: _phoneCtrl, decoration: const InputDecoration(labelText: 'Phone')), const SizedBox(height: 8), TextField(controller: _passCtrl, obscureText: true, decoration: const InputDecoration(labelText: 'Password')), const SizedBox(height: 8), TextField(controller: _confirmCtrl, obscureText: true, decoration: const InputDecoration(labelText: 'Confirm password')), const SizedBox(height: 16), Row( children: [ ElevatedButton(onPressed: _loading ? null : _performRegister, child: _loading ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)) : const Text('Register')), const SizedBox(width: 12), OutlinedButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel')), ], ) ], ), ), ), ), ), ), ), ); } }