import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import '../../../../l10n/app_localizations.dart'; import '../../../settings/presentation/viewmodels/settings_viewmodel.dart'; import '../viewmodels/auth_viewmodel.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); bool _obscurePassword = true; @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } Future _login() async { if (!_formKey.currentState!.validate()) return; final viewModel = context.read(); final success = await viewModel.login( email: _emailController.text.trim(), password: _passwordController.text, ); if (mounted && success) { context.go('/'); } } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Scaffold( body: SafeArea( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Icon( Icons.task_alt, size: 80, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 16), Text( l10n.appTitle, style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( l10n.welcomeBack, style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), const SizedBox(height: 48), TextFormField( controller: _emailController, keyboardType: TextInputType.emailAddress, autocorrect: false, textInputAction: TextInputAction.next, decoration: InputDecoration( labelText: l10n.email, prefixIcon: const Icon(Icons.email_outlined), border: const OutlineInputBorder(), ), validator: (value) { if (value == null || value.isEmpty) { return l10n.emailRequired; } if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') .hasMatch(value)) { return l10n.emailInvalid; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, obscureText: _obscurePassword, textInputAction: TextInputAction.done, onFieldSubmitted: (_) => _login(), decoration: InputDecoration( labelText: l10n.password, prefixIcon: const Icon(Icons.lock_outlined), border: const OutlineInputBorder(), suffixIcon: SizedBox( width: 48, height: 48, child: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, ), onPressed: () { setState(() => _obscurePassword = !_obscurePassword); }, ), ), ), validator: (value) { if (value == null || value.isEmpty) { return l10n.passwordRequired; } return null; }, ), const SizedBox(height: 8), Consumer( builder: (context, viewModel, _) { if (viewModel.error != null) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Text( viewModel.error!, style: TextStyle( color: Theme.of(context).colorScheme.error, ), textAlign: TextAlign.center, ), ); } return const SizedBox.shrink(); }, ), const SizedBox(height: 16), Consumer( builder: (context, viewModel, child) { return SizedBox( height: 56, child: FilledButton( onPressed: viewModel.isLoading ? null : _login, child: viewModel.isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2, ), ) : Text(l10n.login), ), ); }, ), const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(l10n.noAccountYet), TextButton( onPressed: () => context.go('/register'), child: Text(l10n.register), ), ], ), const SizedBox(height: 16), TextButton.icon( onPressed: () async { final settingsVm = context.read(); await settingsVm.setSetupCompleted(false); await settingsVm.setOnboardingShown(false); if (context.mounted) { context.go('/setup'); } }, icon: const Icon(Icons.arrow_back), label: Text(l10n.backToSetup), ), ], ), ), ), ), ), ), ); } }