Настройки, авторизация

This commit is contained in:
Халимов Рустам
2026-05-14 12:06:34 +03:00
parent e8161d23d4
commit ccffc5a53c
41 changed files with 2556 additions and 1375 deletions
@@ -1,8 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/auth_bloc.dart';
import '../bloc/auth_event.dart';
import '../bloc/auth_state.dart';
import 'package:messenger_app/features/auth/presentation/bloc/auth_bloc.dart';
import 'package:messenger_app/features/auth/presentation/bloc/auth_event.dart';
import 'package:messenger_app/features/auth/presentation/bloc/auth_state.dart';
import 'package:messenger_app/features/settings/presentation/bloc/settings_bloc.dart';
import 'package:messenger_app/features/settings/presentation/bloc/settings_state.dart';
import 'package:messenger_app/features/settings/presentation/pages/settings_page.dart';
import 'package:messenger_app/l10n/app_localizations.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@@ -25,25 +29,41 @@ class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
title: const Text('Вход'),
title: Text(l10n.login),
actions: [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const SettingsPage()),
);
},
),
],
),
body: BlocListener<AuthBloc, AuthState>(
listener: (context, state) {
state.whenOrNull(
authenticated: (userId) {
Navigator.of(context).popUntil((route) => route.isFirst);
// Navigation handled in app.dart home
},
error: (message) {
final errorMessage = message == 'loginFailed'
? l10n.loginFailed
: message == 'registrationFailed'
? l10n.registrationFailed
: message;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
SnackBar(content: Text(errorMessage)),
);
},
);
},
child: Center(
child: Padding(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
@@ -56,20 +76,45 @@ class _LoginPageState extends State<LoginPage> {
size: 80,
color: Color(0xFF2481CC),
),
const SizedBox(height: 16),
BlocBuilder<SettingsBloc, SettingsState>(
builder: (context, state) {
final apiUrl = state.when(
initial: () => '',
loading: () => '',
loaded: (url, _, __, ___, ____) => url,
error: (_) => '',
);
return Column(
children: [
Text(
l10n.server,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
Text(
apiUrl.isEmpty ? l10n.notSpecified : apiUrl,
style: const TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
],
);
},
),
const SizedBox(height: 32),
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email_outlined),
decoration: InputDecoration(
labelText: l10n.username,
prefixIcon: const Icon(Icons.person_outline),
),
keyboardType: TextInputType.emailAddress,
keyboardType: TextInputType.text,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Введите email';
return l10n.enterUsername;
}
if (!value.contains('@')) {
return 'Введите корректный email';
// Регулярка для проверки отсутствия кириллицы и минимум 3 символа
if (value.length < 3 || RegExp(r'[а-яА-ЯёЁ]').hasMatch(value)) {
return l10n.invalidUsername;
}
return null;
},
@@ -77,9 +122,9 @@ class _LoginPageState extends State<LoginPage> {
const SizedBox(height: 16),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Пароль',
prefixIcon: Icon(Icons.lock_outlined),
decoration: InputDecoration(
labelText: l10n.password,
prefixIcon: const Icon(Icons.lock_outlined),
),
obscureText: true,
validator: (value) {
@@ -94,34 +139,46 @@ class _LoginPageState extends State<LoginPage> {
),
const SizedBox(height: 32),
BlocBuilder<AuthBloc, AuthState>(
builder: (context, state) {
if (state is AuthLoading) {
builder: (context, authState) {
if (authState is AuthLoading) {
return const Center(child: CircularProgressIndicator());
}
return ElevatedButton(
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
context.read<AuthBloc>().add(
AuthEvent.loginRequested(
_emailController.text,
_passwordController.text,
),
);
AuthEvent.loginRequested(
_emailController.text,
_passwordController.text,
),
);
}
},
child: const Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: Text('Войти', style: TextStyle(fontSize: 16)),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(l10n.loginButton, style: const TextStyle(fontSize: 16)),
),
);
},
),
const SizedBox(height: 16),
TextButton(
onPressed: () {
// Navigate to register page
BlocBuilder<SettingsBloc, SettingsState>(
builder: (context, settingsState) {
final isRegistrationEnabled = settingsState.when(
initial: () => false,
loading: () => false,
loaded: (_, config, __, ___, status) =>
status == ConnectionStatus.connected && (config?.system.enableRegistration ?? false),
error: (_) => false,
);
return TextButton(
onPressed: isRegistrationEnabled ? () {
// Navigate to register page
} : null,
child: Text(l10n.noAccount),
);
},
child: const Text('Нет аккаунта? Зарегистрироваться'),
),
],
),