Fix for loop

This commit is contained in:
2025-07-07 18:11:03 +03:00
parent 9bae137d08
commit 4670cdf5fb
6 changed files with 203 additions and 76 deletions
+9 -2
View File
@@ -15,8 +15,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
on<AuthLoggedOut>(_onAuthLoggedOut);
}
void _onAuthStarted(AuthStarted event, Emitter<AuthState> emit) {
// Получаем текущее состояние пользователя из UserCubit
void _onAuthStarted(AuthStarted event, Emitter<AuthState> emit) async {
// Комментарий: Инициализируем UserCubit при старте AuthBloc.
// Это гарантирует, что UserCubit загрузит данные пользователя
// перед тем, как AuthBloc будет принимать решение об аутентификации.
await _userCubit.init();
// Получаем текущее состояние пользователя из UserCubit после инициализации
final userState = _userCubit.state;
if (userState is UserLoaded && userState.user != null) {
emit(AuthAuthenticated(user: userState.user!));
@@ -26,6 +31,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
void _onAuthLoggedIn(AuthLoggedIn event, Emitter<AuthState> emit) {
// Комментарий: Обновляем UserCubit с новым пользователем.
_userCubit.setUser(event.user);
emit(AuthAuthenticated(user: event.user));
}
+47 -26
View File
@@ -1,17 +1,19 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';
import 'package:logger/logger.dart';
import '/data/repositories/interfaces/iglobal_settings_repository.dart';
import '/data/repositories/interfaces/iuser_repository.dart';
import '/data/repositories/interfaces/icategory_repository.dart';
import '/data/repositories/interfaces/iglobal_settings_repository.dart';
import '/data/repositories/interfaces/itag_repository.dart';
import '/data/repositories/interfaces/itransaction_repository.dart';
import '/data/repositories/interfaces/iuser_repository.dart';
import '/models/user.dart';
import '/utils/category_utils.dart';
import '/utils/tag_utils.dart';
import '/utils/transaction_utils.dart';
part 'user_state.dart';
/// Cubit для управления состоянием пользователя
@@ -29,21 +31,26 @@ class UserCubit extends Cubit<UserState> {
required ICategoryRepository categoryRepository,
required ITagRepository tagRepository,
required ITransactionRepository transactionRepository,
}) : _settingsRepository = settingsRepository,
_userRepository = userRepository,
_categoryRepository = categoryRepository,
_tagRepository = tagRepository,
_transactionRepository = transactionRepository,
super(UserInitial()) {
_init();
}) : _settingsRepository = settingsRepository,
_userRepository = userRepository,
_categoryRepository = categoryRepository,
_tagRepository = tagRepository,
_transactionRepository = transactionRepository,
super(UserInitial());
// Метод для инициализации UserCubit
Future<void> init() async {
await _init();
}
Future<void> _init() async {
emit(UserLoading());
emit(UserLoading(progress: 0.1, message: 'Поиск пользователя...'));
try {
// Этап 1: Проверка существующего пользователя
final userId = await _settingsRepository.getCurrentUserId();
emit(UserLoading(progress: 0.3, message: 'Проверка пользователя...'));
User? currentUser;
if (userId != null) {
currentUser = await _userRepository.getById(userId);
if (currentUser == null) {
@@ -52,18 +59,30 @@ class UserCubit extends Cubit<UserState> {
}
}
// Этап 2: Создание пользователя по умолчанию при необходимости
if (currentUser == null) {
emit(UserLoading(progress: 0.5, message: 'Проверка данных...'));
final allUsers = await _userRepository.getAll();
if (allUsers.isEmpty) {
_logger.i('No users found, creating default user');
currentUser = await _createDefaultUser();
emit(UserLoading(progress: 0.6, message: 'Создание пользователя...'));
// Комментарий: Вызываем создание пользователя по умолчанию.
// Этот метод сам установит состояние UserLoaded, поэтому после него нужно завершить выполнение _init.
await _createDefaultUser();
return;
} else {
_logger.i('Setting first user as current');
currentUser = allUsers.first;
// Комментарий: Устанавливаем первого пользователя как текущего.
// Этот метод также устанавливает состояние UserLoaded, поэтому выходим.
await _setCurrentUser(currentUser);
return;
}
}
// Этап 3: Завершение инициализации (этот блок теперь выполняется только для уже существующих пользователей)
emit(UserLoading(progress: 1.0, message: 'Завершение...'));
await Future.delayed(const Duration(milliseconds: 300));
emit(UserLoaded(currentUser));
} catch (e, stack) {
_logger.e('Error initializing user cubit', error: e, stackTrace: stack);
@@ -85,28 +104,29 @@ class UserCubit extends Cubit<UserState> {
return user;
}
/// Создает начальные данные для нового пользователя (категории, теги, транзакции)
Future<void> _createInitialData(String userId) async {
try {
// Добавляем начальные категории
// Разбиваем создание данных на этапы
emit(UserLoading(progress: 0.7, message: 'Создание категорий...'));
await _categoryRepository.addAll(
CategoryUtils.getDefaultCategories(userId),
);
// Добавляем начальные теги
await _tagRepository.addAll(
TagUtils.getDefaultTags(userId),
);
emit(UserLoading(progress: 0.8, message: 'Создание тегов...'));
await _tagRepository.addAll(TagUtils.getDefaultTags(userId));
// Добавляем примеры транзакций
emit(UserLoading(progress: 0.9, message: 'Создание транзакций...'));
await _transactionRepository.addAll(
TransactionUtils.getSampleTransactions(userId),
);
_logger.i('Initial data created for user: $userId');
} catch (e, stack) {
_logger.e('Error creating initial data for user: $userId', error: e, stackTrace: stack);
// Не прерываем создание пользователя из-за ошибки создания начальных данных
_logger.e(
'Error creating initial data for user: $userId',
error: e,
stackTrace: stack,
);
}
}
@@ -126,6 +146,11 @@ class UserCubit extends Cubit<UserState> {
}
}
/// Устанавливает текущего пользователя (прямая установка без загрузки)
void setUser(User user) {
emit(UserLoaded(user));
}
/// Выход из системы
Future<void> logout() async {
emit(UserLoading());
@@ -142,10 +167,7 @@ class UserCubit extends Cubit<UserState> {
Future<void> createAndSetUser(String name, String email) async {
emit(UserLoading());
try {
final user = User(
name: name,
email: email,
);
final user = User(name: name, email: email);
await _userRepository.add(user);
// Создаем начальные данные для нового пользователя
@@ -168,4 +190,3 @@ class UserCubit extends Cubit<UserState> {
}
}
}
+8 -2
View File
@@ -11,9 +11,15 @@ class UserInitial extends UserState {
const UserInitial();
}
/// Состояние загрузки данных
/// Состояние загрузки данных с прогрессом и сообщением
class UserLoading extends UserState {
const UserLoading();
final double progress;
final String message;
const UserLoading({
this.progress = 0.0,
this.message = '',
});
}
/// Состояние успешной загрузки пользователя
+76 -46
View File
@@ -1,17 +1,34 @@
import 'package:budget_app/pages/home/home_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get_it/get_it.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; // Добавляем импорт
import 'package:get_it/get_it.dart';
import '/l10n/app_localizations.dart';
import 'logic/auth/auth_bloc.dart';
import 'pages/login/login_page.dart';
import 'package:budget_app/pages/home/home_page.dart';
import 'theme/app_theme.dart';
import 'injection_container.dart' as di;
import 'logic/auth/auth_bloc.dart';
import 'logic/settings/settings_cubit.dart'; // Импортируем SettingsCubit
import 'logic/sms/sms_cubit.dart';
import 'logic/transaction/transaction_bloc.dart';
import 'logic/user/user_cubit.dart'; // Импортируем UserCubit
import 'pages/login/login_page.dart';
import 'theme/app_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import '/l10n/app_localizations.dart';
import '/logic/auth/auth_bloc.dart';
import '/logic/settings/settings_cubit.dart';
import '/logic/sms/sms_cubit.dart';
import '/logic/transaction/transaction_bloc.dart';
import '/logic/user/user_cubit.dart';
import '/pages/home/home_page.dart';
import '/pages/login/login_page.dart';
import '/pages/splash/splash_screen.dart';
import '/theme/app_theme.dart';
import 'injection_container.dart' as di;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -19,58 +36,71 @@ void main() async {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (context) => GetIt.instance<UserCubit>()), // Добавляем UserCubit для управления пользователями
BlocProvider(create: (context) => GetIt.instance<AuthBloc>()..add(AuthStarted())),
BlocProvider(
create: (context) => GetIt.instance<UserCubit>()..init(),
),
BlocProvider(
create: (context) => GetIt.instance<AuthBloc>(),
),
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: AppLocalizations.of(context)?.appTitle ?? '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: BlocBuilder<AuthBloc, AuthState>(
builder: (context, authState) {
if (authState is AuthAuthenticated) {
return const HomePage();
} else {
return const LoginPage();
}
},
),
);
child: BlocListener<UserCubit, UserState>(
listener: (context, userState) {
if (userState is UserLoaded && userState.user != null) {
context.read<AuthBloc>().add(AuthStarted());
}
},
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: AppLocalizations.of(context)?.appTitle ?? '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: BlocBuilder<UserCubit, UserState>(
builder: (context, userState) {
if (userState is UserLoading) {
return const SplashScreen();
}
return BlocBuilder<AuthBloc, AuthState>(
builder: (context, authState) {
if (authState is AuthAuthenticated) {
return const HomePage();
} else {
return const LoginPage();
}
},
);
},
),
);
},
),
),
);
}
+2
View File
@@ -29,6 +29,8 @@ class _LoginPageState extends State<LoginPage> {
), // Локализованный заголовок
body: BlocListener<UserCubit, UserState>(
// Слушаем изменения состояния UserCubit
listenWhen: (previous, current) =>
current is UserLoaded || current is UserError,
listener: (context, state) {
if (state is UserLoaded && state.user != null) {
// Комментарий: При успешном создании пользователя отправляем событие в AuthBloc
+61
View File
@@ -0,0 +1,61 @@
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());
},
),
);
}
}