219 lines
6.7 KiB
Dart
219 lines
6.7 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:moonwell_launcher/app/design_system/mw_shells.dart';
|
||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||
|
||
class MwAuthenticationForm extends StatefulWidget {
|
||
const MwAuthenticationForm({
|
||
super.key,
|
||
required this.usernameController,
|
||
required this.passwordController,
|
||
required this.onSubmit,
|
||
this.loading = false,
|
||
this.error,
|
||
});
|
||
|
||
final TextEditingController usernameController;
|
||
final TextEditingController passwordController;
|
||
final VoidCallback onSubmit;
|
||
final bool loading;
|
||
final String? error;
|
||
|
||
@override
|
||
State<MwAuthenticationForm> createState() => _MwAuthenticationFormState();
|
||
}
|
||
|
||
class _MwAuthenticationFormState extends State<MwAuthenticationForm> {
|
||
bool _remember = true;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final accent = MoonWellDesignTokens.of(context).accent;
|
||
return AutofillGroup(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
const MwBrandMark(width: 210, compact: true),
|
||
const SizedBox(height: 28),
|
||
Text(
|
||
'ВХОД',
|
||
style: TextStyle(color: accent, fontSize: 10, letterSpacing: 2.4),
|
||
),
|
||
const SizedBox(height: 10),
|
||
const Text(
|
||
'С возвращением',
|
||
style: TextStyle(
|
||
fontFamily: 'Cinzel',
|
||
color: Color(0xFFECE4D0),
|
||
fontSize: 30,
|
||
letterSpacing: 1,
|
||
),
|
||
),
|
||
const SizedBox(height: 7),
|
||
const Text(
|
||
'Войди в свой аккаунт, чтобы продолжить путь.',
|
||
style: TextStyle(
|
||
fontFamily: 'Cormorant Garamond',
|
||
fontStyle: FontStyle.italic,
|
||
color: Color(0x80ECE4D0),
|
||
fontSize: 15,
|
||
),
|
||
),
|
||
const SizedBox(height: 22),
|
||
Row(
|
||
children: [
|
||
_Tab(label: 'ВХОД', selected: true, accent: accent),
|
||
_Tab(label: 'РЕГИСТРАЦИЯ', accent: accent),
|
||
],
|
||
),
|
||
const SizedBox(height: 18),
|
||
_FieldLabel(text: 'ЛОГИН ИЛИ EMAIL'),
|
||
const SizedBox(height: 7),
|
||
TextField(
|
||
controller: widget.usernameController,
|
||
enabled: !widget.loading,
|
||
textInputAction: TextInputAction.next,
|
||
autofillHints: const [AutofillHints.username],
|
||
decoration: const InputDecoration(hintText: 'Aranthel'),
|
||
),
|
||
const SizedBox(height: 14),
|
||
_FieldLabel(text: 'ПАРОЛЬ'),
|
||
const SizedBox(height: 7),
|
||
TextField(
|
||
controller: widget.passwordController,
|
||
enabled: !widget.loading,
|
||
obscureText: true,
|
||
textInputAction: TextInputAction.done,
|
||
autofillHints: const [AutofillHints.password],
|
||
onSubmitted: (_) => widget.onSubmit(),
|
||
),
|
||
if (widget.error != null) ...[
|
||
const SizedBox(height: 10),
|
||
Text(
|
||
widget.error!,
|
||
style: const TextStyle(color: Color(0xFFD76A78), fontSize: 12),
|
||
),
|
||
],
|
||
const SizedBox(height: 8),
|
||
Row(
|
||
children: [
|
||
SizedBox(
|
||
width: 24,
|
||
child: Checkbox(
|
||
value: _remember,
|
||
onChanged: widget.loading
|
||
? null
|
||
: (value) => setState(() => _remember = value ?? false),
|
||
),
|
||
),
|
||
const SizedBox(width: 8),
|
||
const Text(
|
||
'Запомнить меня',
|
||
style: TextStyle(color: Color(0x9AECE4D0), fontSize: 11),
|
||
),
|
||
const Spacer(),
|
||
Text(
|
||
'Забыли пароль?',
|
||
style: TextStyle(color: accent, fontSize: 11),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 18),
|
||
SizedBox(
|
||
height: 54,
|
||
child: ElevatedButton(
|
||
onPressed: widget.loading ? null : widget.onSubmit,
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: const Color(0xFF6D499E),
|
||
foregroundColor: const Color(0xFFECE4D0),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(6),
|
||
),
|
||
),
|
||
child: widget.loading
|
||
? const SizedBox.square(
|
||
dimension: 18,
|
||
child: CircularProgressIndicator(strokeWidth: 2),
|
||
)
|
||
: const Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Text(
|
||
'ВОЙТИ',
|
||
style: TextStyle(
|
||
fontFamily: 'Cinzel',
|
||
letterSpacing: 2.5,
|
||
),
|
||
),
|
||
SizedBox(width: 14),
|
||
Icon(Icons.arrow_forward, size: 18),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: 28),
|
||
const Center(
|
||
child: Text(
|
||
'MOONWELL · V 1.0.0 · LOGIN.MOONWELL.GG',
|
||
style: TextStyle(
|
||
fontFamily: 'JetBrains Mono',
|
||
color: Color(0x52ECE4D0),
|
||
fontSize: 8,
|
||
letterSpacing: 1.2,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class _FieldLabel extends StatelessWidget {
|
||
const _FieldLabel({required this.text});
|
||
final String text;
|
||
@override
|
||
Widget build(BuildContext context) => Text(
|
||
text,
|
||
style: const TextStyle(
|
||
fontFamily: 'JetBrains Mono',
|
||
color: Color(0x80ECE4D0),
|
||
fontSize: 9,
|
||
letterSpacing: 1.5,
|
||
),
|
||
);
|
||
}
|
||
|
||
class _Tab extends StatelessWidget {
|
||
const _Tab({
|
||
required this.label,
|
||
required this.accent,
|
||
this.selected = false,
|
||
});
|
||
final String label;
|
||
final Color accent;
|
||
final bool selected;
|
||
@override
|
||
Widget build(BuildContext context) => Expanded(
|
||
child: Container(
|
||
height: 38,
|
||
alignment: Alignment.center,
|
||
decoration: BoxDecoration(
|
||
border: Border(
|
||
bottom: BorderSide(
|
||
color: selected ? accent : const Color(0x247FB8D4),
|
||
),
|
||
),
|
||
),
|
||
child: Text(
|
||
label,
|
||
style: TextStyle(
|
||
fontFamily: 'Cinzel',
|
||
color: selected ? const Color(0xFFECE4D0) : const Color(0x80ECE4D0),
|
||
fontSize: 9,
|
||
letterSpacing: 1.5,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|