From c512f143cb9f4db668ff85135b8e8a5ccb3ee392 Mon Sep 17 00:00:00 2001 From: Komek Hayytnazarov Date: Tue, 23 Jul 2024 11:37:42 +0500 Subject: [PATCH] added settings --- lib/application/application.dart | 1 + lib/application/user_bloc/user_bloc.dart | 78 ++++++++++ lib/application/user_bloc/user_event.dart | 18 +++ lib/application/user_bloc/user_state.dart | 33 +++++ lib/core/app/app.dart | 21 ++- lib/core/core.dart | 3 + lib/core/networkchecker/network_info.dart | 14 ++ lib/core/usecases/usecases.dart | 13 ++ lib/core/utils/form_validator.dart | 1 + lib/core/utils/utils.dart | 10 ++ lib/data/data.dart | 3 + lib/data/data_sources/data_sources.dart | 2 + lib/data/data_sources/local/local.dart | 1 + .../local/user_local_data_source.dart | 74 +++++++++ lib/data/data_sources/remote/remote.dart | 1 + .../remote/user_remote_data_source.dart | 59 ++++++++ lib/data/models/models.dart | 2 + .../user/authentication_response_model.dart | 28 ++++ lib/data/models/user/user_model.dart | 30 ++++ lib/data/repositories/repositories.dart | 1 + .../repositories/user_repository_impl.dart | 71 +++++++++ lib/di/common.dart | 20 +++ lib/di/di.dart | 23 +++ lib/di/user.dart | 32 ++++ lib/domain/domain.dart | 3 + lib/domain/entities/entities.dart | 1 + lib/domain/entities/user/user.dart | 25 ++++ lib/domain/repositories/repositories.dart | 1 + lib/domain/repositories/user_repository.dart | 11 ++ lib/domain/usecases/usecases.dart | 1 + .../user/get_cached_user_usecase.dart | 14 ++ lib/domain/usecases/user/sign_in_usecase.dart | 23 +++ .../usecases/user/sign_out_usecase.dart | 14 ++ lib/domain/usecases/user/sign_up_usecase.dart | 27 ++++ lib/domain/usecases/user/user.dart | 4 + lib/main.dart | 3 +- lib/presentation/screens/login.dart | 140 +++++++++++------- pubspec.lock | 88 +++++++++++ pubspec.yaml | 1 + 39 files changed, 837 insertions(+), 58 deletions(-) create mode 100644 lib/application/application.dart create mode 100644 lib/application/user_bloc/user_bloc.dart create mode 100644 lib/application/user_bloc/user_event.dart create mode 100644 lib/application/user_bloc/user_state.dart create mode 100644 lib/core/networkchecker/network_info.dart create mode 100644 lib/core/usecases/usecases.dart create mode 100644 lib/core/utils/form_validator.dart create mode 100644 lib/core/utils/utils.dart create mode 100644 lib/data/data.dart create mode 100644 lib/data/data_sources/data_sources.dart create mode 100644 lib/data/data_sources/local/local.dart create mode 100644 lib/data/data_sources/local/user_local_data_source.dart create mode 100644 lib/data/data_sources/remote/remote.dart create mode 100644 lib/data/data_sources/remote/user_remote_data_source.dart create mode 100644 lib/data/models/models.dart create mode 100644 lib/data/models/user/authentication_response_model.dart create mode 100644 lib/data/models/user/user_model.dart create mode 100644 lib/data/repositories/repositories.dart create mode 100644 lib/data/repositories/user_repository_impl.dart create mode 100644 lib/di/common.dart create mode 100644 lib/di/di.dart create mode 100644 lib/di/user.dart create mode 100644 lib/domain/domain.dart create mode 100644 lib/domain/entities/entities.dart create mode 100644 lib/domain/entities/user/user.dart create mode 100644 lib/domain/repositories/repositories.dart create mode 100644 lib/domain/repositories/user_repository.dart create mode 100644 lib/domain/usecases/usecases.dart create mode 100644 lib/domain/usecases/user/get_cached_user_usecase.dart create mode 100644 lib/domain/usecases/user/sign_in_usecase.dart create mode 100644 lib/domain/usecases/user/sign_out_usecase.dart create mode 100644 lib/domain/usecases/user/sign_up_usecase.dart create mode 100644 lib/domain/usecases/user/user.dart diff --git a/lib/application/application.dart b/lib/application/application.dart new file mode 100644 index 0000000..3bbdad3 --- /dev/null +++ b/lib/application/application.dart @@ -0,0 +1 @@ +export 'user_bloc/user_bloc.dart'; diff --git a/lib/application/user_bloc/user_bloc.dart b/lib/application/user_bloc/user_bloc.dart new file mode 100644 index 0000000..ffc5dbd --- /dev/null +++ b/lib/application/user_bloc/user_bloc.dart @@ -0,0 +1,78 @@ +import 'dart:async'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../core/core.dart'; +import '../../domain/domain.dart'; + +part 'user_event.dart'; +part 'user_state.dart'; + +class UserBloc extends Bloc { + final GetCachedUserUseCase _getCachedUserUseCase; + final SignInUseCase _signInUseCase; + final SignUpUseCase _signUpUseCase; + final SignOutUseCase _signOutUseCase; + UserBloc( + this._signInUseCase, + this._getCachedUserUseCase, + this._signOutUseCase, + this._signUpUseCase, + ) : super(UserInitial()) { + on(_onSignIn); + on(_onSignUp); + on(_onCheckUser); + on(_onSignOut); + } + + void _onSignIn(SignInUser event, Emitter emit) async { + try { + emit(UserLoading()); + final result = await _signInUseCase(event.params); + result.fold( + (failure) => emit(UserLoggedFail(failure)), + (user) => emit(UserLogged(user)), + ); + } catch (e) { + emit(UserLoggedFail(ExceptionFailure())); + } + } + + void _onCheckUser(CheckUser event, Emitter emit) async { + try { + emit(UserLoading()); + final result = await _getCachedUserUseCase(NoParams()); + result.fold( + (failure) => emit(UserLoggedFail(failure)), + (user) => emit(UserLogged(user)), + ); + } catch (e) { + emit(UserLoggedFail(ExceptionFailure())); + } + } + + FutureOr _onSignUp(SignUpUser event, Emitter emit) async { + try { + emit(UserLoading()); + final result = await _signUpUseCase(event.params); + result.fold( + (failure) => emit(UserLoggedFail(failure)), + (user) => emit(UserLogged(user)), + ); + } catch (e) { + emit(UserLoggedFail(ExceptionFailure())); + } + } + + void _onSignOut(SignOutUser event, Emitter emit) async { + try { + emit(UserLoading()); + await _signOutUseCase(NoParams()); + emit(UserLoggedOut()); + } catch (e) { + emit(UserLoggedFail(ExceptionFailure())); + } + } +} diff --git a/lib/application/user_bloc/user_event.dart b/lib/application/user_bloc/user_event.dart new file mode 100644 index 0000000..82910ca --- /dev/null +++ b/lib/application/user_bloc/user_event.dart @@ -0,0 +1,18 @@ +part of 'user_bloc.dart'; + +@immutable +abstract class UserEvent {} + +class SignInUser extends UserEvent { + final SignInParams params; + SignInUser(this.params); +} + +class SignUpUser extends UserEvent { + final SignUpParams params; + SignUpUser(this.params); +} + +class SignOutUser extends UserEvent {} + +class CheckUser extends UserEvent {} diff --git a/lib/application/user_bloc/user_state.dart b/lib/application/user_bloc/user_state.dart new file mode 100644 index 0000000..3868f38 --- /dev/null +++ b/lib/application/user_bloc/user_state.dart @@ -0,0 +1,33 @@ +part of 'user_bloc.dart'; + +@immutable +abstract class UserState extends Equatable {} + +class UserInitial extends UserState { + @override + List get props => []; +} + +class UserLoading extends UserState { + @override + List get props => []; +} + +class UserLogged extends UserState { + final User user; + UserLogged(this.user); + @override + List get props => [user]; +} + +class UserLoggedFail extends UserState { + final Failure failure; + UserLoggedFail(this.failure); + @override + List get props => [failure]; +} + +class UserLoggedOut extends UserState { + @override + List get props => []; +} diff --git a/lib/core/app/app.dart b/lib/core/app/app.dart index c1abfef..6177edf 100644 --- a/lib/core/app/app.dart +++ b/lib/core/app/app.dart @@ -1,17 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../application/application.dart'; import '../core.dart'; +import '../../di/di.dart' as di; + class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { - return const MaterialApp( - debugShowCheckedModeBanner: false, - title: appTitle, - onGenerateRoute: AppRouter.onGenerateRoute, - initialRoute: AppRouter.splash, + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => di.sl()..add(CheckUser()), + ), + ], + child: const MaterialApp( + debugShowCheckedModeBanner: false, + title: appTitle, + onGenerateRoute: AppRouter.onGenerateRoute, + initialRoute: AppRouter.splash, + ), ); } } diff --git a/lib/core/core.dart b/lib/core/core.dart index 7051ee5..9c28970 100644 --- a/lib/core/core.dart +++ b/lib/core/core.dart @@ -3,3 +3,6 @@ export 'constants/constants.dart'; export 'errors/errors.dart'; export 'router/router.dart'; export 'observer/observer.dart'; +export 'usecases/usecases.dart'; +export 'networkchecker/network_info.dart'; +export 'utils/utils.dart'; diff --git a/lib/core/networkchecker/network_info.dart b/lib/core/networkchecker/network_info.dart new file mode 100644 index 0000000..d42160c --- /dev/null +++ b/lib/core/networkchecker/network_info.dart @@ -0,0 +1,14 @@ +import 'package:internet_connection_checker/internet_connection_checker.dart'; + +abstract class NetworkInfo { + Future get isConnected; +} + +class NetworkInfoImpl implements NetworkInfo { + final InternetConnectionChecker connectionChecker; + + NetworkInfoImpl(this.connectionChecker); + + @override + Future get isConnected => connectionChecker.hasConnection; +} diff --git a/lib/core/usecases/usecases.dart b/lib/core/usecases/usecases.dart new file mode 100644 index 0000000..c126e04 --- /dev/null +++ b/lib/core/usecases/usecases.dart @@ -0,0 +1,13 @@ +import 'package:dartz/dartz.dart'; +import 'package:equatable/equatable.dart'; + +import '../errors/failures.dart'; + +abstract class UseCase { + Future> call(Params params); +} + +class NoParams extends Equatable { + @override + List get props => []; +} diff --git a/lib/core/utils/form_validator.dart b/lib/core/utils/form_validator.dart new file mode 100644 index 0000000..b9f78bb --- /dev/null +++ b/lib/core/utils/form_validator.dart @@ -0,0 +1 @@ +// TODO Implement this library. \ No newline at end of file diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart new file mode 100644 index 0000000..e8835a0 --- /dev/null +++ b/lib/core/utils/utils.dart @@ -0,0 +1,10 @@ +export 'form_validator.dart'; + +class FormValidator { + static String? validateField(String? val) { + if (val == null || val.isEmpty) { + return 'This field can\'t be empty'; + } + return null; + } +} diff --git a/lib/data/data.dart b/lib/data/data.dart new file mode 100644 index 0000000..0b6f309 --- /dev/null +++ b/lib/data/data.dart @@ -0,0 +1,3 @@ +export 'data_sources/data_sources.dart'; +export 'models/models.dart'; +export 'repositories/repositories.dart'; diff --git a/lib/data/data_sources/data_sources.dart b/lib/data/data_sources/data_sources.dart new file mode 100644 index 0000000..fac0e67 --- /dev/null +++ b/lib/data/data_sources/data_sources.dart @@ -0,0 +1,2 @@ +export 'local/local.dart'; +export 'remote/remote.dart'; diff --git a/lib/data/data_sources/local/local.dart b/lib/data/data_sources/local/local.dart new file mode 100644 index 0000000..b3b36fc --- /dev/null +++ b/lib/data/data_sources/local/local.dart @@ -0,0 +1 @@ +export 'user_local_data_source.dart'; diff --git a/lib/data/data_sources/local/user_local_data_source.dart b/lib/data/data_sources/local/user_local_data_source.dart new file mode 100644 index 0000000..364aa0c --- /dev/null +++ b/lib/data/data_sources/local/user_local_data_source.dart @@ -0,0 +1,74 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../../core/core.dart'; +import '../../models/models.dart'; + +abstract class UserLocalDataSource { + Future getToken(); + + Future getUser(); + + Future saveToken(String token); + + Future saveUser(UserModel user); + + Future clearCache(); + + Future isTokenAvailable(); +} + +const cachedToken = 'TOKEN'; +const cachedUser = 'USER'; + +class UserLocalDataSourceImpl implements UserLocalDataSource { + final FlutterSecureStorage secureStorage; + final SharedPreferences sharedPreferences; + UserLocalDataSourceImpl({required this.sharedPreferences, required this.secureStorage}); + + @override + Future getToken() async { + String? token = await secureStorage.read(key: cachedToken); + return Future.value(token); + } + + @override + Future saveToken(String token) async { + await secureStorage.write(key: cachedToken, value: token); + } + + @override + Future getUser() async { + if (sharedPreferences.getBool('first_run') ?? true) { + await secureStorage.deleteAll(); + sharedPreferences.setBool('first_run', false); + } + final jsonString = sharedPreferences.getString(cachedUser); + if (jsonString != null) { + return Future.value(userModelFromJson(jsonString)); + } else { + throw CacheException(); + } + } + + @override + Future saveUser(UserModel user) { + return sharedPreferences.setString( + cachedUser, + userModelToJson(user), + ); + } + + @override + Future isTokenAvailable() async { + String? token = await secureStorage.read(key: cachedToken); + return Future.value((token != null)); + } + + @override + Future clearCache() async { + await secureStorage.deleteAll(); + // await sharedPreferences.remove(cachedCart); + await sharedPreferences.remove(cachedUser); + } +} diff --git a/lib/data/data_sources/remote/remote.dart b/lib/data/data_sources/remote/remote.dart new file mode 100644 index 0000000..7583f7d --- /dev/null +++ b/lib/data/data_sources/remote/remote.dart @@ -0,0 +1 @@ +export 'user_remote_data_source.dart'; diff --git a/lib/data/data_sources/remote/user_remote_data_source.dart b/lib/data/data_sources/remote/user_remote_data_source.dart new file mode 100644 index 0000000..5128b50 --- /dev/null +++ b/lib/data/data_sources/remote/user_remote_data_source.dart @@ -0,0 +1,59 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; + +import '../../../core/core.dart'; +import '../../../domain/domain.dart'; +import '../../data.dart'; + +abstract class UserRemoteDataSource { + Future signIn(SignInParams params); + Future signUp(SignUpParams params); +} + +class UserRemoteDataSourceImpl implements UserRemoteDataSource { + final http.Client client; + UserRemoteDataSourceImpl({required this.client}); + + @override + Future signIn(SignInParams params) async { + debugPrint('signIn'); + final response = await client.post(Uri.parse('$baseUrl/authentication/local/sign-in'), + headers: { + 'Content-Type': 'application/json', + }, + body: json.encode({ + 'identifier': params.username, + 'password': params.password, + })); + if (response.statusCode == 200) { + return authenticationResponseModelFromJson(response.body); + } else if (response.statusCode == 400 || response.statusCode == 401) { + throw CredentialFailure(); + } else { + throw ServerException(); + } + } + + @override + Future signUp(SignUpParams params) async { + final response = await client.post(Uri.parse('$baseUrl/authentication/local/sign-up'), + headers: { + 'Content-Type': 'application/json', + }, + body: json.encode({ + 'firstName': params.firstName, + 'lastName': params.lastName, + 'email': params.email, + 'password': params.password, + })); + if (response.statusCode == 201) { + return authenticationResponseModelFromJson(response.body); + } else if (response.statusCode == 400 || response.statusCode == 401) { + throw CredentialFailure(); + } else { + throw ServerException(); + } + } +} diff --git a/lib/data/models/models.dart b/lib/data/models/models.dart new file mode 100644 index 0000000..775d767 --- /dev/null +++ b/lib/data/models/models.dart @@ -0,0 +1,2 @@ +export 'user/user_model.dart'; +export 'user/authentication_response_model.dart'; diff --git a/lib/data/models/user/authentication_response_model.dart b/lib/data/models/user/authentication_response_model.dart new file mode 100644 index 0000000..587960a --- /dev/null +++ b/lib/data/models/user/authentication_response_model.dart @@ -0,0 +1,28 @@ +import 'dart:convert'; + +import 'user_model.dart'; + +AuthenticationResponseModel authenticationResponseModelFromJson(String str) => + AuthenticationResponseModel.fromJson(json.decode(str)); + +String authenticationResponseModelToJson(AuthenticationResponseModel data) => json.encode(data.toJson()); + +class AuthenticationResponseModel { + final String token; + final UserModel user; + + const AuthenticationResponseModel({ + required this.token, + required this.user, + }); + + factory AuthenticationResponseModel.fromJson(Map json) => AuthenticationResponseModel( + token: json['token'], + user: UserModel.fromJson(json['user']), + ); + + Map toJson() => { + 'token': token, + 'user': user.toJson(), + }; +} diff --git a/lib/data/models/user/user_model.dart b/lib/data/models/user/user_model.dart new file mode 100644 index 0000000..07db3e2 --- /dev/null +++ b/lib/data/models/user/user_model.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; + +import '../../../domain/entities/user/user.dart'; + +UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str)); + +String userModelToJson(UserModel data) => json.encode(data.toJson()); + +class UserModel extends User { + const UserModel({ + required super.id, + required super.firstName, + required super.lastName, + required super.email, + }); + + factory UserModel.fromJson(Map json) => UserModel( + id: json['_id'], + firstName: json['firstName'], + lastName: json['lastName'], + email: json['email'], + ); + + Map toJson() => { + '_id': id, + 'firstName': firstName, + 'lastName': lastName, + 'email': email, + }; +} diff --git a/lib/data/repositories/repositories.dart b/lib/data/repositories/repositories.dart new file mode 100644 index 0000000..9099d07 --- /dev/null +++ b/lib/data/repositories/repositories.dart @@ -0,0 +1 @@ +export 'user_repository_impl.dart'; diff --git a/lib/data/repositories/user_repository_impl.dart b/lib/data/repositories/user_repository_impl.dart new file mode 100644 index 0000000..d30ab5d --- /dev/null +++ b/lib/data/repositories/user_repository_impl.dart @@ -0,0 +1,71 @@ +import 'package:dartz/dartz.dart'; + +import '../../core/core.dart'; +import '../../domain/domain.dart'; +import '../data_sources/data_sources.dart'; +import '../models/user/authentication_response_model.dart'; + +typedef _DataSourceChooser = Future Function(); + +class UserRepositoryImpl implements UserRepository { + final UserRemoteDataSource remoteDataSource; + final UserLocalDataSource localDataSource; + final NetworkInfo networkInfo; + + UserRepositoryImpl({ + required this.remoteDataSource, + required this.localDataSource, + required this.networkInfo, + }); + + @override + Future> signIn(params) async { + return await _authenticate(() { + return remoteDataSource.signIn(params); + }); + } + + @override + Future> signUp(params) async { + return await _authenticate(() { + return remoteDataSource.signUp(params); + }); + } + + @override + Future> getCachedUser() async { + try { + final user = await localDataSource.getUser(); + return Right(user); + } on CacheFailure { + return Left(CacheFailure()); + } + } + + @override + Future> signOut() async { + try { + await localDataSource.clearCache(); + return Right(NoParams()); + } on CacheFailure { + return Left(CacheFailure()); + } + } + + Future> _authenticate( + _DataSourceChooser getDataSource, + ) async { + if (await networkInfo.isConnected) { + try { + final remoteResponse = await getDataSource(); + localDataSource.saveToken(remoteResponse.token); + localDataSource.saveUser(remoteResponse.user); + return Right(remoteResponse.user); + } on Failure catch (failure) { + return Left(failure); + } + } else { + return Left(NetworkFailure()); + } + } +} diff --git a/lib/di/common.dart b/lib/di/common.dart new file mode 100644 index 0000000..c5ced3f --- /dev/null +++ b/lib/di/common.dart @@ -0,0 +1,20 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:internet_connection_checker/internet_connection_checker.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart' as http; + +import '../core/networkchecker/network_info.dart'; +import 'di.dart'; + +void registerCommonDependencies() async { + final sharedPreferences = await SharedPreferences.getInstance(); + const secureStorage = FlutterSecureStorage(); + + sl.registerLazySingleton(() => sharedPreferences); + sl.registerLazySingleton(() => secureStorage); + sl.registerLazySingleton(() => http.Client()); + sl.registerLazySingleton(() => InternetConnectionChecker()); + + // Register NetworkInfo + sl.registerLazySingleton(() => NetworkInfoImpl(sl())); +} diff --git a/lib/di/di.dart b/lib/di/di.dart new file mode 100644 index 0000000..91aaf2a --- /dev/null +++ b/lib/di/di.dart @@ -0,0 +1,23 @@ +import 'package:get_it/get_it.dart'; + +import 'common.dart'; +import 'user.dart'; + +final sl = GetIt.instance; + +// Main Initialization +Future init() async { + // Register features + registerUserFeature(); + // registerCategoryFeature(); + // registerProductFeature(); + // registerDeliveryInfoFeature(); + // registerCartFeature(); + // registerOrderFeature(); + + // Register Cubits + // registerCubits(); + + // Register common dependencies + registerCommonDependencies(); +} diff --git a/lib/di/user.dart b/lib/di/user.dart new file mode 100644 index 0000000..04ba987 --- /dev/null +++ b/lib/di/user.dart @@ -0,0 +1,32 @@ +// Feature: User + +import '../application/user_bloc/user_bloc.dart'; +import '../data/data.dart'; +import '../domain/domain.dart'; +import 'di.dart'; + +void registerUserFeature() { + // User BLoC and Use Cases + sl.registerFactory(() => UserBloc(sl(), sl(), sl(), sl())); + sl.registerLazySingleton(() => GetCachedUserUseCase(sl())); + sl.registerLazySingleton(() => SignInUseCase(sl())); + sl.registerLazySingleton(() => SignUpUseCase(sl())); + sl.registerLazySingleton(() => SignOutUseCase(sl())); + + // User Repository and Data Sources + sl.registerLazySingleton( + () => UserRepositoryImpl( + remoteDataSource: sl(), + localDataSource: sl(), + networkInfo: sl(), + ), + ); + + sl.registerLazySingleton( + () => UserLocalDataSourceImpl(sharedPreferences: sl(), secureStorage: sl()), + ); + + sl.registerLazySingleton( + () => UserRemoteDataSourceImpl(client: sl()), + ); +} diff --git a/lib/domain/domain.dart b/lib/domain/domain.dart new file mode 100644 index 0000000..ff77d1c --- /dev/null +++ b/lib/domain/domain.dart @@ -0,0 +1,3 @@ +export 'entities/entities.dart'; +export 'repositories/repositories.dart'; +export 'usecases/usecases.dart'; diff --git a/lib/domain/entities/entities.dart b/lib/domain/entities/entities.dart new file mode 100644 index 0000000..3c543ce --- /dev/null +++ b/lib/domain/entities/entities.dart @@ -0,0 +1 @@ +export 'user/user.dart'; diff --git a/lib/domain/entities/user/user.dart b/lib/domain/entities/user/user.dart new file mode 100644 index 0000000..9660f48 --- /dev/null +++ b/lib/domain/entities/user/user.dart @@ -0,0 +1,25 @@ +import 'package:equatable/equatable.dart'; + +class User extends Equatable { + final String id; + final String firstName; + final String lastName; + final String? image; + final String email; + + const User({ + required this.id, + required this.firstName, + required this.lastName, + this.image, + required this.email, + }); + + @override + List get props => [ + id, + firstName, + lastName, + email, + ]; +} diff --git a/lib/domain/repositories/repositories.dart b/lib/domain/repositories/repositories.dart new file mode 100644 index 0000000..719ec60 --- /dev/null +++ b/lib/domain/repositories/repositories.dart @@ -0,0 +1 @@ +export 'user_repository.dart'; diff --git a/lib/domain/repositories/user_repository.dart b/lib/domain/repositories/user_repository.dart new file mode 100644 index 0000000..8fe325f --- /dev/null +++ b/lib/domain/repositories/user_repository.dart @@ -0,0 +1,11 @@ +import 'package:dartz/dartz.dart'; + +import '../../core/core.dart'; +import '../domain.dart'; + +abstract class UserRepository { + Future> signIn(SignInParams params); + Future> signUp(SignUpParams params); + Future> signOut(); + Future> getCachedUser(); +} diff --git a/lib/domain/usecases/usecases.dart b/lib/domain/usecases/usecases.dart new file mode 100644 index 0000000..3c543ce --- /dev/null +++ b/lib/domain/usecases/usecases.dart @@ -0,0 +1 @@ +export 'user/user.dart'; diff --git a/lib/domain/usecases/user/get_cached_user_usecase.dart b/lib/domain/usecases/user/get_cached_user_usecase.dart new file mode 100644 index 0000000..e200e4b --- /dev/null +++ b/lib/domain/usecases/user/get_cached_user_usecase.dart @@ -0,0 +1,14 @@ +import 'package:dartz/dartz.dart'; + +import '../../../core/core.dart'; +import '../../domain.dart'; + +class GetCachedUserUseCase implements UseCase { + final UserRepository repository; + GetCachedUserUseCase(this.repository); + + @override + Future> call(NoParams params) async { + return await repository.getCachedUser(); + } +} diff --git a/lib/domain/usecases/user/sign_in_usecase.dart b/lib/domain/usecases/user/sign_in_usecase.dart new file mode 100644 index 0000000..c7387e6 --- /dev/null +++ b/lib/domain/usecases/user/sign_in_usecase.dart @@ -0,0 +1,23 @@ +import 'package:dartz/dartz.dart'; + +import '../../../core/core.dart'; +import '../../domain.dart'; + +class SignInUseCase implements UseCase { + final UserRepository repository; + SignInUseCase(this.repository); + + @override + Future> call(SignInParams params) async { + return await repository.signIn(params); + } +} + +class SignInParams { + final String username; + final String password; + const SignInParams({ + required this.username, + required this.password, + }); +} diff --git a/lib/domain/usecases/user/sign_out_usecase.dart b/lib/domain/usecases/user/sign_out_usecase.dart new file mode 100644 index 0000000..891b1a7 --- /dev/null +++ b/lib/domain/usecases/user/sign_out_usecase.dart @@ -0,0 +1,14 @@ +import 'package:dartz/dartz.dart'; + +import '../../../core/core.dart'; +import '../../domain.dart'; + +class SignOutUseCase implements UseCase { + final UserRepository repository; + SignOutUseCase(this.repository); + + @override + Future> call(NoParams params) async { + return await repository.signOut(); + } +} diff --git a/lib/domain/usecases/user/sign_up_usecase.dart b/lib/domain/usecases/user/sign_up_usecase.dart new file mode 100644 index 0000000..f247a93 --- /dev/null +++ b/lib/domain/usecases/user/sign_up_usecase.dart @@ -0,0 +1,27 @@ +import 'package:dartz/dartz.dart'; + +import '../../../core/core.dart'; +import '../../domain.dart'; + +class SignUpUseCase implements UseCase { + final UserRepository repository; + SignUpUseCase(this.repository); + + @override + Future> call(SignUpParams params) async { + return await repository.signUp(params); + } +} + +class SignUpParams { + final String firstName; + final String lastName; + final String email; + final String password; + const SignUpParams({ + required this.firstName, + required this.lastName, + required this.email, + required this.password, + }); +} diff --git a/lib/domain/usecases/user/user.dart b/lib/domain/usecases/user/user.dart new file mode 100644 index 0000000..c74d27d --- /dev/null +++ b/lib/domain/usecases/user/user.dart @@ -0,0 +1,4 @@ +export 'get_cached_user_usecase.dart'; +export 'sign_in_usecase.dart'; +export 'sign_out_usecase.dart'; +export 'sign_up_usecase.dart'; diff --git a/lib/main.dart b/lib/main.dart index 39fe459..4502274 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,13 +4,14 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_phoenix/flutter_phoenix.dart'; import 'core/core.dart'; +import 'di/di.dart' as di; Future main() async { WidgetsFlutterBinding.ensureInitialized(); // await GetStorage.init(); - // await di.init(); + await di.init(); Bloc.observer = MyBlocObserver(); diff --git a/lib/presentation/screens/login.dart b/lib/presentation/screens/login.dart index af6ef3b..c1e901a 100644 --- a/lib/presentation/screens/login.dart +++ b/lib/presentation/screens/login.dart @@ -1,14 +1,33 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import '../../application/user_bloc/user_bloc.dart'; import '../../configs/app.dart'; import '../../configs/configs.dart'; import '../../core/core.dart'; +import '../../domain/domain.dart'; import '../widgets/button.dart'; -class LoginScreen extends StatelessWidget { +class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State { + final TextEditingController _userNameController = TextEditingController(); + final TextEditingController _passwordController = TextEditingController(); + final _formKey = GlobalKey(); + + @override + void dispose() { + _userNameController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { App.init(context); @@ -82,68 +101,85 @@ class LoginScreen extends StatelessWidget { ), child: Padding( padding: Space.hf().copyWith(top: 30), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Şahsy otaga giriş', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Şahsy otaga giriş', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), ), - ), - const Text( - 'özüňize berlen logini we açar sözi giriziň', - style: TextStyle( - color: Colors.grey, + const Text( + 'özüňize berlen logini we açar sözi giriziň', + style: TextStyle( + color: Colors.grey, + ), ), - ), - /// gap - Space.yf(), + /// gap + Space.yf(), - /// username field - const Text('Login'), - Space.yf(0.30), - const TextField( - decoration: InputDecoration( - hintText: 'Öz loginiňizi ýazyň', - prefixIcon: Icon(Icons.person_outline), - // suffixIcon: Icon(Icons.visibility_off), - border: OutlineInputBorder(), + /// username field + const Text('Login'), + Space.y!, + TextFormField( + controller: _userNameController, + decoration: const InputDecoration( + hintText: 'Öz loginiňizi ýazyň', + prefixIcon: Icon(Icons.person_outline), + border: OutlineInputBorder(), + ), + validator: (val) => FormValidator.validateField(val), ), - ), - /// gap - Space.yf(), + /// gap + Space.yf(), - /// password field - const Text('Açar sözi'), - Space.yf(0.30), - const TextField( - obscureText: true, - decoration: InputDecoration( - hintText: 'Öz açar sözüňi ýazyň', - prefixIcon: Icon(Icons.lock_outline), - suffixIcon: Icon(Icons.visibility_off), - border: OutlineInputBorder(), + /// password field + const Text('Açar sözi'), + Space.y!, + TextFormField( + controller: _passwordController, + obscureText: true, + decoration: const InputDecoration( + hintText: 'Öz açar sözüňi ýazyň', + prefixIcon: Icon(Icons.lock_outline), + suffixIcon: Icon(Icons.visibility_off), + border: OutlineInputBorder(), + ), + validator: (val) => FormValidator.validateField(val), ), - ), - /// gap - Space.yf(), + /// gap + Space.yf(), - SizedBox( - width: double.infinity, - child: AppButton( - textColor: AppColors.primary, - btnColor: AppColors.yellow, - onPressed: () {}, - text: 'Yzarlap başlaň', - ), - ) - ], + SizedBox( + width: double.infinity, + child: AppButton( + textColor: AppColors.primary, + btnColor: AppColors.yellow, + onPressed: () { + if (_formKey.currentState!.validate()) { + context.read().add( + SignInUser( + SignInParams( + username: _userNameController.text, + password: _passwordController.text, + ), + ), + ); + } + }, + text: 'Yzarlap başlaň', + ), + ) + ], + ), ), ), ), diff --git a/pubspec.lock b/pubspec.lock index 67f42ab..91dd2f1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -134,6 +134,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_svg: dependency: "direct main" description: @@ -184,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0+1" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" leak_tracker: dependency: transitive description: @@ -264,6 +320,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "30c5aa827a6ae95ce2853cdc5fe3971daaac00f6f081c419c013f7f57bff2f5e" + url: "https://pub.dev" + source: hosted + version: "2.2.7" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -485,6 +565,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" + source: hosted + version: "5.5.1" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e45e1ae..c9d0641 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: internet_connection_checker: ^1.0.0+1 flutter_phoenix: ^1.1.1 flutter_svg: ^2.0.10+1 + flutter_secure_storage: ^9.2.2 dev_dependencies: flutter_test: