This commit is contained in:
2025-06-27 16:42:12 +03:00
parent 74f5b9872a
commit b2c8189cb2
12 changed files with 314 additions and 9 deletions
+16
View File
@@ -6,7 +6,12 @@ import 'data/repositories/hive_transaction_repository.dart';
import 'data/repositories/interfaces/icategory_repository.dart';
import 'data/repositories/interfaces/itag_repository.dart';
import 'data/repositories/interfaces/itransaction_repository.dart';
import 'data/repositories/hive_user_repository.dart';
import 'data/repositories/interfaces/iuser_repository.dart';
import 'logic/auth/auth_bloc.dart';
import 'logic/transaction/transaction_bloc.dart';
import 'services/settings_service.dart';
import 'services/user_service.dart';
final getIt = GetIt.instance;
@@ -29,4 +34,15 @@ Future<void> initDependencies() async {
getIt.registerSingleton<ITransactionRepository>(
HiveTransactionRepository(HiveService.transactions),
);
getIt.registerSingleton<IUserRepository>(
HiveUserRepository(HiveService.users),
);
// Services
getIt.registerSingleton<UserService>(UserService(getIt()));
// Blocs
getIt.registerFactory<AuthBloc>(() => AuthBloc(userService: getIt()));
getIt.registerFactory<TransactionBloc>(() => TransactionBloc(transactionRepository: getIt()));
}
View File
+35
View File
@@ -0,0 +1,35 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:budget_app/models/user.dart';
import 'package:budget_app/services/user_service.dart';
part 'auth_event.dart';
part 'auth_state.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final UserService _userService;
AuthBloc({required UserService userService}) : _userService = userService, super(AuthInitial()) {
on<AuthStarted>(_onAuthStarted);
on<AuthLoggedIn>(_onAuthLoggedIn);
on<AuthLoggedOut>(_onAuthLoggedOut);
}
void _onAuthStarted(AuthStarted event, Emitter<AuthState> emit) {
final user = _userService.currentUser;
if (user != null) {
emit(AuthAuthenticated(user: user));
} else {
emit(AuthUnauthenticated());
}
}
void _onAuthLoggedIn(AuthLoggedIn event, Emitter<AuthState> emit) {
emit(AuthAuthenticated(user: event.user));
}
void _onAuthLoggedOut(AuthLoggedOut event, Emitter<AuthState> emit) {
_userService.logout();
emit(AuthUnauthenticated());
}
}
+24
View File
@@ -0,0 +1,24 @@
part of 'auth_bloc.dart';
abstract class AuthEvent extends Equatable {
const AuthEvent();
@override
List<Object> get props => [];
}
// Событие, которое будет вызываться при инициализации BLoC
class AuthStarted extends AuthEvent {}
// Событие, которое будет вызываться при входе пользователя
class AuthLoggedIn extends AuthEvent {
final User user;
const AuthLoggedIn({required this.user});
@override
List<Object> get props => [user];
}
// Событие, которое будет вызываться при выходе пользователя
class AuthLoggedOut extends AuthEvent {}
+24
View File
@@ -0,0 +1,24 @@
part of 'auth_bloc.dart';
abstract class AuthState extends Equatable {
const AuthState();
@override
List<Object> get props => [];
}
// Начальное состояние, пока мы не знаем, аутентифицирован ли пользователь
class AuthInitial extends AuthState {}
// Состояние, когда пользователь аутентифицирован
class AuthAuthenticated extends AuthState {
final User user;
const AuthAuthenticated({required this.user});
@override
List<Object> get props => [user];
}
// Состояние, когда пользователь не аутентифицирован
class AuthUnauthenticated extends AuthState {}
View File
@@ -0,0 +1,67 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:budget_app/data/repositories/interfaces/itransaction_repository.dart';
import 'package:budget_app/models/transaction_record.dart';
part 'transaction_event.dart';
part 'transaction_state.dart';
class TransactionBloc extends Bloc<TransactionEvent, TransactionState> {
final ITransactionRepository _transactionRepository;
TransactionBloc({required ITransactionRepository transactionRepository})
: _transactionRepository = transactionRepository,
super(TransactionInitial()) {
on<LoadTransactions>(_onLoadTransactions);
on<AddTransaction>(_onAddTransaction);
on<UpdateTransaction>(_onUpdateTransaction);
on<DeleteTransaction>(_onDeleteTransaction);
}
void _onLoadTransactions(LoadTransactions event, Emitter<TransactionState> emit) async {
emit(TransactionLoading());
try {
final transactions = await _transactionRepository.getAllByUser(event.userId);
emit(TransactionLoaded(transactions: transactions));
} catch (e) {
emit(TransactionError(message: e.toString()));
}
}
void _onAddTransaction(AddTransaction event, Emitter<TransactionState> emit) async {
try {
await _transactionRepository.add(event.transaction);
final transactions = await _transactionRepository.getAllByUser(event.transaction.userId);
emit(TransactionLoaded(transactions: transactions));
} catch (e) {
emit(TransactionError(message: e.toString()));
}
}
void _onUpdateTransaction(UpdateTransaction event, Emitter<TransactionState> emit) async {
try {
await _transactionRepository.update(event.transaction);
final transactions = await _transactionRepository.getAllByUser(event.transaction.userId);
emit(TransactionLoaded(transactions: transactions));
} catch (e) {
emit(TransactionError(message: e.toString()));
}
}
void _onDeleteTransaction(DeleteTransaction event, Emitter<TransactionState> emit) async {
try {
// Для удаления нам нужен userId, который мы можем получить из текущего состояния
if (state is TransactionLoaded) {
final loadedState = state as TransactionLoaded;
if (loadedState.transactions.isNotEmpty) {
final userId = loadedState.transactions.first.userId;
await _transactionRepository.delete(event.transactionId);
final transactions = await _transactionRepository.getAllByUser(userId);
emit(TransactionLoaded(transactions: transactions));
}
}
} catch (e) {
emit(TransactionError(message: e.toString()));
}
}
}
@@ -0,0 +1,44 @@
part of 'transaction_bloc.dart';
abstract class TransactionEvent extends Equatable {
const TransactionEvent();
@override
List<Object> get props => [];
}
class LoadTransactions extends TransactionEvent {
final String userId;
const LoadTransactions({required this.userId});
@override
List<Object> get props => [userId];
}
class AddTransaction extends TransactionEvent {
final TransactionRecord transaction;
const AddTransaction({required this.transaction});
@override
List<Object> get props => [transaction];
}
class UpdateTransaction extends TransactionEvent {
final TransactionRecord transaction;
const UpdateTransaction({required this.transaction});
@override
List<Object> get props => [transaction];
}
class DeleteTransaction extends TransactionEvent {
final String transactionId;
const DeleteTransaction({required this.transactionId});
@override
List<Object> get props => [transactionId];
}
@@ -0,0 +1,30 @@
part of 'transaction_bloc.dart';
abstract class TransactionState extends Equatable {
const TransactionState();
@override
List<Object> get props => [];
}
class TransactionInitial extends TransactionState {}
class TransactionLoading extends TransactionState {}
class TransactionLoaded extends TransactionState {
final List<TransactionRecord> transactions;
const TransactionLoaded({this.transactions = const []});
@override
List<Object> get props => [transactions];
}
class TransactionError extends TransactionState {
final String message;
const TransactionError({required this.message});
@override
List<Object> get props => [message];
}
+31 -9
View File
@@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'logic/auth/auth_bloc.dart';
import 'package:budget_app/pages/home_page.dart';
import 'theme/app_theme.dart';
import 'package:hive_ce_flutter/hive_flutter.dart';
@@ -12,19 +14,26 @@ void main() async {
runApp(const MyApp());
}
/// Главный виджет приложения
///
/// Управляет:
/// - Состоянием темы (темная/светлая)
/// - Конфигурацией MaterialApp
class MyApp extends StatefulWidget {
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => GetIt.instance<AuthBloc>()..add(AuthStarted()),
child: const AppView(),
);
}
}
class _MyAppState extends State<MyApp> {
class AppView extends StatefulWidget {
const AppView({super.key});
@override
State<AppView> createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
late final SettingsService _settingsService;
@override
@@ -51,7 +60,20 @@ class _MyAppState extends State<MyApp> {
theme: AppTheme.lightTheme(),
darkTheme: AppTheme.darkTheme(),
themeMode: _settingsService.isDarkMode ? ThemeMode.dark : ThemeMode.light,
home: const HomePage(),
home: BlocBuilder<AuthBloc, AuthState>(
builder: (context, state) {
if (state is AuthAuthenticated) {
return const HomePage();
} else {
// Здесь будет страница входа, пока просто заглушка
return const Scaffold(
body: Center(
child: Text('Требуется аутентификация'),
),
);
}
},
),
);
}
}
+40
View File
@@ -33,6 +33,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.13.0"
bloc:
dependency: transitive
description:
name: bloc
sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189"
url: "https://pub.dev"
source: hosted
version: "9.0.0"
boolean_selector:
dependency: transitive
description:
@@ -177,6 +185,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.0"
equatable:
dependency: "direct main"
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async:
dependency: transitive
description:
@@ -214,6 +230,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_bloc:
dependency: "direct main"
description:
name: flutter_bloc
sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38
url: "https://pub.dev"
source: hosted
version: "9.1.1"
flutter_lints:
dependency: "direct dev"
description:
@@ -419,6 +443,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
package_config:
dependency: transitive
description:
@@ -507,6 +539,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
provider:
dependency: transitive
description:
name: provider
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
pub_semver:
dependency: transitive
description:
+3
View File
@@ -40,6 +40,9 @@ dependencies:
path_provider: ^2.0.15
uuid: ^3.0.7
logger: ^2.5.0
flutter_bloc: ^9.1.1
equatable: ^2.0.7
dev_dependencies:
hive_ce_generator: ^1.9.2