import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../data/sources/localiser/realtime_data_client.dart'; import '../../providers.dart'; class LoginScreen extends ConsumerStatefulWidget { const LoginScreen({super.key}); @override ConsumerState createState() => _LoginScreenState(); } class _LoginScreenState extends ConsumerState { final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); bool _loading = false; String? _error; @override void initState() { super.initState(); _tryAutoLogin(); } @override void dispose() { _usernameController.dispose(); _passwordController.dispose(); super.dispose(); } Future _tryAutoLogin() async { final store = ref.read(credentialStoreProvider); final saved = await store.load(); if (saved == null || !mounted) return; _usernameController.text = saved.username; _passwordController.text = saved.password; await _login(saved.username, saved.password, saveCredentials: false); } Future _login(String username, String password, {bool saveCredentials = true}) async { setState(() { _loading = true; _error = null; }); try { final client = ref.read(sessionClientProvider); final tokenResponse = await client.login(username, password); final token = tokenResponse.token; ref.read(authTokenProvider.notifier).state = token; final config = ref.read(serverConfigProvider)!; final realtime = RealtimeDataClient(config: config, token: token); await realtime.connect(); ref.read(realtimeDataClientProvider.notifier).state = realtime; if (saveCredentials) { await ref .read(credentialStoreProvider) .save((username: username, password: password)); } if (mounted) context.go('/floorplan'); } on Exception catch (e) { setState(() => _error = e.toString()); } finally { if (mounted) setState(() => _loading = false); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Sign In')), body: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextField( controller: _usernameController, decoration: const InputDecoration( labelText: 'Username', border: OutlineInputBorder(), ), textInputAction: TextInputAction.next, autofillHints: const [AutofillHints.username], ), const SizedBox(height: 12), TextField( controller: _passwordController, decoration: const InputDecoration( labelText: 'Password', border: OutlineInputBorder(), ), obscureText: true, textInputAction: TextInputAction.done, autofillHints: const [AutofillHints.password], onSubmitted: _loading ? null : (_) => _login( _usernameController.text.trim(), _passwordController.text, ), ), const SizedBox(height: 16), if (_error != null) Padding( padding: const EdgeInsets.only(bottom: 12), child: Text( _error!, style: TextStyle(color: Theme.of(context).colorScheme.error), ), ), FilledButton( onPressed: _loading ? null : () => _login( _usernameController.text.trim(), _passwordController.text, ), child: _loading ? const SizedBox.square( dimension: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Sign in'), ), ], ), ), ); } }