import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../providers/auth_provider.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { final _formKey = GlobalKey(); final _serverController = TextEditingController(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); bool _obscurePassword = true; @override void initState() { super.initState(); _loadSavedServer(); } Future _loadSavedServer() async { const storage = FlutterSecureStorage(); final saved = await storage.read(key: 'server_url'); if (saved != null && mounted) { _serverController.text = saved; } } void _submit() { if (!_formKey.currentState!.validate()) return; var serverUrl = _serverController.text.trim(); if (!serverUrl.startsWith('http')) { serverUrl = 'https://$serverUrl'; } context.read().login( serverUrl, _usernameController.text.trim(), _passwordController.text, ); } @override Widget build(BuildContext context) { final auth = context.watch(); return Scaffold( body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.phone_in_talk, size: 64, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 16), Text( 'TWP Softphone', style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 32), TextFormField( controller: _serverController, decoration: const InputDecoration( labelText: 'Server URL', hintText: 'https://your-site.com', prefixIcon: Icon(Icons.dns), border: OutlineInputBorder(), ), keyboardType: TextInputType.url, validator: (v) => v == null || v.trim().isEmpty ? 'Required' : null, ), const SizedBox(height: 16), TextFormField( controller: _usernameController, decoration: const InputDecoration( labelText: 'Username', prefixIcon: Icon(Icons.person), border: OutlineInputBorder(), ), validator: (v) => v == null || v.trim().isEmpty ? 'Required' : null, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, decoration: InputDecoration( labelText: 'Password', prefixIcon: const Icon(Icons.lock), border: const OutlineInputBorder(), suffixIcon: IconButton( icon: Icon(_obscurePassword ? Icons.visibility_off : Icons.visibility), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), ), ), obscureText: _obscurePassword, validator: (v) => v == null || v.isEmpty ? 'Required' : null, ), if (auth.error != null) ...[ const SizedBox(height: 16), Text( auth.error!, style: TextStyle( color: Theme.of(context).colorScheme.error), ), ], const SizedBox(height: 24), SizedBox( width: double.infinity, height: 48, child: FilledButton( onPressed: auth.state == AuthState.authenticating ? null : _submit, child: auth.state == AuthState.authenticating ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white), ) : const Text('Connect'), ), ), ], ), ), ), ), ), ); } @override void dispose() { _serverController.dispose(); _usernameController.dispose(); _passwordController.dispose(); super.dispose(); } }