import 'dart:io' show Platform; import 'package:doctor_app_flutter/config/shared_pref_kay.dart'; import 'package:doctor_app_flutter/core/enum/auth_method_types.dart'; import 'package:doctor_app_flutter/core/enum/viewstate.dart'; import 'package:doctor_app_flutter/core/model/imei_details.dart'; import 'package:doctor_app_flutter/core/viewModel/authentication_view_model.dart'; import 'package:doctor_app_flutter/core/viewModel/project_view_model.dart'; import 'package:doctor_app_flutter/models/auth/activation_Code_req_model.dart'; import 'package:doctor_app_flutter/models/auth/check_activation_code_request_model.dart'; import 'package:doctor_app_flutter/models/auth/activation_code_for_verification_screen_model.dart'; import 'package:doctor_app_flutter/models/doctor/clinic_model.dart'; import 'package:doctor_app_flutter/models/doctor/doctor_profile_model.dart'; import 'package:doctor_app_flutter/models/doctor/profile_req_Model.dart'; import 'package:doctor_app_flutter/util/date-utils.dart'; import 'package:doctor_app_flutter/util/dr_app_toast_msg.dart'; import 'package:doctor_app_flutter/util/translations_delegate_base.dart'; import 'package:doctor_app_flutter/widgets/auth/sms-popup.dart'; import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart'; import 'package:doctor_app_flutter/widgets/shared/buttons/app_buttons_widget.dart'; import 'package:doctor_app_flutter/widgets/shared/loader/gif_loader_dialog_utils.dart'; import 'package:doctor_app_flutter/widgets/transitions/fade_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; import 'package:provider/provider.dart'; import '../../config/size_config.dart'; import '../../core/viewModel/doctor_profile_view_model.dart'; import '../../landing_page.dart'; import '../../routes.dart'; import '../../util/dr_app_shared_pref.dart'; import '../../util/helpers.dart'; import '../../widgets/shared/dr_app_circular_progress_Indeicator.dart'; DrAppSharedPreferances sharedPref = new DrAppSharedPreferances(); Helpers helpers = Helpers(); class VerificationMethods extends StatefulWidget { VerificationMethods({this.changeLoadingState, this.password, this.model}); final password; final Function changeLoadingState; final AuthenticationViewModel model; @override _VerificationMethodsState createState() => _VerificationMethodsState(); } class _VerificationMethodsState extends State { MainAxisAlignment spaceBetweenMethods = MainAxisAlignment.spaceBetween; Future _loggedUserFuture; var _loggedUser; var verificationMethod; GetIMEIDetailsModel user; final LocalAuthentication auth = LocalAuthentication(); var _availableBiometrics; ProjectViewModel projectsProvider; bool isMoreOption = false; bool onlySMSBox = false; var loginTokenID; DoctorProfileViewModel doctorProfileViewModel; bool authenticated; AuthMethodTypes fingrePrintBefore; AuthMethodTypes selectedOption; @override void initState() { super.initState(); _loggedUserFuture = getSharedPref(); _getAvailableBiometrics(); } Future getSharedPref() async { sharedPref.getObj(LOGGED_IN_USER).then((userInfo) { _loggedUser = userInfo; }); sharedPref.getObj(LAST_LOGIN_USER).then((lastLogin) { if (lastLogin != null) { user = GetIMEIDetailsModel.fromJson(lastLogin); } }); print(user); } @override void didChangeDependencies() { super.didChangeDependencies(); final routeArgs = ModalRoute.of(context).settings.arguments as Map; verificationMethod = routeArgs != null ? routeArgs['verificationMethod'] : null; } @override Widget build(BuildContext context) { doctorProfileViewModel = Provider.of(context); projectsProvider = Provider.of(context); return FutureBuilder( future: Future.wait([_loggedUserFuture]), builder: (BuildContext context, AsyncSnapshot snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return DrAppCircularProgressIndeicator(); default: if (snapshot.hasError) { Helpers.showErrorToast('Error: ${snapshot.error}'); return Text('Error: ${snapshot.error}'); } else { return SingleChildScrollView( child: Container( height: SizeConfig.realScreenHeight * .87, width: SizeConfig.realScreenWidth, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( child: Column( children: [ user != null && isMoreOption == false ? Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ AppText(TranslationBase.of(context) .welcomeBack), AppText( Helpers.capitalize(user.doctorName), fontSize: SizeConfig.textMultiplier * 3.5, fontWeight: FontWeight.bold, ), SizedBox( height: 20, ), AppText( TranslationBase.of(context).accountInfo, fontSize: SizeConfig.textMultiplier * 2.5, fontWeight: FontWeight.w600, ), SizedBox( height: 20, ), Card( color: Colors.white, child: Row( children: [ Flexible( flex: 3, child: ListTile( title: Text( TranslationBase.of( context) .lastLoginAt, overflow: TextOverflow .ellipsis, style: TextStyle( fontFamily: 'Poppins', fontWeight: FontWeight.w800, fontSize: 14), ), subtitle: AppText( getType( user.logInTypeID, context), fontSize: 14, ))), Flexible( flex: 2, child: ListTile( title: AppText( user.editedOn != null ? getDate(DateUtils .convertStringToDate( user .editedOn)) : user.createdOn != null ? getDate(DateUtils .convertStringToDate( user.createdOn)) : '--', textAlign: TextAlign.right, fontSize: 14, fontWeight: FontWeight.w800, ), subtitle: AppText( user.editedOn != null ? getTime(DateUtils .convertStringToDate( user .editedOn)) : user.createdOn != null ? getTime(DateUtils .convertStringToDate( user.createdOn)) : '--', textAlign: TextAlign.right, fontSize: 14, ), )) ], )), ], ) : Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ this.onlySMSBox == false ? AppText( TranslationBase.of(context) .verifyLoginWith, fontSize: SizeConfig.textMultiplier * 3.5, textAlign: TextAlign.left, ) : AppText( TranslationBase.of(context) .verifyFingerprint2, fontSize: SizeConfig.textMultiplier * 2.5, textAlign: TextAlign.start, ), ]), user != null && isMoreOption == false ? Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: InkWell( onTap: () => { // TODO check this logic it seem it will create bug to us authenticateUser(AuthMethodTypes.Fingerprint, true) }, child: methodCard( user.logInTypeID == 4 ?AuthMethodTypes.FaceID:user.logInTypeID == 2?AuthMethodTypes.WhatsApp:user.logInTypeID ==3? AuthMethodTypes.Fingerprint:AuthMethodTypes.SMS ))), Expanded( child: methodCard(AuthMethodTypes.MoreOptions)) ]), ]) : Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ onlySMSBox == false ? Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: methodCard( AuthMethodTypes.Fingerprint)), Expanded( child: methodCard( AuthMethodTypes.FaceID)) ], ) : SizedBox(), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: methodCard(AuthMethodTypes.SMS)), Expanded( child: methodCard(AuthMethodTypes.WhatsApp)) ], ), ]), // ) ], ), ), Column( mainAxisAlignment: MainAxisAlignment.end, children: [ user != null ? Row( children: [ Expanded( child: AppButton( title: TranslationBase.of(context) .useAnotherAccount, color: Colors.red[700], onPressed: () { Navigator.of(context).pushNamed(LOGIN); }, )), ], ) : SizedBox(), ], ), ], ), )); } } }); } sendActivationCodeByOtpNotificationType( AuthMethodTypes authMethodType) async { if (authMethodType == AuthMethodTypes.SMS || authMethodType == AuthMethodTypes.WhatsApp) { GifLoaderDialogUtils.showMyDialog(context); int projectID = await sharedPref.getInt(PROJECT_ID); // TODO create model for _loggedUser; ActivationCodeModel activationCodeModel = ActivationCodeModel( facilityId: projectID, memberID: _loggedUser['List_MemberInformation'][0]['MemberID'], zipCode: _loggedUser['ZipCode'], mobileNumber: _loggedUser['MobileNumber'], otpSendType: authMethodType.getTypeIdService().toString(), password: widget.password); await widget.model .sendActivationCodeForDoctorApp(activationCodeModel); if(widget.model.state == ViewState.ErrorLocal) { Helpers.showErrorToast(widget.model.error); GifLoaderDialogUtils.hideDialog(context); }else{ print("VerificationCode : " + widget.model.activationCodeForDoctorAppRes["VerificationCode"]); sharedPref.setString(VIDA_AUTH_TOKEN_ID, widget.model.activationCodeForDoctorAppRes["VidaAuthTokenID"]); sharedPref.setString( VIDA_REFRESH_TOKEN_ID, widget.model.activationCodeForDoctorAppRes["VidaRefreshTokenID"]); sharedPref.setString(LOGIN_TOKEN_ID, widget.model.activationCodeForDoctorAppRes["LogInTokenID"]); sharedPref.setString(PASSWORD, widget.password); GifLoaderDialogUtils.hideDialog(context); this.startSMSService(authMethodType); } } else { // TODO route to this page with parameters to inicate we should present 2 option if (Platform.isAndroid && authMethodType == AuthMethodTypes.Fingerprint) { Helpers.showErrorToast('Your device not support this feature'); } else { } } } sendActivationCodeVerificationScreen( AuthMethodTypes authMethodType) async { GifLoaderDialogUtils.showMyDialog(context); ActivationCodeForVerificationScreenModel activationCodeModel = ActivationCodeForVerificationScreenModel( iMEI: user.iMEI, facilityId: user.projectID, memberID: user.doctorID, zipCode: user.outSA == true ? '971' : '966', mobileNumber: user.mobile, oTPSendType: authMethodType ==AuthMethodTypes.FaceID?4:authMethodType ==AuthMethodTypes.Fingerprint?3:authMethodType ==AuthMethodTypes.WhatsApp?2:1, isMobileFingerPrint: 1, vidaAuthTokenID: user.vidaAuthTokenID, vidaRefreshTokenID: user.vidaRefreshTokenID); await widget.model .sendActivationCodeVerificationScreen(activationCodeModel); if(widget.model.state == ViewState.ErrorLocal) { GifLoaderDialogUtils.hideDialog(context); Helpers.showErrorToast(widget.model.error); } else { print("VerificationCode : " + widget.model.activationCodeVerificationScreenRes["VerificationCode"]); sharedPref.setString(VIDA_AUTH_TOKEN_ID, widget.model.activationCodeVerificationScreenRes["VidaAuthTokenID"]); sharedPref.setString( VIDA_REFRESH_TOKEN_ID, widget.model.activationCodeVerificationScreenRes["VidaRefreshTokenID"]); sharedPref.setString(LOGIN_TOKEN_ID, widget.model.activationCodeVerificationScreenRes["LogInTokenID"]); if (authMethodType == AuthMethodTypes.SMS || authMethodType == AuthMethodTypes.WhatsApp) { GifLoaderDialogUtils.hideDialog(context); this.startSMSService(authMethodType); } else { checkActivationCode(); } } } Widget methodCard(AuthMethodTypes authMethodType) { switch (authMethodType) { case AuthMethodTypes.WhatsApp: return InkWell( onTap: () => {authenticateUser(AuthMethodTypes.WhatsApp, true)}, child: Container( margin: EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.white, ), child: Padding( padding: EdgeInsets.fromLTRB(20, 15, 20, 15), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Image.asset( 'assets/images/verify-whtsapp.png', height: 60, width: 60, ), ], ), SizedBox( height: 20, ), AppText( TranslationBase.of(context).verifyWhatsApp, fontSize: 14, fontWeight: FontWeight.w600, ) ], ), ))); break; case AuthMethodTypes.SMS: return InkWell( onTap: () => {authenticateUser(AuthMethodTypes.SMS, true)}, child: Container( margin: EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.white, ), child: Padding( padding: EdgeInsets.fromLTRB(20, 15, 20, 15), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.asset( 'assets/images/verify-sms.png', height: 60, width: 60, ), SizedBox( height: 20, ), AppText( TranslationBase.of(context).verifySMS, fontSize: 14, fontWeight: FontWeight.w600, ) ], ), ))); break; case AuthMethodTypes.Fingerprint: return InkWell( onTap: () => { if (checkIfBiometricAvailable(BiometricType.fingerprint)) {authenticateUser(AuthMethodTypes.Fingerprint, true)} }, child: Container( margin: EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.white, ), child: Padding( padding: EdgeInsets.fromLTRB(20, 15, 20, 15), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.asset( 'assets/images/verification_fingerprint_icon.png', height: 60, width: 60, ), SizedBox( height: 20, ), AppText( TranslationBase.of(context).verifyFingerprint, fontSize: 14, fontWeight: FontWeight.w600, ) ], ), ))); break; case AuthMethodTypes.FaceID: return InkWell( onTap: () { if (checkIfBiometricAvailable(BiometricType.face)) { authenticateUser(AuthMethodTypes.FaceID, true); } }, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.white, ), margin: EdgeInsets.all(10), child: Padding( padding: EdgeInsets.fromLTRB(20, 15, 20, 15), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.asset( 'assets/images/verification_faceid_icon.png', height: 60, width: 60, ), SizedBox( height: 20, ), AppText( TranslationBase.of(context).verifyFaceID, fontSize: 14, fontWeight: FontWeight.w600, ) ], ), ))); break; default: return InkWell( onTap: () => { setState(() { isMoreOption = true; }) }, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.white, ), child: Padding( padding: EdgeInsets.fromLTRB(20, 15, 20, 15), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.asset( 'assets/images/login/more_icon.png', height: 60, width: 60, ), projectsProvider.isArabic ? SizedBox( height: 20, ) : SizedBox( height: 10, ), AppText( TranslationBase.of(context).moreVerification, fontSize: 14, fontWeight: FontWeight.w600, ) ], ), ))); } } getType(type, context) { switch (type) { case 1: return TranslationBase.of(context).verifySMS; break; case 3: return TranslationBase.of(context).verifyFingerprint; break; case 4: return TranslationBase.of(context).verifyFaceID; break; case 2: return TranslationBase.of(context).verifyWhatsApp; break; default: return TranslationBase.of(context).verifySMS; break; } } bool checkIfBiometricAvailable(BiometricType biometricType) { bool isAvailable = false; if (_availableBiometrics != null) { for (var i = 0; i < _availableBiometrics.length; i++) { if (biometricType == _availableBiometrics[i]) isAvailable = true; } } return isAvailable; } authenticateUser( AuthMethodTypes authMethodType, isActive) { if (authMethodType == AuthMethodTypes.Fingerprint || authMethodType == AuthMethodTypes.FaceID) { fingrePrintBefore = authMethodType; } this.selectedOption = fingrePrintBefore != null ? fingrePrintBefore : authMethodType; switch (authMethodType) { case AuthMethodTypes.SMS: sendActivationCode(authMethodType); break; case AuthMethodTypes.WhatsApp: sendActivationCode(authMethodType); break; case AuthMethodTypes.Fingerprint: this.loginWithFingerPrintOrFaceID(AuthMethodTypes.Fingerprint, isActive); break; case AuthMethodTypes.FaceID: this.loginWithFingerPrintOrFaceID(AuthMethodTypes.FaceID, isActive); break; default: break; } sharedPref.setInt(OTP_TYPE, selectedOption.getTypeIdService()); } Future _getAvailableBiometrics() async { var availableBiometrics; try { availableBiometrics = await auth.getAvailableBiometrics(); } on PlatformException catch (e) { print(e); } if (!mounted) return; setState(() { _availableBiometrics = availableBiometrics; }); } sendActivationCode(AuthMethodTypes authMethodType) async { if (user != null) { sendActivationCodeVerificationScreen(authMethodType); } else { sendActivationCodeByOtpNotificationType(authMethodType); } } startSMSService(AuthMethodTypes type) { new SMSOTP( context, type, _loggedUser != null ? _loggedUser['MobileNumber'] : user.mobile, (value) { showDialog( context: context, builder: (BuildContext context) { return Center( child: CircularProgressIndicator(), ); }); this.checkActivationCode(value: value); }, () => { widget.changeLoadingState(false), print('Faild..'), }, ).displayDialog(context); } loginWithFingerPrintOrFaceID(AuthMethodTypes authMethodTypes, isActive) async { if (isActive) { const iosStrings = const IOSAuthMessages( cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); try { authenticated = await auth.authenticateWithBiometrics( localizedReason: 'Scan your fingerprint to authenticate', useErrorDialogs: true, stickyAuth: true, iOSAuthStrings: iosStrings); } on PlatformException catch (e) { DrAppToastMsg.showErrorToast(e.toString()); } if (!mounted) return; if (user != null && (user.logInTypeID == 3 || user.logInTypeID == 4)) { this.sendActivationCode(authMethodTypes); } else { setState(() { this.onlySMSBox = true; }); } } } checkActivationCode( {value}) async { CheckActivationCodeRequestModel checkActivationCodeForDoctorApp = new CheckActivationCodeRequestModel( zipCode: _loggedUser != null ? _loggedUser['ZipCode'] : user.zipCode, mobileNumber: _loggedUser != null ? _loggedUser['MobileNumber'] : user.mobile, projectID: await sharedPref.getInt(PROJECT_ID) != null ? await sharedPref.getInt(PROJECT_ID) : user.projectID, logInTokenID: await sharedPref.getString(LOGIN_TOKEN_ID), activationCode: value ?? '0000', oTPSendType: await sharedPref.getInt(OTP_TYPE), generalid: "Cs2020@2016\$2958"); await widget.model.checkActivationCodeForDoctorApp(checkActivationCodeForDoctorApp); if(widget.model.state == ViewState.ErrorLocal){ Navigator.pop(context); Helpers.showErrorToast(widget.model.error); } else { sharedPref.setString(TOKEN, widget.model.checkActivationCodeForDoctorAppRes['AuthenticationTokenID']); if (widget.model.checkActivationCodeForDoctorAppRes['List_DoctorProfile'] != null) { loginProcessCompleted(widget.model.checkActivationCodeForDoctorAppRes['List_DoctorProfile'][0]); sharedPref.setObj(CLINIC_NAME, widget.model.checkActivationCodeForDoctorAppRes['List_DoctorsClinic']); } else { sharedPref.setObj(CLINIC_NAME, widget.model.checkActivationCodeForDoctorAppRes['List_DoctorsClinic']); ClinicModel clinic = ClinicModel.fromJson(widget.model.checkActivationCodeForDoctorAppRes['List_DoctorsClinic'][0]); getDocProfiles(clinic); } } } loginProcessCompleted(Map profile) { var doctor = DoctorProfileModel.fromJson(profile); doctorProfileViewModel.setDoctorProfile(doctor); sharedPref.setObj(DOCTOR_PROFILE, profile); projectsProvider.isLogin = true; Navigator.pushAndRemoveUntil( context, FadePage( page: LandingPage(), ), (r) => false); } getDocProfiles(ClinicModel clinicInfo) { ProfileReqModel docInfo = new ProfileReqModel( doctorID: clinicInfo.doctorID, clinicID: clinicInfo.clinicID, license: true, projectID: clinicInfo.projectID, tokenID: '', languageID: 2); doctorProfileViewModel.getDocProfiles(docInfo.toJson()).then((res) { if (res['MessageStatus'] == 1) { loginProcessCompleted(res['DoctorProfileList'][0]); } else { // changeLoadingState(false); Helpers.showErrorToast(res['ErrorEndUserMessage']); } }).catchError((err) { Helpers.showErrorToast(err); }); } getDate(DateTime date) { final DateFormat formatter = DateFormat('dd MMM yyyy'); return formatter.format(date); } getTime(DateTime date) { final DateFormat formatter = DateFormat('HH:mm a'); return formatter.format(date); } }