import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import '../config/theme.dart'; import '../services/api_service.dart'; class LoginScreen extends HookWidget { final ApiService api; final VoidCallback onLogin; const LoginScreen({super.key, required this.api, required this.onLogin}); @override Widget build(BuildContext context) { final emailController = useTextEditingController(); final passwordController = useTextEditingController(); final serverController = useTextEditingController(text: api.baseUrl); final formKey = useMemoized(() => GlobalKey()); final loading = useState(false); final error = useState(null); Future login() async { if (!formKey.currentState!.validate()) return; loading.value = true; error.value = null; try { api.baseUrl = serverController.text.trim(); await api.login( emailController.text.trim(), passwordController.text, ); onLogin(); } on ApiException catch (e) { error.value = e.message; } catch (_) { error.value = 'Connection failed'; } finally { loading.value = false; } } return Scaffold( body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(32), child: Form( key: formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 72, height: 72, decoration: BoxDecoration( color: AppColors.accent.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(20), ), child: Image.asset( 'assets/images/app_icon.png', width: 36, height: 36, fit: BoxFit.contain, ), ), const SizedBox(height: 24), const Text( 'UpdateForge', style: TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: AppColors.text, ), ), const SizedBox(height: 6), Text( 'Sign in to access your apps', style: TextStyle(color: AppColors.muted, fontSize: 14), ), const SizedBox(height: 40), TextFormField( controller: serverController, decoration: const InputDecoration( labelText: 'Server URL', hintText: 'https://example.com', prefixIcon: Icon(Icons.dns_outlined, size: 20), ), keyboardType: TextInputType.url, validator: (v) { if (v == null || v.trim().isEmpty) return 'Required'; final url = Uri.tryParse(v.trim()); if (url == null || !url.hasScheme) return 'Invalid URL'; return null; }, ), const SizedBox(height: 14), TextFormField( controller: emailController, decoration: const InputDecoration( labelText: 'Email', prefixIcon: Icon(Icons.mail_outline, size: 20), ), keyboardType: TextInputType.emailAddress, validator: (v) => v == null || v.trim().isEmpty ? 'Required' : null, ), const SizedBox(height: 14), TextFormField( controller: passwordController, decoration: const InputDecoration( labelText: 'Password', prefixIcon: Icon(Icons.lock_outline, size: 20), ), obscureText: true, onFieldSubmitted: (_) => login(), validator: (v) => v == null || v.isEmpty ? 'Required' : null, ), if (error.value != null) ...[ const SizedBox(height: 14), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColors.danger.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all( color: AppColors.danger.withValues(alpha: 0.3), ), ), child: Row( children: [ const Icon(Icons.error_outline, color: AppColors.danger, size: 18), const SizedBox(width: 10), Expanded( child: Text( error.value!, style: const TextStyle( color: AppColors.danger, fontSize: 13, ), ), ), ], ), ), ], const SizedBox(height: 28), ElevatedButton( onPressed: loading.value ? null : login, child: loading.value ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text('Sign In'), ), ], ), ), ), ), ), ); } }