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 '../../data/repositories/auth_repository_impl.dart';
|
||||||
import '../../domain/repositories/auth_repository.dart';
|
import '../../domain/repositories/auth_repository.dart';
|
||||||
import '../../domain/usecases/login_usecase.dart';
|
import '../../domain/usecases/login_usecase.dart';
|
||||||
|
import '../../presentation/blocs/login/login_bloc.dart';
|
||||||
|
|
||||||
final sl = GetIt.instance;
|
final sl = GetIt.instance;
|
||||||
|
|
||||||
@@ -43,7 +44,6 @@ Future<void> initializeDependencies() async {
|
|||||||
// Use cases
|
// Use cases
|
||||||
sl.registerLazySingleton(() => LoginUseCase(repository: sl()));
|
sl.registerLazySingleton(() => LoginUseCase(repository: sl()));
|
||||||
|
|
||||||
// Blocs will be registered here
|
// Blocs
|
||||||
// Example:
|
sl.registerFactory(() => LoginBloc(loginUseCase: sl()));
|
||||||
// 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,13 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../widgets/app_background.dart';
|
import '../widgets/app_background.dart';
|
||||||
import '../widgets/auth_form.dart';
|
import '../widgets/auth_form.dart';
|
||||||
|
import '../core/di/injection_container.dart';
|
||||||
|
import '../presentation/blocs/login/login_bloc.dart';
|
||||||
|
|
||||||
class AuthScreen extends StatelessWidget {
|
class AuthScreen extends StatelessWidget {
|
||||||
const AuthScreen({super.key});
|
const AuthScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return BlocProvider(
|
||||||
|
create: (context) => sl<LoginBloc>(),
|
||||||
|
child: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
body: AppBackground(
|
body: AppBackground(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
@@ -23,6 +28,7 @@ class AuthScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import '../screens/main_screen.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 '../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';
|
import 'onboarding_button.dart';
|
||||||
|
|
||||||
class AuthForm extends StatefulWidget {
|
class AuthForm extends StatefulWidget {
|
||||||
@@ -16,7 +18,6 @@ class AuthForm extends StatefulWidget {
|
|||||||
|
|
||||||
class _AuthFormState extends State<AuthForm> {
|
class _AuthFormState extends State<AuthForm> {
|
||||||
bool _obscure = true;
|
bool _obscure = true;
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
// Text controllers
|
// Text controllers
|
||||||
final TextEditingController _phoneNumberController = TextEditingController();
|
final TextEditingController _phoneNumberController = TextEditingController();
|
||||||
@@ -26,10 +27,7 @@ class _AuthFormState extends State<AuthForm> {
|
|||||||
late FocusNode _phoneNumberFocusNode;
|
late FocusNode _phoneNumberFocusNode;
|
||||||
late FocusNode _passwordFocusNode;
|
late FocusNode _passwordFocusNode;
|
||||||
|
|
||||||
// Get LoginUseCase from dependency injection
|
void _handleLogin() {
|
||||||
final LoginUseCase _loginUseCase = sl<LoginUseCase>();
|
|
||||||
|
|
||||||
Future<void> _handleLogin() async {
|
|
||||||
// Validate inputs
|
// Validate inputs
|
||||||
if (_phoneNumberController.text.trim().isEmpty) {
|
if (_phoneNumberController.text.trim().isEmpty) {
|
||||||
_showError('الرجاء إدخال رقم الهاتف');
|
_showError('الرجاء إدخال رقم الهاتف');
|
||||||
@@ -45,48 +43,13 @@ class _AuthFormState extends State<AuthForm> {
|
|||||||
_phoneNumberFocusNode.unfocus();
|
_phoneNumberFocusNode.unfocus();
|
||||||
_passwordFocusNode.unfocus();
|
_passwordFocusNode.unfocus();
|
||||||
|
|
||||||
setState(() {
|
// Dispatch login event
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
final request = LoginRequest(
|
final request = LoginRequest(
|
||||||
phoneNumber: _phoneNumberController.text.trim(),
|
phoneNumber: _phoneNumberController.text.trim(),
|
||||||
password: _passwordController.text.trim(),
|
password: _passwordController.text.trim(),
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await _loginUseCase(request);
|
context.read<LoginBloc>().add(LoginSubmitted(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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showError(String message) {
|
void _showError(String message) {
|
||||||
@@ -139,7 +102,24 @@ class _AuthFormState extends State<AuthForm> {
|
|||||||
final horizontalPadding = screenWidth > 600 ? 30.0 : 25.0;
|
final horizontalPadding = screenWidth > 600 ? 30.0 : 25.0;
|
||||||
final verticalPadding = screenHeight > 800 ? 38.0 : 28.0;
|
final verticalPadding = screenHeight > 800 ? 38.0 : 28.0;
|
||||||
|
|
||||||
return Directionality(
|
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,
|
textDirection: TextDirection.rtl,
|
||||||
child: FocusScope(
|
child: FocusScope(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -251,12 +231,17 @@ class _AuthFormState extends State<AuthForm> {
|
|||||||
|
|
||||||
SizedBox(height: buttonSpacing), // Responsive spacing
|
SizedBox(height: buttonSpacing), // Responsive spacing
|
||||||
|
|
||||||
Center(
|
BlocBuilder<LoginBloc, LoginState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final isLoading = state is LoginLoading;
|
||||||
|
return Center(
|
||||||
child: OnboardingButton(
|
child: OnboardingButton(
|
||||||
text: _isLoading ? "جاري تسجيل الدخول..." : "تسجيل دخول",
|
text: isLoading ? "جاري تسجيل الدخول..." : "تسجيل دخول",
|
||||||
backgroundColor: const Color.fromARGB(239, 35, 87, 74),
|
backgroundColor: const Color.fromARGB(239, 35, 87, 74),
|
||||||
onPressed: _isLoading ? null : _handleLogin,
|
onPressed: isLoading ? null : _handleLogin,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
SizedBox(height: bottomSpacing),
|
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"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.0"
|
version: "2.13.0"
|
||||||
|
bloc:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bloc
|
||||||
|
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.1.4"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -206,6 +214,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -368,6 +384,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -440,6 +464,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
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:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ dependencies:
|
|||||||
dartz: ^0.10.1
|
dartz: ^0.10.1
|
||||||
equatable: ^2.0.5
|
equatable: ^2.0.5
|
||||||
shared_preferences: ^2.2.2
|
shared_preferences: ^2.2.2
|
||||||
|
flutter_bloc: ^8.1.6
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user