import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:pyramid/features/auth/login_notifier.dart'; class LoginPage extends ConsumerStatefulWidget { const LoginPage({super.key}); @override ConsumerState createState() => _LoginPageState(); } class _LoginPageState extends ConsumerState { final _formKey = GlobalKey(); final _userIdCtrl = TextEditingController(); final _passwordCtrl = TextEditingController(); bool _obscurePassword = true; @override void dispose() { _userIdCtrl.dispose(); _passwordCtrl.dispose(); super.dispose(); } Future _submit() async { if (!_formKey.currentState!.validate()) return; await ref.read(loginNotifierProvider.notifier).login( userId: _userIdCtrl.text.trim(), password: _passwordCtrl.text, ); final state = ref.read(loginNotifierProvider); if (state.status == LoginStatus.idle && mounted) { context.go('/rooms'); } } @override Widget build(BuildContext context) { final state = ref.watch(loginNotifierProvider); final loading = state.status == LoginStatus.loading; return Scaffold( body: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: Padding( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const _PyramidLogo(), const SizedBox(height: 32), TextFormField( controller: _userIdCtrl, decoration: const InputDecoration( labelText: 'Matrix ID', hintText: '@name:server.de', prefixIcon: Icon(Icons.person_outline), border: OutlineInputBorder(), ), keyboardType: TextInputType.emailAddress, autofillHints: const [AutofillHints.username], validator: (v) { if (v == null || v.trim().isEmpty) { return 'Matrix ID eingeben'; } if (!v.trim().startsWith('@') || !v.trim().contains(':')) { return 'Format: @name:server.de'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _passwordCtrl, obscureText: _obscurePassword, decoration: InputDecoration( labelText: 'Passwort', prefixIcon: const Icon(Icons.lock_outline), border: const OutlineInputBorder(), suffixIcon: IconButton( icon: Icon(_obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined), onPressed: () => setState( () => _obscurePassword = !_obscurePassword), ), ), autofillHints: const [AutofillHints.password], validator: (v) => (v == null || v.isEmpty) ? 'Passwort eingeben' : null, onFieldSubmitted: (_) => _submit(), ), if (state.errorMessage != null) ...[ const SizedBox(height: 12), Text( state.errorMessage!, style: TextStyle( color: Theme.of(context).colorScheme.error), textAlign: TextAlign.center, ), ], const SizedBox(height: 24), FilledButton( onPressed: loading ? null : _submit, child: loading ? const SizedBox( height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Anmelden'), ), ], ), ), ), ), ), ); } } class _PyramidLogo extends StatelessWidget { const _PyramidLogo(); @override Widget build(BuildContext context) { return Column( children: [ Icon( Icons.change_history_rounded, size: 72, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 8), Text( 'Pyramid', style: Theme.of(context) .textTheme .headlineMedium ?.copyWith(fontWeight: FontWeight.bold), ), ], ); } }