Chepuhagram/lib/presentation/screens/login_screen.dart

205 lines
7.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:chepuhagram/presentation/screens/contacts_screen.dart';
import 'package:chepuhagram/presentation/screens/account_setup_screen.dart';
import 'package:chepuhagram/presentation/screens/key_recovery_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../logic/auth_provider.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
final _totpController = TextEditingController();
bool _showTotpField = false;
String? _errorMessage;
@override
Widget build(BuildContext context) {
final authProvider = context.watch<AuthProvider>();
return Scaffold(
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Иконка
Icon(
Icons.messenger_outline,
size: 80,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 16),
Text(
"Чепухаграм",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 32),
// Поле Логин
TextFormField(
controller: _usernameController,
decoration: InputDecoration(
labelText: "Логин",
prefixIcon: const Icon(Icons.person_outline),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
fillColor: Theme.of(context).colorScheme.primary,
iconColor: Theme.of(context).colorScheme.primary,
hoverColor: Theme.of(context).colorScheme.primary,
focusColor: Theme.of(context).colorScheme.primary,
),
validator: (value) => value!.isEmpty ? "Введите логин" : null,
),
const SizedBox(height: 16),
// Поле Пароль
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: "Пароль",
prefixIcon: const Icon(Icons.lock_outline),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
fillColor: Theme.of(context).colorScheme.primary,
iconColor: Theme.of(context).colorScheme.primary,
hoverColor: Theme.of(context).colorScheme.primary,
focusColor: Theme.of(context).colorScheme.primary,
),
validator: (value) =>
value!.length < 6 ? "Минимум 6 символов" : null,
),
const SizedBox(height: 16),
// Поле TOTP, если требуется
if (_showTotpField)
TextFormField(
controller: _totpController,
decoration: InputDecoration(
labelText: "TOTP код",
prefixIcon: const Icon(Icons.security),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
fillColor: Theme.of(context).colorScheme.primary,
iconColor: Theme.of(context).colorScheme.primary,
hoverColor: Theme.of(context).colorScheme.primary,
focusColor: Theme.of(context).colorScheme.primary,
),
validator: (value) => value!.isEmpty ? "Введите TOTP код" : null,
),
if (_showTotpField) const SizedBox(height: 16),
// Сообщение об ошибке
if (_errorMessage != null)
Text(
_errorMessage!,
style: TextStyle(color: Theme.of(context).colorScheme.error),
textAlign: TextAlign.center,
),
if (_errorMessage != null) const SizedBox(height: 16),
const SizedBox(height: 24),
// Кнопка Входа
ElevatedButton(
onPressed: authProvider.isLoading ? null : _submit,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: authProvider.isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text("Войти", style: TextStyle(fontSize: 16)),
),
],
),
),
),
),
);
}
void _submit() async {
try {
if (!_formKey.currentState!.validate()) return;
final authProvider = context.read<AuthProvider>();
final success = await authProvider.login(
_usernameController.text,
_passwordController.text,
totpCode: _showTotpField ? _totpController.text : null,
);
if (success && mounted) {
await authProvider.initRealtime();
// Определяем путь пользователя после входа
if (authProvider.needsSetup) {
// Путь А: Первичная настройка
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const AccountSetupScreen()),
);
} else if (authProvider.needsKeyRecovery) {
// Путь В: Восстановление ключей
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const KeyRecoveryScreen()),
);
} else {
// Путь Б: Нормальный вход
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => const ContactsScreen()),
);
}
}
} catch (e) {
final error = e.toString().replaceAll('Exception: ', '');
if (error.contains('TOTP код требуется')) {
setState(() {
_showTotpField = true;
_errorMessage = error;
});
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(error)),
);
}
}
}
@override
void dispose() {
_usernameController.dispose();
_passwordController.dispose();
_totpController.dispose();
super.dispose();
}
}