diff --git a/lib/view/login/login.dart b/lib/view/login/login.dart index 87aed0d..92753c6 100644 --- a/lib/view/login/login.dart +++ b/lib/view/login/login.dart @@ -46,7 +46,8 @@ class _LoginState extends State { super.dispose(); } - String? _required(String? value) => (value ?? '').trim().isEmpty ? 'Eingabe erforderlich' : null; + String? _required(String? value) => + (value ?? '').trim().isEmpty ? 'Eingabe erforderlich' : null; Future _submit() async { if (_loading) return; @@ -97,10 +98,7 @@ class _LoginState extends State { icon: Icon(Icons.error_outline, color: theme.colorScheme.error), title: const Text('Fehlerdetails'), content: SingleChildScrollView( - child: SelectableText( - details, - style: theme.textTheme.bodySmall, - ), + child: SelectableText(details, style: theme.textTheme.bodySmall), ), actions: [ TextButton.icon( @@ -133,11 +131,15 @@ class _LoginState extends State { return Scaffold( backgroundColor: _marianumRed, body: SafeArea( - child: LayoutBuilder( - builder: (context, constraints) => SingleChildScrollView( - padding: const EdgeInsets.symmetric(horizontal: 24), + child: LayoutBuilder( + builder: (context, constraints) => SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Center( child: ConstrainedBox( - constraints: BoxConstraints(minHeight: constraints.maxHeight), + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + maxWidth: 420, + ), child: IntrinsicHeight( child: Column( children: [ @@ -170,168 +172,209 @@ class _LoginState extends State { ), ), const SizedBox(height: 28), - ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 420), - child: Card( - elevation: 8, - shadowColor: Colors.black.withValues(alpha: 0.35), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20), - ), - color: theme.colorScheme.surface, - child: Padding( - padding: const EdgeInsets.fromLTRB(24, 24, 24, 20), - child: Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - 'Anmelden', - style: theme.textTheme.titleLarge?.copyWith( - fontWeight: FontWeight.w600, - ), + Card( + elevation: 8, + shadowColor: Colors.black.withValues(alpha: 0.35), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + color: theme.colorScheme.surface, + child: Padding( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 20), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + 'Anmelden', + style: theme.textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.w600, ), - const SizedBox(height: 6), - Text( - 'Melde dich mit deinen Marianum-Zugangsdaten an.', - style: theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), + ), + const SizedBox(height: 6), + Text( + 'Melde dich mit deinen Marianum-Zugangsdaten an.', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, ), - const SizedBox(height: 20), - TextFormField( - controller: _usernameController, - enabled: !_loading, - validator: _required, - autocorrect: false, - textInputAction: TextInputAction.next, - onFieldSubmitted: (_) => _passwordFocus.requestFocus(), - decoration: InputDecoration( - labelText: 'Nutzername', - prefixIcon: const Icon(Icons.person_outline), - filled: true, - fillColor: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.4), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: theme.colorScheme.primary, width: 1.5), + ), + const SizedBox(height: 20), + TextFormField( + controller: _usernameController, + enabled: !_loading, + validator: _required, + autocorrect: false, + textInputAction: TextInputAction.next, + onFieldSubmitted: (_) => + _passwordFocus.requestFocus(), + decoration: InputDecoration( + labelText: 'Nutzername', + prefixIcon: const Icon( + Icons.person_outline, + ), + filled: true, + fillColor: theme + .colorScheme + .surfaceContainerHighest + .withValues(alpha: 0.4), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: theme.colorScheme.primary, + width: 1.5, ), ), ), - const SizedBox(height: 12), - TextFormField( - controller: _passwordController, - focusNode: _passwordFocus, - enabled: !_loading, - validator: _required, - obscureText: true, - obscuringCharacter: '•', - autocorrect: false, - enableSuggestions: false, - keyboardType: TextInputType.visiblePassword, - textInputAction: TextInputAction.done, - onFieldSubmitted: (_) => _submit(), - decoration: InputDecoration( - labelText: 'Passwort', - prefixIcon: const Icon(Icons.lock_outline), - filled: true, - fillColor: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.4), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: theme.colorScheme.primary, width: 1.5), + ), + const SizedBox(height: 12), + TextFormField( + controller: _passwordController, + focusNode: _passwordFocus, + enabled: !_loading, + validator: _required, + obscureText: true, + obscuringCharacter: '•', + autocorrect: false, + enableSuggestions: false, + keyboardType: TextInputType.visiblePassword, + textInputAction: TextInputAction.done, + onFieldSubmitted: (_) => _submit(), + decoration: InputDecoration( + labelText: 'Passwort', + prefixIcon: const Icon(Icons.lock_outline), + filled: true, + fillColor: theme + .colorScheme + .surfaceContainerHighest + .withValues(alpha: 0.4), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide( + color: theme.colorScheme.primary, + width: 1.5, ), ), ), - AnimatedSize( - duration: const Duration(milliseconds: 180), - curve: Curves.easeOut, - child: _errorMessage == null - ? const SizedBox(height: 0, width: double.infinity) - : Padding( - padding: const EdgeInsets.only(top: 14), - child: Material( - color: theme.colorScheme.errorContainer.withValues(alpha: 0.6), - borderRadius: BorderRadius.circular(12), - child: InkWell( - onTap: _errorDetails != null ? _showErrorDetails : null, - borderRadius: BorderRadius.circular(12), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 10), - child: Row( - children: [ - Icon(Icons.error_outline, - size: 20, - color: theme.colorScheme.onErrorContainer), - const SizedBox(width: 10), - Expanded( - child: Text( - _errorMessage!, - style: TextStyle( - color: theme.colorScheme.onErrorContainer, - fontSize: 13, - height: 1.3, - ), + ), + AnimatedSize( + duration: const Duration(milliseconds: 180), + curve: Curves.easeOut, + child: _errorMessage == null + ? const SizedBox( + height: 0, + width: double.infinity, + ) + : Padding( + padding: const EdgeInsets.only( + top: 14, + ), + child: Material( + color: theme + .colorScheme + .errorContainer + .withValues(alpha: 0.6), + borderRadius: BorderRadius.circular( + 12, + ), + child: InkWell( + onTap: _errorDetails != null + ? _showErrorDetails + : null, + borderRadius: + BorderRadius.circular(12), + child: Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + child: Row( + children: [ + Icon( + Icons.error_outline, + size: 20, + color: theme + .colorScheme + .onErrorContainer, + ), + const SizedBox(width: 10), + Expanded( + child: Text( + _errorMessage!, + style: TextStyle( + color: theme + .colorScheme + .onErrorContainer, + fontSize: 13, + height: 1.3, ), ), - if (_errorDetails != null) ...[ - const SizedBox(width: 8), - Icon(Icons.chevron_right, - size: 20, - color: theme.colorScheme.onErrorContainer - .withValues(alpha: 0.7)), - ], + ), + if (_errorDetails != + null) ...[ + const SizedBox(width: 8), + Icon( + Icons.chevron_right, + size: 20, + color: theme + .colorScheme + .onErrorContainer + .withValues( + alpha: 0.7, + ), + ), ], - ), + ], ), ), ), ), - ), - const SizedBox(height: 20), - SizedBox( - height: 50, - child: FilledButton( - onPressed: _loading ? null : _submit, - style: FilledButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - textStyle: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w600, ), + ), + const SizedBox(height: 20), + SizedBox( + height: 50, + child: FilledButton( + onPressed: _loading ? null : _submit, + style: FilledButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + textStyle: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, ), - child: _loading - ? const SizedBox( - height: 22, - width: 22, - child: CircularProgressIndicator( - strokeWidth: 2.5, - color: Colors.white, - ), - ) - : const Text('Anmelden'), ), + child: _loading + ? const SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator( + strokeWidth: 2.5, + color: Colors.white, + ), + ) + : const Text('Anmelden'), ), - ], - ), + ), + ], ), ), ), @@ -368,6 +411,7 @@ class _LoginState extends State { ), ), ), + ), ), ); }