1111
This commit is contained in:
@@ -7,6 +7,7 @@ import '../../data/datasources/user_local_data_source.dart';
|
||||
import '../../data/repositories/auth_repository_impl.dart';
|
||||
import '../../domain/repositories/auth_repository.dart';
|
||||
import '../../domain/usecases/login_usecase.dart';
|
||||
import '../../presentation/blocs/login/login_bloc.dart';
|
||||
|
||||
final sl = GetIt.instance;
|
||||
|
||||
@@ -43,7 +44,6 @@ Future<void> initializeDependencies() async {
|
||||
// Use cases
|
||||
sl.registerLazySingleton(() => LoginUseCase(repository: sl()));
|
||||
|
||||
// Blocs will be registered here
|
||||
// Example:
|
||||
// sl.registerFactory(() => LoginBloc(loginUseCase: sl()));
|
||||
// Blocs
|
||||
sl.registerFactory(() => LoginBloc(loginUseCase: sl()));
|
||||
}
|
||||
|
||||
40
lib/presentation/blocs/login/login_bloc.dart
Normal file
40
lib/presentation/blocs/login/login_bloc.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../domain/usecases/login_usecase.dart';
|
||||
import 'login_event.dart';
|
||||
import 'login_state.dart';
|
||||
|
||||
class LoginBloc extends Bloc<LoginEvent, LoginState> {
|
||||
final LoginUseCase loginUseCase;
|
||||
|
||||
LoginBloc({required this.loginUseCase}) : super(const LoginInitial()) {
|
||||
on<LoginSubmitted>(_onLoginSubmitted);
|
||||
on<LoginReset>(_onLoginReset);
|
||||
}
|
||||
|
||||
Future<void> _onLoginSubmitted(
|
||||
LoginSubmitted event,
|
||||
Emitter<LoginState> emit,
|
||||
) async {
|
||||
emit(const LoginLoading());
|
||||
|
||||
final result = await loginUseCase(event.request);
|
||||
|
||||
result.fold(
|
||||
(failure) => emit(LoginError(failure.message)),
|
||||
(response) {
|
||||
if (response.isSuccess) {
|
||||
emit(LoginSuccess(response));
|
||||
} else {
|
||||
emit(LoginError(response.message));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _onLoginReset(
|
||||
LoginReset event,
|
||||
Emitter<LoginState> emit,
|
||||
) {
|
||||
emit(const LoginInitial());
|
||||
}
|
||||
}
|
||||
22
lib/presentation/blocs/login/login_event.dart
Normal file
22
lib/presentation/blocs/login/login_event.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../../domain/models/login_request.dart';
|
||||
|
||||
abstract class LoginEvent extends Equatable {
|
||||
const LoginEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoginSubmitted extends LoginEvent {
|
||||
final LoginRequest request;
|
||||
|
||||
const LoginSubmitted(this.request);
|
||||
|
||||
@override
|
||||
List<Object> get props => [request];
|
||||
}
|
||||
|
||||
class LoginReset extends LoginEvent {
|
||||
const LoginReset();
|
||||
}
|
||||
35
lib/presentation/blocs/login/login_state.dart
Normal file
35
lib/presentation/blocs/login/login_state.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../../domain/models/login_response_model.dart';
|
||||
|
||||
abstract class LoginState extends Equatable {
|
||||
const LoginState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoginInitial extends LoginState {
|
||||
const LoginInitial();
|
||||
}
|
||||
|
||||
class LoginLoading extends LoginState {
|
||||
const LoginLoading();
|
||||
}
|
||||
|
||||
class LoginSuccess extends LoginState {
|
||||
final LoginResponseModel response;
|
||||
|
||||
const LoginSuccess(this.response);
|
||||
|
||||
@override
|
||||
List<Object> get props => [response];
|
||||
}
|
||||
|
||||
class LoginError extends LoginState {
|
||||
final String message;
|
||||
|
||||
const LoginError(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
@@ -1,25 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../widgets/app_background.dart';
|
||||
import '../widgets/auth_form.dart';
|
||||
import '../core/di/injection_container.dart';
|
||||
import '../presentation/blocs/login/login_bloc.dart';
|
||||
|
||||
class AuthScreen extends StatelessWidget {
|
||||
const AuthScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: AppBackground(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 60),
|
||||
// Logo
|
||||
Center(child: Image.asset("assets/images/logo2.png", width: 200)),
|
||||
// const SizedBox(height: 15),
|
||||
// Form - taking remaining space and centered
|
||||
Expanded(child: Center(child: const AuthForm())),
|
||||
],
|
||||
return BlocProvider(
|
||||
create: (context) => sl<LoginBloc>(),
|
||||
child: Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: AppBackground(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 60),
|
||||
// Logo
|
||||
Center(child: Image.asset("assets/images/logo2.png", width: 200)),
|
||||
// const SizedBox(height: 15),
|
||||
// Form - taking remaining space and centered
|
||||
Expanded(child: Center(child: const AuthForm())),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../screens/main_screen.dart';
|
||||
import '../core/di/injection_container.dart';
|
||||
import '../domain/usecases/login_usecase.dart';
|
||||
import '../domain/models/login_request.dart';
|
||||
import '../presentation/blocs/login/login_bloc.dart';
|
||||
import '../presentation/blocs/login/login_event.dart';
|
||||
import '../presentation/blocs/login/login_state.dart';
|
||||
import 'onboarding_button.dart';
|
||||
|
||||
class AuthForm extends StatefulWidget {
|
||||
@@ -16,7 +18,6 @@ class AuthForm extends StatefulWidget {
|
||||
|
||||
class _AuthFormState extends State<AuthForm> {
|
||||
bool _obscure = true;
|
||||
bool _isLoading = false;
|
||||
|
||||
// Text controllers
|
||||
final TextEditingController _phoneNumberController = TextEditingController();
|
||||
@@ -26,10 +27,7 @@ class _AuthFormState extends State<AuthForm> {
|
||||
late FocusNode _phoneNumberFocusNode;
|
||||
late FocusNode _passwordFocusNode;
|
||||
|
||||
// Get LoginUseCase from dependency injection
|
||||
final LoginUseCase _loginUseCase = sl<LoginUseCase>();
|
||||
|
||||
Future<void> _handleLogin() async {
|
||||
void _handleLogin() {
|
||||
// Validate inputs
|
||||
if (_phoneNumberController.text.trim().isEmpty) {
|
||||
_showError('الرجاء إدخال رقم الهاتف');
|
||||
@@ -45,48 +43,13 @@ class _AuthFormState extends State<AuthForm> {
|
||||
_phoneNumberFocusNode.unfocus();
|
||||
_passwordFocusNode.unfocus();
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
// Dispatch login event
|
||||
final request = LoginRequest(
|
||||
phoneNumber: _phoneNumberController.text.trim(),
|
||||
password: _passwordController.text.trim(),
|
||||
);
|
||||
|
||||
try {
|
||||
final request = LoginRequest(
|
||||
phoneNumber: _phoneNumberController.text.trim(),
|
||||
password: _passwordController.text.trim(),
|
||||
);
|
||||
|
||||
final result = await _loginUseCase(request);
|
||||
|
||||
result.fold(
|
||||
(failure) {
|
||||
_showError(failure.message);
|
||||
},
|
||||
(response) {
|
||||
if (response.isSuccess) {
|
||||
// Call the onSubmit callback if provided
|
||||
if (widget.onSubmit != null) {
|
||||
widget.onSubmit!();
|
||||
}
|
||||
|
||||
// Navigate to the MainPage
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MainPage()),
|
||||
);
|
||||
} else {
|
||||
_showError(response.message);
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
_showError('حدث خطأ غير متوقع: $e');
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
context.read<LoginBloc>().add(LoginSubmitted(request));
|
||||
}
|
||||
|
||||
void _showError(String message) {
|
||||
@@ -139,10 +102,27 @@ class _AuthFormState extends State<AuthForm> {
|
||||
final horizontalPadding = screenWidth > 600 ? 30.0 : 25.0;
|
||||
final verticalPadding = screenHeight > 800 ? 38.0 : 28.0;
|
||||
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: FocusScope(
|
||||
child: Stack(
|
||||
return BlocListener<LoginBloc, LoginState>(
|
||||
listener: (context, state) {
|
||||
if (state is LoginSuccess) {
|
||||
// Call the onSubmit callback if provided
|
||||
if (widget.onSubmit != null) {
|
||||
widget.onSubmit!();
|
||||
}
|
||||
|
||||
// Navigate to the MainPage
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const MainPage()),
|
||||
);
|
||||
} else if (state is LoginError) {
|
||||
_showError(state.message);
|
||||
}
|
||||
},
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: FocusScope(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Border container - decorative element behind the form
|
||||
@@ -251,12 +231,17 @@ class _AuthFormState extends State<AuthForm> {
|
||||
|
||||
SizedBox(height: buttonSpacing), // Responsive spacing
|
||||
|
||||
Center(
|
||||
child: OnboardingButton(
|
||||
text: _isLoading ? "جاري تسجيل الدخول..." : "تسجيل دخول",
|
||||
backgroundColor: const Color.fromARGB(239, 35, 87, 74),
|
||||
onPressed: _isLoading ? null : _handleLogin,
|
||||
),
|
||||
BlocBuilder<LoginBloc, LoginState>(
|
||||
builder: (context, state) {
|
||||
final isLoading = state is LoginLoading;
|
||||
return Center(
|
||||
child: OnboardingButton(
|
||||
text: isLoading ? "جاري تسجيل الدخول..." : "تسجيل دخول",
|
||||
backgroundColor: const Color.fromARGB(239, 35, 87, 74),
|
||||
onPressed: isLoading ? null : _handleLogin,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
SizedBox(height: bottomSpacing),
|
||||
@@ -266,6 +251,7 @@ class _AuthFormState extends State<AuthForm> {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
32
pubspec.lock
32
pubspec.lock
@@ -33,6 +33,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -206,6 +214,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -368,6 +384,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"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -440,6 +464,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.3"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -17,6 +17,7 @@ dependencies:
|
||||
dartz: ^0.10.1
|
||||
equatable: ^2.0.5
|
||||
shared_preferences: ^2.2.2
|
||||
flutter_bloc: ^8.1.6
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user