diff --git a/assets/images/logo.png b/assets/images/logo.png index 2cc752e..f8d7196 100644 Binary files a/assets/images/logo.png and b/assets/images/logo.png differ diff --git a/assets/images/logo_dark.png b/assets/images/logo_dark.png index 23c18ee..cf40c07 100644 Binary files a/assets/images/logo_dark.png and b/assets/images/logo_dark.png differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a1618e5..0e6f6ef 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,5 +1,7 @@ PODS: - Flutter (1.0.0) + - flutter_secure_storage (6.0.0): + - Flutter - fluttertoast (0.0.2): - Flutter - Toast @@ -25,6 +27,7 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) @@ -41,6 +44,8 @@ SPEC REPOS: EXTERNAL SOURCES: Flutter: :path: Flutter + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" objectbox_flutter_libs: @@ -56,6 +61,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a ObjectBox: a7900d5335218cd437cbc080b7ccc38a5211f7b4 diff --git a/lib/bloc/authentication/login/login_bloc.dart b/lib/bloc/authentication/login/login_bloc.dart new file mode 100644 index 0000000..f1e9d58 --- /dev/null +++ b/lib/bloc/authentication/login/login_bloc.dart @@ -0,0 +1,28 @@ +import 'package:bloc/bloc.dart'; +import 'package:mobdr/model/integration/login_model.dart'; +import 'package:mobdr/network/api_provider.dart'; +import 'package:meta/meta.dart'; + +part 'login_event.dart'; +part 'login_state.dart'; + +class LoginBloc extends Bloc { + LoginBloc() : super(LoginInitial()) { + on(_login); + } +} + +void _login(Login event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + emit(LoginWaiting()); + try { + List data = + await _apiProvider.login2(event.email, event.password, event.apiToken); + emit(LoginSuccess(loginData: data)); + } catch (ex) { + if (ex != 'cancel') { + emit(LoginError(errorMessage: ex.toString())); + } + } +} diff --git a/lib/bloc/authentication/login/login_event.dart b/lib/bloc/authentication/login/login_event.dart new file mode 100644 index 0000000..33bf630 --- /dev/null +++ b/lib/bloc/authentication/login/login_event.dart @@ -0,0 +1,11 @@ +part of 'login_bloc.dart'; + +@immutable +abstract class LoginEvent {} + +class Login extends LoginEvent { + final String email; + final String password; + final apiToken; + Login({required this.email, required this.password, required this.apiToken}); +} \ No newline at end of file diff --git a/lib/bloc/authentication/login/login_state.dart b/lib/bloc/authentication/login/login_state.dart new file mode 100644 index 0000000..5fb56a5 --- /dev/null +++ b/lib/bloc/authentication/login/login_state.dart @@ -0,0 +1,23 @@ +part of 'login_bloc.dart'; + +@immutable +abstract class LoginState {} + +class LoginInitial extends LoginState {} + +class InitialLoginState extends LoginState {} + +class LoginError extends LoginState { + final String errorMessage; + + LoginError({ + required this.errorMessage, + }); +} + +class LoginWaiting extends LoginState {} + +class LoginSuccess extends LoginState { + final List loginData; + LoginSuccess({required this.loginData}); +} \ No newline at end of file diff --git a/lib/bloc/example/bloc.dart b/lib/bloc/example/bloc.dart new file mode 100644 index 0000000..fdf7254 --- /dev/null +++ b/lib/bloc/example/bloc.dart @@ -0,0 +1,3 @@ +export 'example_bloc.dart'; +export 'example_event.dart'; +export 'example_state.dart'; \ No newline at end of file diff --git a/lib/bloc/example/example_bloc.dart b/lib/bloc/example/example_bloc.dart new file mode 100644 index 0000000..8b8854c --- /dev/null +++ b/lib/bloc/example/example_bloc.dart @@ -0,0 +1,38 @@ +import 'package:bloc/bloc.dart'; +import 'package:mobdr/network/api_provider.dart'; +import './bloc.dart'; + +class ExampleBloc extends Bloc { + ExampleBloc() : super(InitialExampleState()) { + on(_getExample); + on(_postExample); + } +} + +void _getExample(GetExample event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + emit(ExampleWaiting()); + try { + String data = await _apiProvider.getExample(event.apiToken); + emit(GetExampleSuccess(exampleData: data)); + } catch (ex) { + if (ex != 'cancel') { + emit(ExampleError(errorMessage: ex.toString())); + } + } +} + +void _postExample(PostExample event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + emit(ExampleWaiting()); + try { + String data = await _apiProvider.postExample(event.id, event.apiToken); + emit(PostExampleSuccess(exampleData: data)); + } catch (ex) { + if (ex != 'cancel') { + emit(ExampleError(errorMessage: ex.toString())); + } + } +} diff --git a/lib/bloc/example/example_event.dart b/lib/bloc/example/example_event.dart new file mode 100644 index 0000000..8b16fc3 --- /dev/null +++ b/lib/bloc/example/example_event.dart @@ -0,0 +1,15 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class ExampleEvent {} + +class GetExample extends ExampleEvent { + final apiToken; + GetExample({@required this.apiToken}); +} + +class PostExample extends ExampleEvent { + final String id; + final apiToken; + PostExample({required this.id, required this.apiToken}); +} \ No newline at end of file diff --git a/lib/bloc/example/example_state.dart b/lib/bloc/example/example_state.dart new file mode 100644 index 0000000..9cbcc74 --- /dev/null +++ b/lib/bloc/example/example_state.dart @@ -0,0 +1,26 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class ExampleState {} + +class InitialExampleState extends ExampleState {} + +class ExampleError extends ExampleState { + final String errorMessage; + + ExampleError({ + required this.errorMessage, + }); +} + +class ExampleWaiting extends ExampleState {} + +class GetExampleSuccess extends ExampleState { + final String exampleData; + GetExampleSuccess({required this.exampleData}); +} + +class PostExampleSuccess extends ExampleState { + final String exampleData; + PostExampleSuccess({required this.exampleData}); +} \ No newline at end of file diff --git a/lib/bloc/product_grid/bloc.dart b/lib/bloc/product_grid/bloc.dart new file mode 100644 index 0000000..bfae681 --- /dev/null +++ b/lib/bloc/product_grid/bloc.dart @@ -0,0 +1,3 @@ +export 'product_grid_bloc.dart'; +export 'product_grid_event.dart'; +export 'product_grid_state.dart'; \ No newline at end of file diff --git a/lib/bloc/product_grid/product_grid_bloc.dart b/lib/bloc/product_grid/product_grid_bloc.dart new file mode 100644 index 0000000..cd5eb0f --- /dev/null +++ b/lib/bloc/product_grid/product_grid_bloc.dart @@ -0,0 +1,26 @@ +import 'package:bloc/bloc.dart'; +import 'package:mobdr/model/integration/product_grid_model.dart'; +import 'package:mobdr/network/api_provider.dart'; +import './bloc.dart'; + +class ProductGridBloc extends Bloc { + ProductGridBloc() : super(InitialProductGridState()) { + on(_getProductGrid); + } +} + +void _getProductGrid( + GetProductGrid event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + emit(ProductGridWaiting()); + try { + List data = await _apiProvider.getProductGrid( + event.sessionId, event.skip, event.limit, event.apiToken); + emit(GetProductGridSuccess(productGridData: data)); + } catch (ex) { + if (ex != 'cancel') { + emit(ProductGridError(errorMessage: ex.toString())); + } + } +} diff --git a/lib/bloc/product_grid/product_grid_event.dart b/lib/bloc/product_grid/product_grid_event.dart new file mode 100644 index 0000000..005de82 --- /dev/null +++ b/lib/bloc/product_grid/product_grid_event.dart @@ -0,0 +1,10 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class ProductGridEvent {} + +class GetProductGrid extends ProductGridEvent { + final String sessionId, skip, limit; + final apiToken; + GetProductGrid({required this.sessionId, required this.skip, required this.limit, @required this.apiToken}); +} \ No newline at end of file diff --git a/lib/bloc/product_grid/product_grid_state.dart b/lib/bloc/product_grid/product_grid_state.dart new file mode 100644 index 0000000..961d233 --- /dev/null +++ b/lib/bloc/product_grid/product_grid_state.dart @@ -0,0 +1,22 @@ +import 'package:mobdr/model/integration/product_grid_model.dart'; +import 'package:meta/meta.dart'; + +@immutable +abstract class ProductGridState {} + +class InitialProductGridState extends ProductGridState {} + +class ProductGridError extends ProductGridState { + final String errorMessage; + + ProductGridError({ + required this.errorMessage, + }); +} + +class ProductGridWaiting extends ProductGridState {} + +class GetProductGridSuccess extends ProductGridState { + final List productGridData; + GetProductGridSuccess({required this.productGridData}); +} diff --git a/lib/bloc/product_listview/bloc.dart b/lib/bloc/product_listview/bloc.dart new file mode 100644 index 0000000..4f3f564 --- /dev/null +++ b/lib/bloc/product_listview/bloc.dart @@ -0,0 +1,3 @@ +export 'product_listview_bloc.dart'; +export 'product_listview_event.dart'; +export 'product_listview_state.dart'; \ No newline at end of file diff --git a/lib/bloc/product_listview/product_listview_bloc.dart b/lib/bloc/product_listview/product_listview_bloc.dart new file mode 100644 index 0000000..232c94b --- /dev/null +++ b/lib/bloc/product_listview/product_listview_bloc.dart @@ -0,0 +1,27 @@ +import 'package:bloc/bloc.dart'; +import 'package:mobdr/model/integration/product_listview_model.dart'; +import 'package:mobdr/network/api_provider.dart'; +import './bloc.dart'; + +class ProductListviewBloc + extends Bloc { + ProductListviewBloc() : super(InitialProductListviewState()) { + on(_getProductListview); + } +} + +void _getProductListview( + GetProductListview event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + emit(ProductListviewWaiting()); + try { + List data = await _apiProvider.getProductListview( + event.sessionId, event.skip, event.limit, event.apiToken); + emit(GetProductListviewSuccess(productListviewData: data)); + } catch (ex) { + if (ex != 'cancel') { + emit(ProductListviewError(errorMessage: ex.toString())); + } + } +} diff --git a/lib/bloc/product_listview/product_listview_event.dart b/lib/bloc/product_listview/product_listview_event.dart new file mode 100644 index 0000000..e15ec76 --- /dev/null +++ b/lib/bloc/product_listview/product_listview_event.dart @@ -0,0 +1,10 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class ProductListviewEvent {} + +class GetProductListview extends ProductListviewEvent { + final String sessionId, skip, limit; + final apiToken; + GetProductListview({required this.sessionId, required this.skip, required this.limit, required this.apiToken}); +} \ No newline at end of file diff --git a/lib/bloc/product_listview/product_listview_state.dart b/lib/bloc/product_listview/product_listview_state.dart new file mode 100644 index 0000000..fc24d42 --- /dev/null +++ b/lib/bloc/product_listview/product_listview_state.dart @@ -0,0 +1,22 @@ +import 'package:mobdr/model/integration/product_listview_model.dart'; +import 'package:meta/meta.dart'; + +@immutable +abstract class ProductListviewState {} + +class InitialProductListviewState extends ProductListviewState {} + +class ProductListviewError extends ProductListviewState { + final String errorMessage; + + ProductListviewError({ + required this.errorMessage, + }); +} + +class ProductListviewWaiting extends ProductListviewState {} + +class GetProductListviewSuccess extends ProductListviewState { + final List productListviewData; + GetProductListviewSuccess({required this.productListviewData}); +} diff --git a/lib/bloc/student/bloc.dart b/lib/bloc/student/bloc.dart new file mode 100644 index 0000000..7840f88 --- /dev/null +++ b/lib/bloc/student/bloc.dart @@ -0,0 +1,3 @@ +export 'student_bloc.dart'; +export 'student_event.dart'; +export 'student_state.dart'; \ No newline at end of file diff --git a/lib/bloc/student/student_bloc.dart b/lib/bloc/student/student_bloc.dart new file mode 100644 index 0000000..bbb94b6 --- /dev/null +++ b/lib/bloc/student/student_bloc.dart @@ -0,0 +1,121 @@ +import 'package:bloc/bloc.dart'; +import 'package:mobdr/model/integration/student_model.dart'; +import 'package:mobdr/network/api_provider.dart'; +import './bloc.dart'; + +class StudentBloc extends Bloc { + StudentBloc() : super(InitialStudentState()) { + on(_getStudent); + on(_addStudent); + on(_editStudent); + on(_deleteStudent); + } +} + +void _getStudent(GetStudent event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + emit(GetStudentWaiting()); + try { + List data = + await _apiProvider.getStudent(event.sessionId, event.apiToken); + emit(GetStudentSuccess(studentData: data)); + } catch (ex) { + if (ex != 'cancel') { + emit(GetStudentError(errorMessage: ex.toString())); + } + } +} + +void _addStudent(AddStudent event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + String errorMessage = ''; + if (event.studentName == '') { + errorMessage = 'Student name cannot be empty'; + } else if (event.studentPhoneNumber == '') { + errorMessage = 'Student phone number can not be empty'; + } else if (event.studentGender == '') { + errorMessage = 'Student gender can not be empty'; + } else if (event.studentAddress == '') { + errorMessage = 'Student address can not be empty'; + } + + if (errorMessage == '') { + emit(AddStudentWaiting()); + try { + List data = await _apiProvider.addStudent( + event.sessionId, + event.studentName, + event.studentPhoneNumber, + event.studentGender, + event.studentAddress, + event.apiToken); + emit(AddStudentSuccess( + msg: data[0], + studentId: data[1], + studentName: event.studentName, + studentPhoneNumber: event.studentPhoneNumber, + studentGender: event.studentGender, + studentAddress: event.studentAddress)); + } catch (ex) { + emit(AddStudentError(errorMessage: ex.toString())); + } + } else { + emit(StudentErrorValidation(errorMessage: errorMessage)); + } +} + +void _editStudent(EditStudent event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + String errorMessage = ''; + if (event.studentName == '') { + errorMessage = 'Student name cannot be empty'; + } else if (event.studentPhoneNumber == '') { + errorMessage = 'Student phone number can not be empty'; + } else if (event.studentGender == '') { + errorMessage = 'Student gender can not be empty'; + } else if (event.studentAddress == '') { + errorMessage = 'Student address can not be empty'; + } + + if (errorMessage == '') { + emit(EditStudentWaiting()); + try { + String message = await _apiProvider.editStudent( + event.sessionId, + event.studentId, + event.studentName, + event.studentPhoneNumber, + event.studentGender, + event.studentAddress, + event.apiToken); + emit(EditStudentSuccess( + msg: message, + index: event.index, + studentId: event.studentId, + studentName: event.studentName, + studentPhoneNumber: event.studentPhoneNumber, + studentGender: event.studentGender, + studentAddress: event.studentAddress)); + } catch (ex) { + emit(EditStudentError(errorMessage: ex.toString())); + } + } else { + emit(StudentErrorValidation(errorMessage: errorMessage)); + } +} + +void _deleteStudent(DeleteStudent event, Emitter emit) async { + ApiProvider _apiProvider = ApiProvider(); + + emit(DeleteStudentWaiting()); + try { + String msg = await _apiProvider.deleteStudent( + event.sessionId, event.studentId, event.apiToken); + emit(DeleteStudentSuccess(msg: msg, index: event.index)); + } catch (ex) { + emit(DeleteStudentError(errorMessage: ex.toString())); + } +} diff --git a/lib/bloc/student/student_event.dart b/lib/bloc/student/student_event.dart new file mode 100644 index 0000000..dfd5ac3 --- /dev/null +++ b/lib/bloc/student/student_event.dart @@ -0,0 +1,32 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class StudentEvent {} + +class GetStudent extends StudentEvent { + final String sessionId; + final apiToken; + GetStudent({required this.sessionId, required this.apiToken}); +} + +class AddStudent extends StudentEvent { + final String sessionId; + final String studentName, studentPhoneNumber, studentGender, studentAddress; + final apiToken; + AddStudent({required this.sessionId, required this.studentName, required this.studentPhoneNumber, required this.studentGender, required this.studentAddress, required this.apiToken}); +} + +class EditStudent extends StudentEvent { + final String sessionId; + final int studentId, index; + final String studentName, studentPhoneNumber, studentGender, studentAddress; + final apiToken; + EditStudent({required this.sessionId, required this.index, required this.studentId, required this.studentName, required this.studentPhoneNumber, required this.studentGender, required this.studentAddress, required this.apiToken}); +} + +class DeleteStudent extends StudentEvent { + final String sessionId; + final int studentId, index; + final apiToken; + DeleteStudent({required this.sessionId, required this.studentId, required this.index, required this.apiToken}); +} \ No newline at end of file diff --git a/lib/bloc/student/student_state.dart b/lib/bloc/student/student_state.dart new file mode 100644 index 0000000..60d225a --- /dev/null +++ b/lib/bloc/student/student_state.dart @@ -0,0 +1,99 @@ +import 'package:mobdr/model/integration/student_model.dart'; +import 'package:meta/meta.dart'; + +@immutable +abstract class StudentState {} + +class InitialStudentState extends StudentState {} + +// get student state +class GetStudentWaiting extends StudentState {} + +class GetStudentError extends StudentState { + final String errorMessage; + GetStudentError({ + required this.errorMessage, + }); +} + +class GetStudentSuccess extends StudentState { + final List studentData; + GetStudentSuccess({required this.studentData}); +} + +// general +class StudentErrorValidation extends StudentState { + final String errorMessage; + StudentErrorValidation({ + required this.errorMessage, + }); +} + +// add student state +class AddStudentWaiting extends StudentState {} + +class AddStudentError extends StudentState { + final String errorMessage; + AddStudentError({ + required this.errorMessage, + }); +} + +class AddStudentSuccess extends StudentState { + final int studentId; + final String msg, + studentName, + studentPhoneNumber, + studentGender, + studentAddress; + AddStudentSuccess( + {required this.msg, + required this.studentId, + required this.studentName, + required this.studentPhoneNumber, + required this.studentGender, + required this.studentAddress}); +} + +// edit student state +class EditStudentWaiting extends StudentState {} + +class EditStudentError extends StudentState { + final String errorMessage; + EditStudentError({ + required this.errorMessage, + }); +} + +class EditStudentSuccess extends StudentState { + final int studentId, index; + final String msg, + studentName, + studentPhoneNumber, + studentGender, + studentAddress; + EditStudentSuccess( + {required this.msg, + required this.index, + required this.studentId, + required this.studentName, + required this.studentPhoneNumber, + required this.studentGender, + required this.studentAddress}); +} + +// delete student state +class DeleteStudentWaiting extends StudentState {} + +class DeleteStudentError extends StudentState { + final String errorMessage; + DeleteStudentError({ + required this.errorMessage, + }); +} + +class DeleteStudentSuccess extends StudentState { + final String msg; + final int index; + DeleteStudentSuccess({required this.msg, required this.index}); +} diff --git a/lib/config/constant.dart b/lib/config/constant.dart index 881b614..7ca8e84 100644 --- a/lib/config/constant.dart +++ b/lib/config/constant.dart @@ -17,6 +17,12 @@ const Color BLACK_GREY = Color(0xff777777); const Color SOFT_GREY = Color(0xFFaaaaaa); const Color SOFT_BLUE = Color(0xff01aed6); +const int STATUS_OK = 200; +const int STATUS_BAD_REQUEST = 400; +const int STATUS_NOT_AUTHORIZED = 403; +const int STATUS_NOT_FOUND = 404; +const int STATUS_INTERNAL_ERROR = 500; + const String ERROR_OCCURED = 'Error occured, please try again later'; const int LIMIT_PAGE = 8; @@ -26,3 +32,14 @@ const String GLOBAL_URL = 'https://ijtechnology.net/assets/images/api/devkit'; //const String GLOBAL_URL = 'http://192.168.100.9/devkit'; const String LOCAL_IMAGES_URL = 'assets/images'; + +class ApiConstants { + static String baseUrl = 'https://mp4.ikksgroup.com'; + static String mobDREndpoint = '/mobdr'; + static String mp4Endpoint = '/MobilePortal4/webresources'; + static String externalEndpoint = '/MobilePortal4_external/webresources'; + static String restEndpoint = '/rest/api'; +} + +const String LOGIN_API = 'https://mp4.ikksgroup.com' + "/authentication/login"; +const String PRODUCT_API = 'https://mp4.ikksgroup.com' + "/example/getProduct"; diff --git a/lib/model/login.dart b/lib/model/login.dart new file mode 100644 index 0000000..6041b1f --- /dev/null +++ b/lib/model/login.dart @@ -0,0 +1,57 @@ +class LoginModel { + int idUtilisateur; + String email; + int expire; + String guid; + String langage; + String lastTraduction; + String login; + String nom; + String prenom; + String version; + String photo; + + LoginModel( + this.idUtilisateur, + this.email, + this.expire, + this.guid, + this.langage, + this.lastTraduction, + this.login, + this.nom, + this.prenom, + this.version, + this.photo); + + LoginModel.fromJson(Map json) + : idUtilisateur = json['id_utilisateur'], + email = json['email'], + expire = json['expire'], + guid = json['guid'], + langage = json['langage'], + lastTraduction = json['last_traduction'], + login = json['login'], + nom = json['nom'], + prenom = json['prenom'], + version = json['version'], + photo = json['photo']; + + Map toJson() { + final Map data = new Map(); + + data['id_utilisateur'] = this.idUtilisateur; + data['email'] = this.email; + data['expire'] = this.expire; + data['guid'] = this.guid; + data['langage'] = this.langage; + data['last_traduction'] = this.lastTraduction; + data['login'] = this.login; + data['nom'] = this.nom; + data['prenom'] = this.prenom; + data['version'] = this.version; + data['photo'] = this.photo; + + return data; + } +} diff --git a/lib/model/user_profile.dart b/lib/model/user_profile.dart new file mode 100644 index 0000000..c24bd5c --- /dev/null +++ b/lib/model/user_profile.dart @@ -0,0 +1,20 @@ +class UserProfileModel { + final String name; + final String accessToken; + final int age; + + UserProfileModel({this.name = '', this.accessToken = '', this.age = 0}); + + UserProfileModel.fromJson(Map json) + : name = json['name'], + accessToken = json['accessToken'], + age = json['age']; + + Map toJson() { + final Map data = new Map(); + data['name'] = this.name; + data['accessToken'] = this.accessToken; + data['age'] = this.age; + return data; + } +} diff --git a/lib/network/api_provider.dart b/lib/network/api_provider.dart new file mode 100644 index 0000000..e83f342 --- /dev/null +++ b/lib/network/api_provider.dart @@ -0,0 +1,313 @@ +/* +This is api provider +This page is used to get data from API + */ + +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/service/local_storage.dart'; +import 'package:mobdr/model/login.dart'; +import 'package:dio/dio.dart'; +import 'dart:convert'; +import 'package:crypto/crypto.dart'; + +class ApiProvider { + Dio dio = Dio(); + late Response response; + String connErr = 'Please check your internet connection and try again'; + + Future getCrud(url, body) async { + print('url : ' + url.toString()); + try { + dio.options.headers['content-Type'] = 'application/json'; + dio.options.headers['Accept'] = 'application/json'; + dio.options.connectTimeout = 30000; //5s + dio.options.receiveTimeout = 25000; + dio.options.queryParameters = body; + + return await dio.get(url); + } on DioError catch (e) { + //print(e.toString()+' | '+url.toString()); + if (e.type == DioErrorType.response) { + int? statusCode = e.response!.statusCode; + if (statusCode == STATUS_NOT_FOUND) { + throw "Api not found"; + } else if (statusCode == STATUS_INTERNAL_ERROR) { + throw "Internal Server Error"; + } else { + throw e.error.message.toString(); + } + } else if (e.type == DioErrorType.connectTimeout) { + throw e.message.toString(); + } else if (e.type == DioErrorType.cancel) { + throw 'cancel'; + } + throw connErr; + } finally { + //dio.close(); // TODO: Attention pas close + } + } + + Future getConnect(url, apiToken) async { + print('url : ' + url.toString()); + try { + dio.options.headers['content-Type'] = 'application/x-www-form-urlencoded'; + dio.options.connectTimeout = 30000; //5s + dio.options.receiveTimeout = 25000; + + return await dio.post(url, cancelToken: apiToken); + } on DioError catch (e) { + //print(e.toString()+' | '+url.toString()); + if (e.type == DioErrorType.response) { + int? statusCode = e.response!.statusCode; + if (statusCode == STATUS_NOT_FOUND) { + throw "Api not found"; + } else if (statusCode == STATUS_INTERNAL_ERROR) { + throw "Internal Server Error"; + } else { + throw e.error.message.toString(); + } + } else if (e.type == DioErrorType.connectTimeout) { + throw e.message.toString(); + } else if (e.type == DioErrorType.cancel) { + throw 'cancel'; + } + throw connErr; + } finally { + //dio.close(); + } + } + + Future postConnect(url, data, apiToken) async { + print('url : ' + url.toString()); + print('postData : ' + data.toString()); + try { + dio.options.headers['content-Type'] = 'application/x-www-form-urlencoded'; + dio.options.connectTimeout = 30000; //5s + dio.options.receiveTimeout = 25000; + + return await dio.post(url, data: data, cancelToken: apiToken); + } on DioError catch (e) { + //print(e.toString()+' | '+url.toString()); + if (e.type == DioErrorType.response) { + int? statusCode = e.response!.statusCode; + if (statusCode == STATUS_NOT_FOUND) { + throw "Api not found"; + } else if (statusCode == STATUS_INTERNAL_ERROR) { + throw "Internal Server Error"; + } else { + throw e.error.message.toString(); + } + } else if (e.type == DioErrorType.connectTimeout) { + throw e.message.toString(); + } else if (e.type == DioErrorType.cancel) { + throw 'cancel'; + } + throw connErr; + } finally { + dio.close(); + } + } + + /// login function + Future login( + String userName, String pinCode, String securityCode) async { + var body = { + "application": "MobDR ", //+ ExeInfo(exeVersion) + "pin_code": md5.convert(utf8.encode(pinCode)), + "security_code": securityCode, //sCodeSecurite + "langage": "fr", + "screenheight": "0", //SysYRes() + "screenwidth": "0", //SysXRes() + "browser_name": "Natif", + "browser_version": "Application", + "engine_name": "Android", + "device_model": "", + "device_type": "mobile", //SysInfoAppareil(sysModele) WDM 23 + "device_vendor": "", //SysInfoAppareil(sysFabricant) WDM 23 + "ismobile": 1, + "engine_version": "", // SysVersionAndroid(sysVersionApiLevel) + "os_name": "", // SysVersionAndroid(sysVersionPlateForme) + "os_version": "", //SysVersionAndroid(sysVersionNumÈro) + "fingerprint": + "aa" // TODO: on peut mettre un fingerprint sur la version ou sur l'heure + }; + + try { + response = await getCrud( + ApiConstants.baseUrl + + ApiConstants.mp4Endpoint + + ApiConstants.restEndpoint + + '/login/' + + userName + + '/guid', + body); + + print('res : ' + response.toString()); + + switch (response.data['autorisation']) { + case 1: + LocalStorage.saveLoginData(LoginModel.fromJson(response.data)); + return 'OK'; + case -1: + return 'Compte désactivé ...'; + case -2: + return 'SECURITY_CODE'; + case -3: + return 'Téléphone bloqué !'; + case -4: + return 'Code PIN incorrect !'; + default: + return "Une erreur inconnue s''est produite"; + } + } catch (ex) { + return ex.toString(); // return ex.response!.data; + } + } + + Future getExample(apiToken) async { + response = + await getConnect(ApiConstants.baseUrl + '/example/getData', apiToken); + print('res : ' + response.toString()); + return response.data.toString(); + } + + Future postExample(String id, apiToken) async { + var postData = {'id': id}; + response = await postConnect( + ApiConstants.baseUrl + '/example/postData', postData, apiToken); + print('res : ' + response.toString()); + return response.data.toString(); + } + /* + Future> getStudent(String sessionId, apiToken) async { + var postData = {'session_id': sessionId}; + response = await postConnect( + ApiConstants.baseUrl + '/student/getStudent', postData, apiToken); + if (response.data['status'] == STATUS_OK) { + List responseList = response.data['data']; + List listData = + responseList.map((f) => StudentModel.fromJson(f)).toList(); + return listData; + } else { + throw response.data['msg']; + } + } + */ + + Future> addStudent( + String sessionId, + String studentName, + String studentPhoneNumber, + String studentGender, + String studentAddress, + apiToken) async { + var postData = { + 'session_id': sessionId, + 'student_name': studentName, + 'student_phone_number': studentPhoneNumber, + 'student_gender': studentGender, + 'student_address': studentAddress, + }; + response = await postConnect( + ApiConstants.baseUrl + '/student/addStudent', postData, apiToken); + if (response.data['status'] == STATUS_OK) { + List respList = []; + respList.add(response.data['msg']); + respList.add(response.data['data']['id']); + return respList; + } else { + throw response.data['msg']; + } + } + + Future editStudent( + String sessionId, + int studentId, + String studentName, + String studentPhoneNumber, + String studentGender, + String studentAddress, + apiToken) async { + var postData = { + 'session_id': sessionId, + 'student_id': studentId, + 'student_name': studentName, + 'student_phone_number': studentPhoneNumber, + 'student_gender': studentGender, + 'student_address': studentAddress, + }; + response = await postConnect( + ApiConstants.baseUrl + '/student/editStudent', postData, apiToken); + if (response.data['status'] == STATUS_OK) { + return response.data['msg']; + } else { + throw response.data['msg']; + } + } + + Future deleteStudent( + String sessionId, int studentId, apiToken) async { + var postData = { + 'session_id': sessionId, + 'student_id': studentId, + }; + response = await postConnect( + ApiConstants.baseUrl + '/student/deleteStudent', postData, apiToken); + if (response.data['status'] == STATUS_OK) { + return response.data['msg']; + } else { + throw response.data['msg']; + } + } + + Future> login2( + String email, String password, apiToken) async { + var postData = { + 'email': email, + 'password': password, + }; + response = await postConnect(LOGIN_API, postData, apiToken); + if (response.data['status'] == STATUS_OK) { + List responseList = response.data['data']; + List listData = + responseList.map((f) => LoginModel.fromJson(f)).toList(); + return listData; + } else { + throw response.data['msg']; + } + } + + /* + Future> getProductGrid( + String sessionId, String skip, String limit, apiToken) async { + var postData = {'session_id': sessionId, 'skip': skip, 'limit': limit}; + response = await postConnect(PRODUCT_API, postData, apiToken); + if (response.data['status'] == STATUS_OK) { + List responseList = response.data['data']; + //print('data : '+responseList.toString()); + List listData = + responseList.map((f) => ProductGridModel.fromJson(f)).toList(); + return listData; + } else { + throw response.data['msg']; + } + } + */ + + /* + Future> getProductListview( + String sessionId, String skip, String limit, apiToken) async { + var postData = {'session_id': sessionId, 'skip': skip, 'limit': limit}; + response = await postConnect(PRODUCT_API, postData, apiToken); + if (response.data['status'] == STATUS_OK) { + List responseList = response.data['data']; + //print('data : '+responseList.toString()); + List listData = + responseList.map((f) => ProductListviewModel.fromJson(f)).toList(); + return listData; + } else { + throw response.data['msg']; + } + } + */ +} diff --git a/lib/service/local_storage.dart b/lib/service/local_storage.dart new file mode 100644 index 0000000..fa1e79b --- /dev/null +++ b/lib/service/local_storage.dart @@ -0,0 +1,85 @@ +import 'dart:convert'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +//import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:mobdr/model/login.dart'; +import 'package:mobdr/model/user_profile.dart'; + +class LocalStorage { + //static Future get prefs async => + // await SharedPreferences.getInstance(); + + static FlutterSecureStorage get securePref => FlutterSecureStorage(); + + static Future eraseProfileData() async { + var loginData = (await getLoginData()); + loginData?.guid = ''; + //await saveLoginData(LoginModel: loginData); + } + + static Future getLoginData() async { + try { + var loginData = await securePref.read(key: 'loginData'); + return LoginModel.fromJson(json.decode(loginData!)); + } catch (e) { + return null; + } + } + + static Future saveLoginData(LoginModel loginModel) async { + return securePref.write( + key: 'loginData', value: json.encode(loginModel.toJson())); + } + + static Future getProfileData() async { + try { + var profileData = await securePref.read(key: 'profileData'); + return UserProfileModel.fromJson(json.decode(profileData!)); + } catch (e) { + return null; + } + } + + static Future saveProfileData(UserProfileModel profileModel) async { + try { + return securePref.write( + key: 'profileData', value: json.encode(profileModel.toJson())); + } catch (e) { + return null; + } + } + + static Future saveItem({@required item, @required key}) async { + securePref.write(key: key.toString(), value: item); + } + + static Future eraseItem({@required key}) async { + securePref.delete(key: '$key'); + } + + static Future keyExists(String key) async { + return securePref.containsKey(key: key); + } + + /* + static eraseAll() async { + var savedData = await getSavedBooks(); + for (var item in savedData) { + try { + await File(await downloadDir(item.id)).delete(); + } catch (e) {} + } + securePref.deleteAll(); + (await prefs).clear(); + return; + } + + static Future getItemData({@required key}) async { + return await securePref.containsKey(key: '$key') + ? securePref.read(key: '$key') + : null; + } + */ +} diff --git a/lib/ui/account/tab_account.dart b/lib/ui/account/tab_account.dart index 8654ea4..13a2edf 100644 --- a/lib/ui/account/tab_account.dart +++ b/lib/ui/account/tab_account.dart @@ -19,7 +19,7 @@ import 'package:mobdr/ui/general/notification.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/ui/reusable/cache_image_network.dart'; import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; +import 'package:mobdr/ui/authentication/signin.dart'; class TabAccountPage extends StatefulWidget { @override @@ -97,8 +97,24 @@ class _TabAccountPageState extends State child: GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { - Fluttertoast.showToast( - msg: 'Click Sign Out', toastLength: Toast.LENGTH_LONG); + Navigator.pushAndRemoveUntil( + context, + PageRouteBuilder(pageBuilder: (BuildContext context, + Animation animation, Animation secondaryAnimation) { + return SigninPage(); + }, transitionsBuilder: (BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child) { + return new SlideTransition( + position: new Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: child, + ); + }), + (Route route) => false); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/ui/authentication/forgot_password.dart b/lib/ui/authentication/forgot_password.dart deleted file mode 100644 index 1430c09..0000000 --- a/lib/ui/authentication/forgot_password.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:mobdr/config/constant.dart'; -import 'package:mobdr/config/global_style.dart'; - -class ForgotPasswordPage extends StatefulWidget { - @override - _ForgotPasswordPageState createState() => _ForgotPasswordPageState(); -} - -class _ForgotPasswordPageState extends State { - TextEditingController _etEmail = TextEditingController(); - - @override - void initState() { - _etEmail = TextEditingController(text: 'robert.steven@ijtechnology.net'); - - super.initState(); - } - - @override - void dispose() { - _etEmail.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: ListView( - padding: EdgeInsets.fromLTRB(30, 120, 30, 30), - children: [ - Center(child: Image.asset('assets/images/logo.png', height: 32)), - SizedBox( - height: 80, - ), - TextFormField( - keyboardType: TextInputType.emailAddress, - controller: _etEmail, - style: TextStyle(color: CHARCOAL), - onChanged: (textValue) { - setState(() {}); - }, - decoration: InputDecoration( - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Color(0xFFCCCCCC)), - ), - labelText: 'Email', - labelStyle: TextStyle(color: BLACK_GREY)), - ), - SizedBox( - height: 10, - ), - Text( - 'We will send the instruction to reset your password through the email', - style: GlobalStyle.resetPasswordNotes, - ), - SizedBox( - height: 40, - ), - Container( - child: TextButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) => PRIMARY_COLOR, - ), - overlayColor: MaterialStateProperty.all(Colors.transparent), - shape: MaterialStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(3.0), - )), - ), - onPressed: () { - Fluttertoast.showToast( - msg: 'Click Reset Password', - toastLength: Toast.LENGTH_LONG); - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: Text( - 'Reset Password', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.white), - textAlign: TextAlign.center, - ), - )), - ), - SizedBox( - height: 50, - ), - // create sign in link - Center( - child: GestureDetector( - onTap: () { - Navigator.pop(context); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GlobalStyle.iconBack, - Text( - ' Back to login', - style: GlobalStyle.back, - ) - ], - ), - ), - ) - ], - )); - } -} diff --git a/lib/ui/authentication/signin.dart b/lib/ui/authentication/signin.dart index 301d2af..100a24e 100644 --- a/lib/ui/authentication/signin.dart +++ b/lib/ui/authentication/signin.dart @@ -1,8 +1,10 @@ import 'package:universal_io/io.dart'; -import 'package:mobdr/ui/home.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:mobdr/network/api_provider.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:mobdr/ui/home.dart'; +import 'package:mobdr/ui/authentication/verification.dart'; class SigninPage extends StatefulWidget { @override @@ -12,6 +14,10 @@ class SigninPage extends StatefulWidget { class _SigninPageState extends State { bool _obscureText = true; IconData _iconVisible = Icons.visibility_off; + ApiProvider _apiProvider = ApiProvider(); // TODO: A voir si bien positionné + + TextEditingController _etUserName = TextEditingController(); + TextEditingController _etPinCode = TextEditingController(); Color _gradientTop = Color(0xFF039be6); Color _gradientBottom = Color(0xFF0299e2); @@ -31,11 +37,15 @@ class _SigninPageState extends State { @override void initState() { + _etUserName = TextEditingController(text: 'fbenoist'); + _etPinCode = TextEditingController(text: '9295'); super.initState(); } @override void dispose() { + _etUserName.dispose(); + _etPinCode.dispose(); super.dispose(); } @@ -97,6 +107,7 @@ class _SigninPageState extends State { ), TextField( keyboardType: TextInputType.emailAddress, + controller: _etUserName, decoration: InputDecoration( focusedBorder: UnderlineInputBorder( borderSide: @@ -114,6 +125,7 @@ class _SigninPageState extends State { ), TextField( obscureText: _obscureText, + controller: _etPinCode, decoration: InputDecoration( focusedBorder: UnderlineInputBorder( borderSide: @@ -122,7 +134,7 @@ class _SigninPageState extends State { borderSide: BorderSide(color: _underlineColor), ), - labelText: 'Password', + labelText: 'Pin code', labelStyle: TextStyle(color: Colors.grey[700]), suffixIcon: IconButton( icon: Icon(_iconVisible, @@ -133,24 +145,7 @@ class _SigninPageState extends State { ), ), SizedBox( - height: 20, - ), - Align( - alignment: Alignment.centerRight, - child: GestureDetector( - onTap: () { - Fluttertoast.showToast( - msg: 'Click forgot password', - toastLength: Toast.LENGTH_SHORT); - }, - child: Text( - 'Forgot Password?', - style: TextStyle(fontSize: 13), - ), - ), - ), - SizedBox( - height: 40, + height: 60, ), SizedBox( width: double.maxFinite, @@ -167,12 +162,26 @@ class _SigninPageState extends State { borderRadius: BorderRadius.circular(10), )), ), - onPressed: () { + onPressed: () async { //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => HomePage())); + var apiResponse = await _apiProvider.login( + _etUserName.text, _etPinCode.text, ''); + if (apiResponse == 'OK') { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => HomePage()), + (Route route) => false); + } else if (apiResponse == 'SECURITY_CODE') { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => + VerificationPage())); + } else { + Fluttertoast.showToast( + msg: apiResponse, + toastLength: Toast.LENGTH_SHORT); + } }, child: Padding( padding: @@ -188,32 +197,6 @@ class _SigninPageState extends State { ], )), ), - SizedBox( - height: 50, - ), - // create sign up link - Center( - child: Wrap( - children: [ - Text('New User? '), - GestureDetector( - onTap: () { - Fluttertoast.showToast( - msg: 'Click signup', - toastLength: Toast.LENGTH_SHORT); - }, - child: Text( - 'Sign Up', - style: TextStyle( - color: _mainColor, fontWeight: FontWeight.w700), - ), - ) - ], - ), - ), - SizedBox( - height: 20, - ), ], ) ], diff --git a/lib/ui/authentication/signin_old.dart b/lib/ui/authentication/signin_old.dart deleted file mode 100644 index 7019d2f..0000000 --- a/lib/ui/authentication/signin_old.dart +++ /dev/null @@ -1,250 +0,0 @@ -import 'package:mobdr/config/global_style.dart'; -import 'package:mobdr/ui/authentication/forgot_password.dart'; -import 'package:mobdr/ui/home.dart'; -import 'package:mobdr/ui/authentication/signup.dart'; -import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:mobdr/config/constant.dart'; - -class SigninPage extends StatefulWidget { - @override - _SigninPageState createState() => _SigninPageState(); -} - -class _SigninPageState extends State { - TextEditingController _etEmail = TextEditingController(); - bool _obscureText = true; - IconData _iconVisible = Icons.visibility_off; - - void _toggleObscureText() { - setState(() { - _obscureText = !_obscureText; - if (_obscureText == true) { - _iconVisible = Icons.visibility_off; - } else { - _iconVisible = Icons.visibility; - } - }); - } - - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - _etEmail.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: ListView( - padding: EdgeInsets.fromLTRB(30, 120, 30, 30), - children: [ - Center(child: Image.asset('assets/images/logo.png', height: 32)), - SizedBox( - height: 80, - ), - Text('Sign In', style: GlobalStyle.authTitle), - TextFormField( - keyboardType: TextInputType.emailAddress, - controller: _etEmail, - style: TextStyle(color: CHARCOAL), - onChanged: (textValue) { - setState(() {}); - }, - decoration: InputDecoration( - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Color(0xFFCCCCCC)), - ), - labelText: 'Email', - labelStyle: TextStyle(color: BLACK_GREY), - ), - ), - SizedBox( - height: 20, - ), - TextField( - obscureText: _obscureText, - style: TextStyle(color: CHARCOAL), - decoration: InputDecoration( - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Color(0xFFCCCCCC)), - ), - labelText: 'Password', - labelStyle: TextStyle(color: BLACK_GREY), - suffixIcon: IconButton( - icon: Icon(_iconVisible, color: Colors.grey[400], size: 20), - onPressed: () { - _toggleObscureText(); - }), - ), - ), - SizedBox( - height: 20, - ), - Align( - alignment: Alignment.centerRight, - child: Container( - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ForgotPasswordPage())); - FocusScope.of(context).unfocus(); - }, - child: Text( - 'Forgot Password?', - style: TextStyle(color: PRIMARY_COLOR, fontSize: 13), - ), - ), - )), - SizedBox( - height: 40, - ), - Container( - child: TextButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) => PRIMARY_COLOR, - ), - overlayColor: MaterialStateProperty.all(Colors.transparent), - shape: MaterialStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(3.0), - )), - ), - onPressed: () { - //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); - Navigator.pushReplacement(context, - MaterialPageRoute(builder: (context) => HomePage())); - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: Text( - 'Login', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.white), - textAlign: TextAlign.center, - ), - )), - ), - SizedBox( - height: 40, - ), - Center( - child: Text( - 'Or sign in with', - style: GlobalStyle.authSignWith, - ), - ), - SizedBox( - height: 20, - ), - Container( - margin: EdgeInsets.fromLTRB(20, 0, 20, 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - GestureDetector( - onTap: () { - //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); - Navigator.pushReplacement(context, - MaterialPageRoute(builder: (context) => HomePage())); - Fluttertoast.showToast( - msg: 'Sign in with Google', - toastLength: Toast.LENGTH_LONG); - }, - child: Image( - image: AssetImage("assets/images/google.png"), - width: 40, - ), - ), - GestureDetector( - onTap: () { - //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); - Navigator.pushReplacement(context, - MaterialPageRoute(builder: (context) => HomePage())); - Fluttertoast.showToast( - msg: 'Sign in with Facebook', - toastLength: Toast.LENGTH_LONG); - }, - child: Image( - image: AssetImage("assets/images/facebook.png"), - width: 40, - ), - ), - GestureDetector( - onTap: () { - //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); - Navigator.pushReplacement(context, - MaterialPageRoute(builder: (context) => HomePage())); - Fluttertoast.showToast( - msg: 'Sign in with Twitter', - toastLength: Toast.LENGTH_LONG); - }, - child: Image( - image: AssetImage("assets/images/twitter.png"), - width: 40, - ), - ) - ], - ), - ), - SizedBox( - height: 20, - ), - Center( - child: GestureDetector( - onTap: () { - Navigator.push(context, - MaterialPageRoute(builder: (context) => SignupPage())); - FocusScope.of(context).unfocus(); - }, - child: Wrap( - children: [ - Text( - 'No account yet? ', - style: GlobalStyle.authBottom1, - ), - Text( - 'Create one', - style: GlobalStyle.authBottom2, - ) - ], - ), - ), - ), - SizedBox( - height: 30, - ), - Center( - child: GestureDetector( - onTap: () { - Navigator.pop(context); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GlobalStyle.iconBack, - Text( - ' Back', - style: GlobalStyle.back, - ) - ], - ), - ), - ), - ], - )); - } -} diff --git a/lib/ui/authentication/signup.dart b/lib/ui/authentication/signup.dart deleted file mode 100644 index dcedf87..0000000 --- a/lib/ui/authentication/signup.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:mobdr/ui/home.dart'; -import 'package:flutter/material.dart'; -import 'package:mobdr/config/constant.dart'; -import 'package:mobdr/config/global_style.dart'; - -class SignupPage extends StatefulWidget { - final bool fromList; - - const SignupPage({Key? key, this.fromList = false}) : super(key: key); - - @override - _SignupPageState createState() => _SignupPageState(); -} - -class _SignupPageState extends State { - TextEditingController _etEmail = TextEditingController(); - TextEditingController _etName = TextEditingController(); - bool _obscureText = true; - IconData _iconVisible = Icons.visibility_off; - - void _toggleObscureText() { - setState(() { - _obscureText = !_obscureText; - if (_obscureText == true) { - _iconVisible = Icons.visibility_off; - } else { - _iconVisible = Icons.visibility; - } - }); - } - - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - _etEmail.dispose(); - _etName.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: WillPopScope( - onWillPop: () { - FocusScope.of(context).unfocus(); - return Future.value(true); - }, - child: ListView( - padding: EdgeInsets.fromLTRB(30, 120, 30, 30), - children: [ - Center(child: Image.asset('assets/images/logo.png', height: 32)), - SizedBox( - height: 80, - ), - Text('Sign Up', style: GlobalStyle.authTitle), - TextFormField( - keyboardType: TextInputType.emailAddress, - controller: _etEmail, - style: TextStyle(color: CHARCOAL), - onChanged: (textValue) { - setState(() {}); - }, - decoration: InputDecoration( - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Color(0xFFCCCCCC)), - ), - labelText: 'Email', - labelStyle: TextStyle(color: BLACK_GREY), - ), - ), - SizedBox( - height: 20, - ), - TextFormField( - keyboardType: TextInputType.text, - controller: _etName, - style: TextStyle(color: CHARCOAL), - onChanged: (textValue) { - setState(() {}); - }, - decoration: InputDecoration( - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Color(0xFFCCCCCC)), - ), - labelText: 'Name', - labelStyle: TextStyle(color: BLACK_GREY), - ), - ), - SizedBox( - height: 20, - ), - TextField( - obscureText: _obscureText, - style: TextStyle(color: CHARCOAL), - decoration: InputDecoration( - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: PRIMARY_COLOR, width: 2.0)), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Color(0xFFCCCCCC)), - ), - labelText: 'Password', - labelStyle: TextStyle(color: BLACK_GREY), - suffixIcon: IconButton( - icon: Icon(_iconVisible, color: Colors.grey[400], size: 20), - onPressed: () { - _toggleObscureText(); - }), - ), - ), - SizedBox( - height: 40, - ), - Container( - child: TextButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) => PRIMARY_COLOR, - ), - overlayColor: MaterialStateProperty.all(Colors.transparent), - shape: MaterialStateProperty.all(RoundedRectangleBorder( - borderRadius: BorderRadius.circular(3.0), - )), - ), - onPressed: () { - //Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => HomePage()), (Route route) => false); - if (!widget.fromList) { - Navigator.pop(context); - } - Navigator.pushReplacement(context, - MaterialPageRoute(builder: (context) => HomePage())); - }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: Text( - 'Register', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.white), - textAlign: TextAlign.center, - ), - )), - ), - SizedBox( - height: 30, - ), - Center( - child: GestureDetector( - onTap: () { - Navigator.pop(context); - FocusScope.of(context).unfocus(); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GlobalStyle.iconBack, - Text( - ' Back to login', - style: GlobalStyle.back, - ) - ], - ), - ), - ), - ], - ), - )); - } -} diff --git a/lib/ui/authentication/verification.dart b/lib/ui/authentication/verification.dart new file mode 100644 index 0000000..3dfdfe0 --- /dev/null +++ b/lib/ui/authentication/verification.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:pin_code_fields/pin_code_fields.dart'; +import 'package:mobdr/network/api_provider.dart'; +import 'package:mobdr/ui/home.dart'; +import 'package:mobdr/ui/authentication/signin.dart'; + +class VerificationPage extends StatefulWidget { + @override + _VerificationPageState createState() => _VerificationPageState(); +} + +class _VerificationPageState extends State { + Color _color1 = Color(0xFF07ac12); + Color _color2 = Color(0xFF515151); + Color _color3 = Color(0xff777777); + Color _color4 = Color(0xFFaaaaaa); + + bool _buttonDisabled = true; + String _securityCode = ''; + ApiProvider _apiProvider = ApiProvider(); // TODO: A voir si bien positionné + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: ListView( + padding: EdgeInsets.fromLTRB(30, 120, 30, 30), + children: [ + Center(child: Icon(Icons.phone_android, color: _color1, size: 50)), + SizedBox(height: 20), + Center( + child: Text( + 'Enter the Verification Code', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold, color: _color2), + )), + SizedBox( + height: 20, + ), + Container( + width: MediaQuery.of(context).size.width / 1.5, + child: Text( + 'The verification code has been sent by mail', + style: TextStyle(fontSize: 13, color: _color3), + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 40, + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 50), + child: PinCodeTextField( + autoFocus: true, + appContext: context, + keyboardType: TextInputType.number, + length: 4, + showCursor: false, + obscureText: false, + animationType: AnimationType.fade, + pinTheme: PinTheme( + shape: PinCodeFieldShape.underline, + fieldHeight: 50, + fieldWidth: 40, + inactiveColor: _color4, + activeColor: _color1, + selectedColor: _color1), + animationDuration: Duration(milliseconds: 300), + backgroundColor: Colors.transparent, + onChanged: (value) { + setState(() { + if (value.length == 4) { + _buttonDisabled = false; + } else { + _buttonDisabled = true; + } + _securityCode = value; + }); + }, + beforeTextPaste: (text) { + return false; + }, + ), + ), + SizedBox( + height: 40, + ), + Container( + child: SizedBox( + width: double.maxFinite, + child: TextButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => + _buttonDisabled ? Colors.grey[300]! : _color1, + ), + overlayColor: MaterialStateProperty.all(Colors.transparent), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3.0), + )), + ), + onPressed: () async { + if (!_buttonDisabled) { + FocusScope.of(context).unfocus(); + + var apiResponse = await _apiProvider.login( + 'fbenoist', '9295', _securityCode); + if (apiResponse == 'OK') { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute(builder: (context) => HomePage()), + (Route route) => false); + } else { + Fluttertoast.showToast( + msg: apiResponse, toastLength: Toast.LENGTH_SHORT); + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => SigninPage()), + (Route route) => false); + } + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'Verify', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: _buttonDisabled + ? Colors.grey[600] + : Colors.white), + textAlign: TextAlign.center, + ), + ))), + ), + SizedBox( + height: 40, + ), + Center( + child: Wrap( + children: [ + Text( + "Didn't receive the code? ", + style: TextStyle(fontSize: 13, color: _color4), + ), + GestureDetector( + onTap: () { + Fluttertoast.showToast( + msg: 'Click resend', toastLength: Toast.LENGTH_SHORT); + }, + child: Text( + 'Resend', + style: TextStyle(fontSize: 13, color: _color1), + ), + ) + ], + ), + ), + ], + )); + } +} diff --git a/lib/ui/home/tab_home.dart b/lib/ui/home/tab_home.dart index 84d310b..eed776c 100644 --- a/lib/ui/home/tab_home.dart +++ b/lib/ui/home/tab_home.dart @@ -16,6 +16,8 @@ import 'package:mobdr/ui/reusable/reusable_widget.dart'; import 'package:mobdr/ui/reusable/cache_image_network.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:mobdr/service/local_storage.dart'; +import 'package:mobdr/model/login.dart'; class TabHomePage extends StatefulWidget { @override @@ -35,6 +37,7 @@ class _TabHomePageState extends State bool get wantKeepAlive => true; String defaultLang = 'en'; + String UserName = ''; late LanguageCubit _languageCubit; @@ -46,6 +49,9 @@ class _TabHomePageState extends State defaultLang = val!; }); }); + + LocalStorage.getLoginData().then((value) => UserName = value!.prenom); + super.initState(); } @@ -79,7 +85,8 @@ class _TabHomePageState extends State elevation: GlobalStyle.appBarElevation, title: Text( AppLocalizations.of(context)!.translate('i18n_hello')! + - ', Frédérik', + ', ' + + UserName, style: GlobalStyle.appBarTitle, ), backgroundColor: GlobalStyle.appBarBackgroundColor, diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index c58ebc9..b773214 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,9 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); g_autoptr(FlPluginRegistrar) objectbox_flutter_libs_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ObjectboxFlutterLibsPlugin"); objectbox_flutter_libs_plugin_register_with_registrar(objectbox_flutter_libs_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 94262e7..6d5d836 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_linux objectbox_flutter_libs ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8bffdec..9ca9b99 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import flutter_secure_storage_macos import objectbox_flutter_libs import package_info_plus import path_provider_foundation @@ -12,6 +13,7 @@ import shared_preferences_foundation import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) ObjectboxFlutterLibsPlugin.register(with: registry.registrar(forPlugin: "ObjectboxFlutterLibsPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 16959fc..6bbda76 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,40 +3,57 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - ObjectBox (1.8.1) + - objectbox_flutter_libs (0.0.1): + - FlutterMacOS + - ObjectBox (= 1.8.1) - package_info_plus (0.0.1): - FlutterMacOS - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS - sqflite (0.0.2): - FlutterMacOS - FMDB (>= 2.7.5) DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) + - objectbox_flutter_libs (from `Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) SPEC REPOS: trunk: - FMDB + - ObjectBox EXTERNAL SOURCES: FlutterMacOS: :path: Flutter/ephemeral + objectbox_flutter_libs: + :path: Flutter/ephemeral/.symlinks/plugins/objectbox_flutter_libs/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos sqflite: :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + ObjectBox: a7900d5335218cd437cbc080b7ccc38a5211f7b4 + objectbox_flutter_libs: f89ab4878f0f764a49077cfaa59030be69ae1d7e package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 diff --git a/pubspec.lock b/pubspec.lock index 7e2d371..e127f63 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -233,6 +233,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.4" + dio: + dependency: "direct main" + description: + name: dio + sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" + url: "https://pub.dev" + source: hosted + version: "4.0.6" fake_async: dependency: transitive description: @@ -315,6 +323,54 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "98352186ee7ad3639ccc77ad7924b773ff6883076ab952437d20f18a61f0a7c5" + url: "https://pub.dev" + source: hosted + version: "8.0.0" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "0912ae29a572230ad52d8a4697e5518d7f0f429052fd51df7e5a7952c7efe2a3" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "083add01847fc1c80a07a08e1ed6927e9acd9618a35e330239d4422cd2a58c50" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: b3773190e385a3c8a382007893d678ae95462b3c2279e987b55d140d3b0cb81b + url: "https://pub.dev" + source: hosted + version: "1.0.1" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: "42938e70d4b872e856e678c423cc0e9065d7d294f45bc41fc1981a4eb4beaffe" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: fc2910ec9b28d60598216c29ea763b3a96c401f0ce1d13cdf69ccb0e5c93c3ee + url: "https://pub.dev" + source: hosted + version: "2.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -341,6 +397,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: "290fde3a86072e4b37dbb03c07bec6126f0ecc28dad403c12ffe2e5a2d751ab7" + url: "https://pub.dev" + source: hosted + version: "7.2.0" glob: dependency: transitive description: @@ -597,6 +661,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + pin_code_fields: + dependency: "direct main" + description: + name: pin_code_fields + sha256: c8652519d14688f3fe2a8288d86910a46aa0b9046d728f292d3bf6067c31b4c7 + url: "https://pub.dev" + source: hosted + version: "7.4.0" platform: dependency: transitive description: @@ -665,10 +737,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.0.18" shared_preferences_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 08b4796..685a2dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,9 +30,12 @@ environment: dependencies: flutter: sdk: flutter + + dio: 4.0.6 objectbox: ^1.7.2 objectbox_flutter_libs: any - + pin_code_fields: 7.4.0 + flutter_localizations: sdk: flutter @@ -43,13 +46,16 @@ dependencies: package_info_plus: 3.0.3 flutter_bloc: 8.1.2 flutter_html: 3.0.0-alpha.6 + flutter_secure_storage: ^8.0.0 #https://pub.dev/packages/intl intl: 0.17.0 carousel_slider: 4.2.1 cached_network_image: 3.2.3 - shared_preferences: 2.0.17 + get_it: ^7.2.0 + shared_preferences: 2.0.18 + universal_io: 2.2.0 dev_dependencies: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index a84779d..0ee502a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,12 @@ #include "generated_plugin_registrant.h" +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); ObjectboxFlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ObjectboxFlutterLibsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 9f0138e..8d486f6 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + flutter_secure_storage_windows objectbox_flutter_libs )