import 'package:flutter/material.dart'; import '../services/auth_service.dart'; import '../theme/app_theme.dart'; import '../utils/snackbar_utils.dart'; class LoginScreen extends StatefulWidget { final void Function(AuthUser user) onLoggedIn; const LoginScreen({super.key, required this.onLoggedIn}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _auth = AuthService(); final _emailCtrl = TextEditingController(); final _passwordCtrl = TextEditingController(); bool _busy = false; @override void dispose() { _emailCtrl.dispose(); _passwordCtrl.dispose(); super.dispose(); } Future _login() async { final email = _emailCtrl.text.trim(); final password = _passwordCtrl.text; if (email.isEmpty || password.isEmpty) { SnackbarUtils.showError(context, 'Enter your email and password'); return; } setState(() { _busy = true; }); try { final user = await _auth.login(email, password); if (!mounted) return; if (user == null) { setState(() { _busy = false; }); SnackbarUtils.showError(context, 'Invalid email or password'); return; } widget.onLoggedIn(user); } catch (_) { if (!mounted) return; setState(() { _busy = false; }); SnackbarUtils.showError(context, 'Could not reach server. Check your connection.'); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.primary, body: SafeArea( child: Align( alignment: const Alignment(0, -0.4), child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), child: Column( mainAxisSize: MainAxisSize.min, children: [ _logo(), const SizedBox(height: 20), const Text( 'Doormile CRM', style: TextStyle( fontSize: 24, fontWeight: FontWeight.w800, color: Colors.white, letterSpacing: 0.2, ), ), const SizedBox(height: 6), Text( 'FIELD SALES PORTAL', style: TextStyle( fontSize: 10, fontWeight: FontWeight.w700, letterSpacing: 2, color: Colors.white.withValues(alpha: 0.7), ), ), const SizedBox(height: 28), _card(), ], ), ), ), ), ), ); } Widget _logo() { return Container( width: 92, height: 92, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.18), blurRadius: 24, offset: const Offset(0, 10), ), ], ), child: Padding( padding: const EdgeInsets.all(14), child: Image.asset('assets/doormilelogoround.png', fit: BoxFit.contain), ), ); } Widget _card() { return Container( width: double.infinity, padding: const EdgeInsets.all(22), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.12), blurRadius: 30, offset: const Offset(0, 12), ), ], ), child: _loginForm(), ); } Widget _loginForm() { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( 'Sign In', style: TextStyle( fontSize: 22, fontWeight: FontWeight.w800, color: AppColors.darkBlue, ), ), const SizedBox(height: 6), const Text( 'Welcome back! Please enter your details.', textAlign: TextAlign.center, style: TextStyle(fontSize: 13, color: AppColors.textSecondary), ), const SizedBox(height: 24), TextField( controller: _emailCtrl, keyboardType: TextInputType.emailAddress, autocorrect: false, textInputAction: TextInputAction.next, decoration: InputDecoration( hintText: 'Email', prefixIcon: const Icon(Icons.email_outlined, color: AppColors.muted), filled: true, fillColor: Colors.grey.shade50, contentPadding: const EdgeInsets.symmetric(vertical: 16), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.primary, width: 2), ), ), ), const SizedBox(height: 16), TextField( controller: _passwordCtrl, obscureText: true, textInputAction: TextInputAction.go, onSubmitted: (_) => _login(), decoration: InputDecoration( hintText: 'Password', prefixIcon: const Icon(Icons.lock_outline, color: AppColors.muted), filled: true, fillColor: Colors.grey.shade50, contentPadding: const EdgeInsets.symmetric(vertical: 16), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade200), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.primary, width: 2), ), ), ), const SizedBox(height: 24), _primaryButton( label: 'Continue', onPressed: _busy ? null : _login, ), ], ); } Widget _primaryButton({ required String label, required VoidCallback? onPressed, }) { return SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: onPressed, style: ElevatedButton.styleFrom( backgroundColor: AppColors.primary, foregroundColor: Colors.white, disabledBackgroundColor: AppColors.primary.withValues(alpha: 0.5), elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: _busy ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2.2, color: Colors.white, ), ) : Text( label, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w800, ), ), ), ); } }