import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/constants/app_theme.dart'; import '../../core/providers.dart'; class LoginScreen extends ConsumerStatefulWidget { const LoginScreen({super.key}); @override ConsumerState createState() => _LoginScreenState(); } class _LoginScreenState extends ConsumerState { final _formKey = GlobalKey(); final _serverUrlController = TextEditingController(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); final _deviceNameController = TextEditingController(); bool _obscurePassword = true; @override void initState() { super.initState(); _initDeviceNameAndCachedUrl(); } @override void dispose() { _serverUrlController.dispose(); _usernameController.dispose(); _passwordController.dispose(); _deviceNameController.dispose(); super.dispose(); } Future _initDeviceNameAndCachedUrl() async { // 1. Pre-fill cached server URL if previously entered final cachedUrl = await ref.read(secureStorageProvider).getServerUrl(); if (cachedUrl != null && cachedUrl.isNotEmpty) { _serverUrlController.text = cachedUrl; } // 2. Auto-detect device model for friendly identification in ForkFolio settings String defaultName = 'Mobile App'; try { final deviceInfo = DeviceInfoPlugin(); if (Platform.isAndroid) { final androidInfo = await deviceInfo.androidInfo; defaultName = '${androidInfo.manufacturer} ${androidInfo.model}'.trim(); } else if (Platform.isIOS) { final iosInfo = await deviceInfo.iosInfo; defaultName = iosInfo.utsname.machine; } } catch (_) {} _deviceNameController.text = defaultName; } Future _submit() async { if (!_formKey.currentState!.validate()) return; final success = await ref.read(authProvider.notifier).login( serverUrl: _serverUrlController.text.trim(), username: _usernameController.text.trim(), password: _passwordController.text, deviceName: _deviceNameController.text.trim(), ); if (!success && mounted) { final error = ref.read(authProvider).errorMessage ?? 'Login failed'; ScaffoldMessenger.of(context).showSnackBar( SnackBar(backgroundColor: AppTheme.danger, content: Text(error)), ); } } @override Widget build(BuildContext context) { final authState = ref.watch(authProvider); return Scaffold( body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 24), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // App Logo & Heading Center( child: Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: AppTheme.primary.withOpacity(0.12), shape: BoxShape.circle, border: Border.all(color: AppTheme.primary.withOpacity(0.3), width: 1.5), ), child: const Icon(Icons.restaurant_menu_rounded, size: 48, color: AppTheme.primary), ), ), const SizedBox(height: 20), const Text( 'ForkFolio Mobile', textAlign: TextAlign.center, style: TextStyle(fontSize: 26, fontWeight: FontWeight.w800, letterSpacing: -0.5), ), const SizedBox(height: 6), const Text( 'Connect your mobile chef companion', textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: AppTheme.textMuted), ), const SizedBox(height: 36), // Server URL Field TextFormField( controller: _serverUrlController, keyboardType: TextInputType.url, decoration: const InputDecoration( labelText: 'Server Domain (HTTPS)', hintText: 'https://recipes.yourdomain.com', prefixIcon: Icon(Icons.dns_rounded, size: 20), ), validator: (v) { if (v == null || v.trim().isEmpty) return 'Server URL is required'; if (!v.trim().startsWith('http://') && !v.trim().startsWith('https://')) { return 'Include https:// at the beginning'; } return null; }, ), const SizedBox(height: 16), // Username Field TextFormField( controller: _usernameController, decoration: const InputDecoration( labelText: 'Username', prefixIcon: Icon(Icons.person_outline_rounded, size: 20), ), validator: (v) => (v == null || v.trim().isEmpty) ? 'Username is required' : null, ), const SizedBox(height: 16), // Password Field TextFormField( controller: _passwordController, obscureText: _obscurePassword, decoration: InputDecoration( labelText: 'Password', prefixIcon: const Icon(Icons.lock_outline_rounded, size: 20), suffixIcon: IconButton( icon: Icon(_obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, size: 20), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), ), ), validator: (v) => (v == null || v.isEmpty) ? 'Password is required' : null, ), const SizedBox(height: 16), // Device Name Field TextFormField( controller: _deviceNameController, decoration: const InputDecoration( labelText: 'Device Friendly Name', hintText: 'e.g. Pixel 8 Pro', prefixIcon: Icon(Icons.smartphone_rounded, size: 20), ), validator: (v) => (v == null || v.trim().isEmpty) ? 'Device name is required' : null, ), const SizedBox(height: 28), // Submit Button SizedBox( height: 52, child: ElevatedButton( onPressed: authState.isLoading ? null : _submit, child: authState.isLoading ? const SizedBox( width: 22, height: 22, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2.5), ) : const Text('Authorize & Link Device'), ), ), ], ), ), ), ), ), ); } }