Implements authentication flow with user registration
This commit implements a complete authentication flow, including user registration. - Introduces an `AuthRegisterRequested` event to handle user registration. - Persists the user ID in settings upon successful authentication. - Modifies `AuthBloc` to load the user based on the stored ID, improving app launch persistence. - Refactors `UserCubit` to only manage the user state and removes authentication logic. - Removes `UserCubit` initialization from `main.dart` and triggers `AuthStarted` to initiate the authentication process.
This commit is contained in:
@@ -59,7 +59,11 @@ Future<void> initGlobalDependencies() async {
|
||||
null, // Временно null, будет заменен в initUserSpecificDependencies
|
||||
),
|
||||
);
|
||||
getIt.registerFactory<AuthBloc>(() => AuthBloc(userCubit: getIt()));
|
||||
getIt.registerFactory<AuthBloc>(() => AuthBloc(
|
||||
userCubit: getIt(),
|
||||
settingsRepository: getIt(),
|
||||
userRepository: getIt(),
|
||||
));
|
||||
}
|
||||
|
||||
/// Инициализация зависимостей, специфичных для пользователя.
|
||||
|
||||
@@ -3,27 +3,52 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:budget_app/models/user.dart';
|
||||
import 'package:budget_app/logic/user/user_cubit.dart';
|
||||
import 'package:budget_app/injection_container.dart' as di;
|
||||
import 'package:budget_app/data/repositories/interfaces/iglobal_settings_repository.dart';
|
||||
import 'package:budget_app/data/repositories/interfaces/iuser_repository.dart';
|
||||
|
||||
part 'auth_event.dart';
|
||||
part 'auth_state.dart';
|
||||
|
||||
class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
final UserCubit _userCubit;
|
||||
final IGlobalSettingsRepository _settingsRepository;
|
||||
final IUserRepository _userRepository;
|
||||
|
||||
AuthBloc({required UserCubit userCubit}) : _userCubit = userCubit, super(AuthInitial()) {
|
||||
AuthBloc({
|
||||
required UserCubit userCubit,
|
||||
required IGlobalSettingsRepository settingsRepository,
|
||||
required IUserRepository userRepository,
|
||||
}) : _userCubit = userCubit,
|
||||
_settingsRepository = settingsRepository,
|
||||
_userRepository = userRepository,
|
||||
super(AuthInitial()) {
|
||||
on<AuthStarted>(_onAuthStarted);
|
||||
on<AuthLoggedIn>(_onAuthLoggedIn);
|
||||
on<AuthLoggedOut>(_onAuthLoggedOut);
|
||||
on<AuthRegisterRequested>(_onAuthRegisterRequested);
|
||||
}
|
||||
|
||||
void _onAuthStarted(AuthStarted event, Emitter<AuthState> emit) async {
|
||||
final userState = _userCubit.state;
|
||||
if (userState is UserLoaded && userState.user != null) {
|
||||
// Если пользователь уже загружен, инициализируем его зависимости.
|
||||
await di.initUserSpecificDependencies(userState.user!.id);
|
||||
emit(AuthAuthenticated(user: userState.user!));
|
||||
} else {
|
||||
emit(AuthUnauthenticated());
|
||||
try {
|
||||
final userId = await _settingsRepository.getCurrentUserId();
|
||||
if (userId != null) {
|
||||
final user = await _userRepository.getById(userId);
|
||||
if (user != null) {
|
||||
// Пользователь найден, инициализируем зависимости и аутентифицируем
|
||||
await di.initUserSpecificDependencies(user.id);
|
||||
_userCubit.setUser(user);
|
||||
emit(AuthAuthenticated(user: user));
|
||||
} else {
|
||||
// ID есть, а пользователя нет (ошибка) -> сбрасываем
|
||||
await _settingsRepository.setCurrentUserId(null);
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
} else {
|
||||
// ID не найден, пользователь не аутентифицирован
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AuthError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,14 +56,40 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
// Инициализируем зависимости для вошедшего пользователя.
|
||||
await di.initUserSpecificDependencies(event.user.id);
|
||||
_userCubit.setUser(event.user);
|
||||
await _settingsRepository.setCurrentUserId(event.user.id);
|
||||
emit(AuthAuthenticated(user: event.user));
|
||||
}
|
||||
|
||||
void _onAuthLoggedOut(AuthLoggedOut event, Emitter<AuthState> emit) async {
|
||||
// Сбрасываем пользовательские зависимости.
|
||||
await di.resetUserSpecificDependencies();
|
||||
await _settingsRepository.setCurrentUserId(null);
|
||||
_userCubit.logout();
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
|
||||
Future<void> _onAuthRegisterRequested(
|
||||
AuthRegisterRequested event, Emitter<AuthState> emit) async {
|
||||
emit(AuthLoading());
|
||||
try {
|
||||
// 1. Создаем пользователя
|
||||
final user = User(name: event.name, email: event.email);
|
||||
await _userRepository.add(user);
|
||||
|
||||
// 2. Инициализируем его зависимости
|
||||
await di.initUserSpecificDependencies(user.id);
|
||||
|
||||
// 3. Создаем начальные данные (теперь это сработает)
|
||||
await _userCubit.createInitialData(user.id);
|
||||
|
||||
// 4. Сохраняем и устанавливаем пользователя
|
||||
await _settingsRepository.setCurrentUserId(user.id);
|
||||
_userCubit.setUser(user);
|
||||
emit(AuthAuthenticated(user: user));
|
||||
} catch (e) {
|
||||
emit(AuthError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,10 +7,8 @@ abstract class AuthEvent extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
// Событие, которое будет вызываться при инициализации BLoC
|
||||
class AuthStarted extends AuthEvent {}
|
||||
|
||||
// Событие, которое будет вызываться при входе пользователя
|
||||
class AuthLoggedIn extends AuthEvent {
|
||||
final User user;
|
||||
|
||||
@@ -20,5 +18,15 @@ class AuthLoggedIn extends AuthEvent {
|
||||
List<Object> get props => [user];
|
||||
}
|
||||
|
||||
// Событие, которое будет вызываться при выходе пользователя
|
||||
class AuthLoggedOut extends AuthEvent {}
|
||||
|
||||
class AuthRegisterRequested extends AuthEvent {
|
||||
final String name;
|
||||
final String email;
|
||||
|
||||
const AuthRegisterRequested({required this.name, required this.email});
|
||||
|
||||
@override
|
||||
List<Object> get props => [name, email];
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ abstract class AuthState extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
// Начальное состояние, пока мы не знаем, аутентифицирован ли пользователь
|
||||
class AuthInitial extends AuthState {}
|
||||
|
||||
// Состояние, когда пользователь аутентифицирован
|
||||
class AuthLoading extends AuthState {}
|
||||
|
||||
class AuthAuthenticated extends AuthState {
|
||||
final User user;
|
||||
|
||||
@@ -20,5 +20,14 @@ class AuthAuthenticated extends AuthState {
|
||||
List<Object> get props => [user];
|
||||
}
|
||||
|
||||
// Состояние, когда пользователь не аутентифицирован
|
||||
class AuthUnauthenticated extends AuthState {}
|
||||
|
||||
class AuthError extends AuthState {
|
||||
final String message;
|
||||
|
||||
const AuthError(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ import '/utils/transaction_utils.dart';
|
||||
|
||||
part 'user_state.dart';
|
||||
|
||||
/// Cubit для управления состоянием пользователя
|
||||
/// Cubit для управления состоянием пользователя.
|
||||
/// Больше не управляет процессом входа, а только хранит состояние пользователя.
|
||||
class UserCubit extends Cubit<UserState> {
|
||||
final IGlobalSettingsRepository _settingsRepository;
|
||||
final IUserRepository _userRepository;
|
||||
@@ -56,37 +57,6 @@ class UserCubit extends Cubit<UserState> {
|
||||
set transactionRepository(ITransactionRepository? repo) =>
|
||||
_transactionRepository = repo;
|
||||
|
||||
// Метод для инициализации UserCubit
|
||||
Future<void> init() async {
|
||||
await _init();
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
emit(UserLoading(progress: 0.1, message: 'Инициализация...'));
|
||||
try {
|
||||
final userId = await _settingsRepository.getCurrentUserId();
|
||||
User? currentUser;
|
||||
|
||||
if (userId != null) {
|
||||
emit(UserLoading(progress: 0.3, message: 'Поиск пользователя...'));
|
||||
currentUser = await _userRepository.getById(userId);
|
||||
if (currentUser == null) {
|
||||
await _settingsRepository.setCurrentUserId(null);
|
||||
}
|
||||
}
|
||||
|
||||
emit(UserLoading(progress: 1.0, message: 'Завершение...'));
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
emit(UserLoaded(currentUser));
|
||||
} catch (e, stack) {
|
||||
_logger.e(
|
||||
'--- UserCubit: FATAL ERROR in _init ---',
|
||||
error: e,
|
||||
stackTrace: stack,
|
||||
);
|
||||
emit(UserError('Ошибка загрузки пользователя: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
Future<void> createInitialData(String userId) async {
|
||||
// Проверяем, что репозитории были установлены, прежде чем их использовать.
|
||||
if (_categoryRepository == null ||
|
||||
@@ -120,22 +90,6 @@ class UserCubit extends Cubit<UserState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setCurrentUser(User user) async {
|
||||
await _settingsRepository.setCurrentUserId(user.id);
|
||||
emit(UserLoaded(user));
|
||||
}
|
||||
|
||||
/// Устанавливает текущего пользователя
|
||||
Future<void> setCurrentUser(User user) async {
|
||||
emit(UserLoading());
|
||||
try {
|
||||
await _setCurrentUser(user);
|
||||
} catch (e, stack) {
|
||||
_logger.e('Error setting current user', error: e, stackTrace: stack);
|
||||
emit(UserError('Ошибка установки пользователя: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Устанавливает текущего пользователя (прямая установка без загрузки)
|
||||
void setUser(User user) {
|
||||
emit(UserLoaded(user));
|
||||
@@ -153,23 +107,6 @@ class UserCubit extends Cubit<UserState> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Создает нового пользователя и устанавливает его как текущего
|
||||
Future<void> createAndSetUser(String name, String email) async {
|
||||
emit(UserLoading());
|
||||
try {
|
||||
final user = User(name: name, email: email);
|
||||
await _userRepository.add(user);
|
||||
|
||||
// Создаем начальные данные для нового пользователя
|
||||
await createInitialData(user.id);
|
||||
|
||||
await _setCurrentUser(user);
|
||||
} catch (e, stack) {
|
||||
_logger.e('Error creating user', error: e, stackTrace: stack);
|
||||
emit(UserError('Ошибка создания пользователя: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Возвращает всех пользователей
|
||||
Future<List<User>> getAllUsers() async {
|
||||
try {
|
||||
@@ -180,3 +117,4 @@ class UserCubit extends Cubit<UserState> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+59
-74
@@ -26,91 +26,76 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// 1. Предоставляем глобальные Blocs, которые доступны всегда.
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => GetIt.instance<UserCubit>()..init(),
|
||||
create: (context) => GetIt.instance<UserCubit>(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => GetIt.instance<AuthBloc>(),
|
||||
create: (context) => GetIt.instance<AuthBloc>()..add(AuthStarted()),
|
||||
),
|
||||
],
|
||||
child: BlocListener<UserCubit, UserState>(
|
||||
// 2. Запускаем проверку аутентификации, как только UserCubit загрузил данные.
|
||||
listenWhen: (previous, current) => current is UserLoaded,
|
||||
listener: (context, userState) {
|
||||
context.read<AuthBloc>().add(AuthStarted());
|
||||
},
|
||||
// 3. В зависимости от статуса аутентификации, строим разное дерево виджетов.
|
||||
child: BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, authState) {
|
||||
// 4. Если пользователь аутентифицирован...
|
||||
if (authState is AuthAuthenticated) {
|
||||
// ...предоставляем все пользовательские зависимости.
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(create: (context) => GetIt.instance<SettingsCubit>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<TransactionBloc>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<SmsCubit>()),
|
||||
// Можно добавить и остальные, если они нужны глобально в авторизованной зоне
|
||||
],
|
||||
// И строим приложение с пользовательской темой.
|
||||
child: BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, settingsState) {
|
||||
final isDarkMode = settingsState is SettingsLoaded
|
||||
? settingsState.isDarkMode
|
||||
: false;
|
||||
final languageCode = settingsState is SettingsLoaded
|
||||
? settingsState.languageCode
|
||||
: 'ru';
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Budget App',
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: Locale(languageCode),
|
||||
home: const HomePage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Если пользователь НЕ аутентифицирован, показываем SplashScreen или LoginPage
|
||||
// с темой по умолчанию.
|
||||
return MaterialApp(
|
||||
title: 'Budget App',
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: ThemeMode.light, // Тема по умолчанию
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
child: BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, authState) {
|
||||
if (authState is AuthAuthenticated) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(create: (context) => GetIt.instance<SettingsCubit>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<TransactionBloc>()),
|
||||
BlocProvider(create: (context) => GetIt.instance<SmsCubit>()),
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: const Locale('ru'), // Язык по умолчанию
|
||||
home: BlocBuilder<UserCubit, UserState>(
|
||||
builder: (context, userState) {
|
||||
if (userState is UserLoading || userState is UserInitial) {
|
||||
return const SplashScreen();
|
||||
}
|
||||
return const LoginPage();
|
||||
child: BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, settingsState) {
|
||||
final isDarkMode = settingsState is SettingsLoaded
|
||||
? settingsState.isDarkMode
|
||||
: false;
|
||||
final languageCode = settingsState is SettingsLoaded
|
||||
? settingsState.languageCode
|
||||
: 'ru';
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Budget App',
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: Locale(languageCode),
|
||||
home: const HomePage(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
Widget homeWidget;
|
||||
if (authState is AuthInitial) {
|
||||
homeWidget = const SplashScreen();
|
||||
} else {
|
||||
homeWidget = const LoginPage();
|
||||
}
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Budget App',
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: ThemeMode.light, // Тема по умолчанию
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: const Locale('ru'), // Язык по умолчанию
|
||||
home: homeWidget,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '/l10n/app_localizations.dart';
|
||||
import '../../logic/auth/auth_bloc.dart';
|
||||
import '../../logic/user/user_cubit.dart'; // Импортируем UserCubit вместо UserService
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
@@ -17,26 +16,26 @@ class _LoginPageState extends State<LoginPage> {
|
||||
final _emailController = TextEditingController();
|
||||
final _nameController = TextEditingController();
|
||||
|
||||
void _login() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
context.read<AuthBloc>().add(AuthRegisterRequested(
|
||||
name: _nameController.text,
|
||||
email: _emailController.text,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(
|
||||
context,
|
||||
)!; // Получаем экземпляр локализации
|
||||
final localizations = AppLocalizations.of(context)!;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(localizations.loginPageTitle),
|
||||
), // Локализованный заголовок
|
||||
body: BlocListener<UserCubit, UserState>(
|
||||
// Слушаем изменения состояния UserCubit
|
||||
listenWhen: (previous, current) =>
|
||||
current is UserLoaded || current is UserError,
|
||||
),
|
||||
body: BlocListener<AuthBloc, AuthState>(
|
||||
listener: (context, state) {
|
||||
if (state is UserLoaded && state.user != null) {
|
||||
// Комментарий: При успешном создании пользователя отправляем событие в AuthBloc
|
||||
context.read<AuthBloc>().add(AuthLoggedIn(user: state.user!));
|
||||
} else if (state is UserError) {
|
||||
// Комментарий: Показываем ошибку пользователю
|
||||
if (state is AuthError) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(state.message)),
|
||||
);
|
||||
@@ -52,11 +51,10 @@ class _LoginPageState extends State<LoginPage> {
|
||||
controller: _nameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: localizations.nameFieldLabel,
|
||||
), // Локализованный текст
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return localizations
|
||||
.nameFieldEmptyError; // Локализованный текст
|
||||
return localizations.nameFieldEmptyError;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
@@ -65,41 +63,28 @@ class _LoginPageState extends State<LoginPage> {
|
||||
controller: _emailController,
|
||||
decoration: InputDecoration(
|
||||
labelText: localizations.emailFieldLabel,
|
||||
), // Локализованный текст
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return localizations
|
||||
.emailFieldEmptyError; // Локализованный текст
|
||||
return localizations.emailFieldEmptyError;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
BlocBuilder<UserCubit, UserState>(
|
||||
// Строим кнопку в зависимости от состояния UserCubit
|
||||
BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, state) {
|
||||
final isLoading = state is UserLoading;
|
||||
|
||||
final isLoading = state is AuthLoading;
|
||||
|
||||
return ElevatedButton(
|
||||
onPressed: isLoading ? null : () {
|
||||
// Комментарий: Блокируем кнопку во время загрузки
|
||||
if (_formKey.currentState!.validate()) {
|
||||
// Комментарий: Используем UserCubit для создания пользователя
|
||||
context.read<UserCubit>().createAndSetUser(
|
||||
_nameController.text,
|
||||
_emailController.text,
|
||||
);
|
||||
}
|
||||
},
|
||||
onPressed: isLoading ? null : _login,
|
||||
child: isLoading
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
) // Показываем индикатор загрузки
|
||||
: Text(
|
||||
localizations.loginButtonText,
|
||||
), // Локализованный текст
|
||||
)
|
||||
: Text(localizations.loginButtonText),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -111,3 +96,4 @@ class _LoginPageState extends State<LoginPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,60 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '/logic/user/user_cubit.dart';
|
||||
|
||||
class SplashScreen extends StatelessWidget {
|
||||
const SplashScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: BlocBuilder<UserCubit, UserState>(
|
||||
builder: (context, state) {
|
||||
if (state is UserLoading) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
state.message,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'${(state.progress * 100).toInt()}%',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (state is UserError) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Ошибка загрузки',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(state.message),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: () => context.read<UserCubit>().init(),
|
||||
child: const Text('Повторить'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 20),
|
||||
Text('Загрузка...'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user