import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:forui/forui.dart'; import '../../core/contracts/server.dart'; import '../../providers/session_provider.dart'; import 'app_loading_ring.dart'; import 'app_inline_alert.dart'; class LoginForm extends ConsumerStatefulWidget { final EmbyServer server; final VoidCallback? onSuccess; final VoidCallback? onBack; const LoginForm({ super.key, required this.server, this.onSuccess, this.onBack, }); @override ConsumerState createState() => _LoginFormState(); } class _LoginFormState extends ConsumerState { final _formKey = GlobalKey(); final _username = TextEditingController(); final _password = TextEditingController(); bool _loading = false; bool _obscure = true; String? _error; @override void dispose() { _username.dispose(); _password.dispose(); super.dispose(); } Future _submit() async { if (!_formKey.currentState!.validate()) return; setState(() { _loading = true; _error = null; }); try { await ref .read(sessionProvider.notifier) .login( serverId: widget.server.id, username: _username.text.trim(), password: _password.text, ); if (!mounted) return; widget.onSuccess?.call(); } catch (e) { setState(() => _error = e.toString()); } finally { if (mounted) setState(() => _loading = false); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text('登录到 ${widget.server.name}', style: theme.textTheme.titleLarge), const SizedBox(height: 4), Text( widget.server.baseUrl, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 20), TextFormField( controller: _username, decoration: const InputDecoration(labelText: '用户名'), autofillHints: const [AutofillHints.username], validator: (v) => (v == null || v.trim().isEmpty) ? '请输入用户名' : null, ), const SizedBox(height: 12), TextFormField( controller: _password, decoration: InputDecoration( labelText: '密码', suffixIcon: IconButton( icon: Icon( _obscure ? Icons.visibility_outlined : Icons.visibility_off_outlined, ), onPressed: () => setState(() => _obscure = !_obscure), ), ), obscureText: _obscure, autofillHints: const [AutofillHints.password], onFieldSubmitted: (_) => _submit(), ), if (_error != null) ...[ const SizedBox(height: 12), AppInlineAlert(message: _error!), ], const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (widget.onBack != null) FButton( variant: FButtonVariant.ghost, onPress: _loading ? null : widget.onBack, child: const Text('返回'), ), const SizedBox(width: 8), FButton( variant: FButtonVariant.primary, onPress: _loading ? null : _submit, child: _loading ? const AppLoadingRing(size: 16, color: Colors.white) : const Text('登录'), ), ], ), ], ), ); } }