Merge branch 'development_faiz' into 'main'

Updated Code Structure UI

See merge request Cloud_Solution/hmg_nurses!4
development_faiz
Mirza Shafique 3 years ago
commit 7551836598

@ -46,5 +46,7 @@
"otp": "OTP",
"verification": "تَحَقّق",
"resend": "إعادة إرسال",
"codeExpire": "انتهت صلاحية رمز التحقق"
"codeExpire": "انتهت صلاحية رمز التحقق",
"moreVerificationOpts" :"المزيد من خيارات التحقق"
}

@ -46,5 +46,6 @@
"otp": "OTP",
"verification": "Verification",
"resend": "Resend",
"codeExpire": "The verification code has been expired"
"codeExpire": "The verification code has been expired",
"moreVerificationOpts" :"More verification options"
}

@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:hmg_nurses/classes/enums.dart';
class BaseViewModel extends ChangeNotifier {
ViewState _state = ViewState.idle;
bool isInternetConnection = true;
ViewState get state => _state;
String error = "";
//TODO add the user login model when we need it
void setState(ViewState viewState) {
_state = viewState;
notifyListeners();
}
}

@ -1,4 +1,8 @@
class ApiConsts {
static const MAX_SMALL_SCREEN = 660;
//static String baseUrl = "http://10.200.204.20:2801/"; // Local server
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
// static String baseUrl = "https://hmgwebservices.com"; // Live server

@ -0,0 +1,3 @@
enum APPSTATUS { loading, unAuthenticated, authenticated, unverified }
enum AuthMethodTypes { sms, whatsApp, fingerPrint, faceID, moreOptions }
enum ViewState { idle, busy, error, busyLocal, errorLocal }

@ -0,0 +1,103 @@
import 'package:flutter/cupertino.dart';
import 'package:hmg_nurses/classes/consts.dart';
class SizeConfig {
static double _blockWidth = 0;
static double _blockHeight = 0;
static double? realScreenWidth;
static double? realScreenHeight;
static double? screenWidth;
static double? screenHeight;
static double? textMultiplier;
static double? imageSizeMultiplier;
static double? heightMultiplier;
static bool isPortrait = true;
static double? widthMultiplier;
static bool isMobilePortrait = false;
static bool isMobile = false;
static bool isHeightShort = false;
static bool isHeightVeryShort = false;
static bool isHeightMiddle = false;
static bool isHeightLarge = false;
static bool isWidthLarge = false;
void init(BoxConstraints constraints, Orientation orientation) {
realScreenHeight = constraints.maxHeight;
realScreenWidth = constraints.maxWidth;
if (constraints.maxWidth <= ApiConsts.MAX_SMALL_SCREEN) {
isMobile = true;
}
if (constraints.maxHeight < 600) {
isHeightVeryShort = true;
} else if (constraints.maxHeight < 800) {
isHeightShort = true;
} else if (constraints.maxHeight < 1000) {
isHeightMiddle = true;
} else {
isHeightLarge = true;
}
if (constraints.maxWidth > 600) {
isWidthLarge = true;
}
if (orientation == Orientation.portrait) {
isPortrait = true;
if (realScreenWidth! < 450) {
isMobilePortrait = true;
}
// textMultiplier = _blockHeight;
// imageSizeMultiplier = _blockWidth;
screenHeight = realScreenHeight;
screenWidth = realScreenWidth;
} else {
isPortrait = false;
isMobilePortrait = false;
// textMultiplier = _blockWidth;
// imageSizeMultiplier = _blockHeight;
screenHeight = realScreenWidth;
screenWidth = realScreenHeight;
}
_blockWidth = screenWidth! / 100;
_blockHeight = screenHeight! / 100;
textMultiplier = _blockHeight;
imageSizeMultiplier = _blockWidth;
heightMultiplier = _blockHeight;
widthMultiplier = _blockWidth;
print('realScreenWidth $realScreenWidth');
print('realScreenHeight $realScreenHeight');
print('textMultiplier $textMultiplier');
print('imageSizeMultiplier $imageSizeMultiplier');
print('heightMultiplier$heightMultiplier');
print('widthMultiplier $widthMultiplier');
print('isPortrait $isPortrait');
print('isMobilePortrait $isMobilePortrait');
}
static getTextMultiplierBasedOnWidth({double? width}) {
// TODO handel LandScape case
if (width != null) {
return width / 100;
}
return widthMultiplier;
}
static getWidthMultiplier({double? width}) {
// TODO handel LandScape case
if (width != null) {
return width / 100;
}
return widthMultiplier;
}
static getHeightMultiplier({double? height}) {
// TODO handel LandScape case
if (height != null) {
return height / 100;
}
return heightMultiplier;
}
}

@ -14,6 +14,7 @@ import 'package:hmg_nurses/exceptions/api_exception.dart';
import 'package:hmg_nurses/extensions/int_extensions.dart';
import 'package:hmg_nurses/extensions/string_extensions.dart';
import 'package:hmg_nurses/extensions/widget_extensions.dart';
import 'package:hmg_nurses/main.dart';
import 'package:hmg_nurses/widgets/dialogs/confirm_dialog.dart';
import 'package:hmg_nurses/widgets/loading_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -55,11 +56,11 @@ class Utils {
return await Future.delayed(Duration(milliseconds: millis));
}
static void showLoading(BuildContext context) {
static void showLoading() {
WidgetsBinding.instance.addPostFrameCallback((_) {
_isLoadingVisible = true;
showDialog(
context: context,
context: navigatorKey.currentState!.overlay!.context,
barrierColor: Colors.black.withOpacity(0.5),
builder: (BuildContext context) => LoadingDialog(),
).then((value) {

@ -1,19 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AppProvider extends StatelessWidget {
final Widget child;
AppProvider({required this.child});
@override
Widget build(BuildContext context) {
return child;
return MultiProvider(
providers: [
// ChangeNotifierProvider(create: (_) => Counter()),
],
child: child,
);
}
}

@ -0,0 +1,18 @@
import 'package:get_it/get_it.dart';
import 'package:hmg_nurses/ui/login/login_vm.dart';
GetIt getIt = GetIt.instance;
void initDependencies () {
// TODO; WE WILL REGISTER ALL THE SERVICES HERE AS LAZY SINGLETON
//TODO: WE WILL REGISTER ALL THE VIEW MODELS AS FACTORY HERE
/// View Model
getIt.registerFactory(() => LoginViewModel());
}

@ -0,0 +1,33 @@
import 'package:hmg_nurses/classes/enums.dart';
extension SelectedAuthMethodTypesService on AuthMethodTypes {
int getTypeIdService() {
switch (this) {
case AuthMethodTypes.sms:
return 1;
case AuthMethodTypes.whatsApp:
return 2;
case AuthMethodTypes.fingerPrint:
return 3;
case AuthMethodTypes.faceID:
return 4;
case AuthMethodTypes.moreOptions:
return 5;
}
}
static getMethodsTypeService(int typeId) {
switch (typeId) {
case 1:
return AuthMethodTypes.sms;
case 2:
return AuthMethodTypes.whatsApp;
case 3:
return AuthMethodTypes.fingerPrint;
case 4:
return AuthMethodTypes.faceID;
case 5:
return AuthMethodTypes.moreOptions;
}
}
}

@ -62,7 +62,8 @@ class CodegenLoader extends AssetLoader{
"otp": "OTP",
"verification": "تَحَقّق",
"resend": "إعادة إرسال",
"codeExpire": "انتهت صلاحية رمز التحقق"
"codeExpire": "انتهت صلاحية رمز التحقق",
"moreVerificationOpts": "المزيد من خيارات التحقق"
};
static const Map<String,dynamic> en_US = {
"mohemm": "Mohemm",
@ -112,7 +113,8 @@ static const Map<String,dynamic> en_US = {
"otp": "OTP",
"verification": "Verification",
"resend": "Resend",
"codeExpire": "The verification code has been expired"
"codeExpire": "The verification code has been expired",
"moreVerificationOpts": "More verification options"
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": ar_SA, "en_US": en_US};
}

@ -2,9 +2,11 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_nurses/classes/size_config.dart';
import 'package:hmg_nurses/config/routes.dart';
import 'package:hmg_nurses/provider/dashboard_provider_model.dart';
import 'package:hmg_nurses/theme/app_theme.dart';
import 'package:hmg_nurses/ui/login/login_vm.dart';
import 'package:logger/logger.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
@ -12,7 +14,6 @@ import 'package:sizer/sizer.dart';
import 'generated/codegen_loader.g.dart';
GlobalKey<NavigatorState> navigatorKey = GlobalKey();
Logger logger = Logger(
@ -45,9 +46,8 @@ Future<void> main() async {
assetLoader: const CodegenLoader(),
child: MultiProvider(
providers: <SingleChildWidget>[
ChangeNotifierProvider<DashboardProviderModel>(
create: (_) => DashboardProviderModel(),
)
ChangeNotifierProvider<DashboardProviderModel>(create: (_) => DashboardProviderModel()),
ChangeNotifierProvider<LoginViewModel>(create: (_) => LoginViewModel())
],
child: const MyApp(),
),
@ -66,24 +66,26 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Sizer(
builder: (
BuildContext context,
Orientation orientation,
DeviceType deviceType,
) {
List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates;
return MaterialApp(
key: navigatorKey,
theme: AppTheme.getTheme(
EasyLocalization.of(context)?.locale.languageCode == "ar",
),
debugShowCheckedModeBanner: false,
localizationsDelegates: delegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
initialRoute: AppRoutes.login,
routes: AppRoutes.routes,
return LayoutBuilder(
builder: (context, constraints) {
return Sizer(
builder: (BuildContext context, Orientation orientation, DeviceType deviceType) {
SizeConfig().init(constraints, orientation);
List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates;
return MaterialApp(
key: navigatorKey,
theme: AppTheme.getTheme(
EasyLocalization.of(context)?.locale.languageCode == "ar",
),
debugShowCheckedModeBanner: false,
localizationsDelegates: delegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
initialRoute: AppRoutes.login,
routes: AppRoutes.routes,
);
},
);
},
);

@ -2,15 +2,18 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:hmg_nurses/classes/colors.dart';
import 'package:hmg_nurses/classes/utils.dart';
import 'package:hmg_nurses/dialogs/otp_dialog.dart';
import 'package:hmg_nurses/extensions/int_extensions.dart';
import 'package:hmg_nurses/classes/enums.dart';
import 'package:hmg_nurses/classes/size_config.dart';
import 'package:hmg_nurses/extensions/string_extensions.dart';
import 'package:hmg_nurses/extensions/util_extensions.dart';
import 'package:hmg_nurses/extensions/widget_extensions.dart';
import 'package:hmg_nurses/generated/locale_keys.g.dart';
import 'package:hmg_nurses/ui/login/login_vm.dart';
import 'package:hmg_nurses/ui/login/widgets/verification_method_card.dart';
import 'package:hmg_nurses/widgets/button/default_button.dart';
import 'package:local_auth/local_auth.dart';
import 'package:provider/provider.dart';
import 'package:sizer/sizer.dart';
// WhatsApp 2
@ -26,162 +29,15 @@ class LoginMethodsPage extends StatefulWidget {
}
class LoginMethodsPageState extends State<LoginMethodsPage> {
final LocalAuthentication localAuth = LocalAuthentication();
List<BiometricType> _availableBioMetricType = [];
int selectedFlag = 0;
bool isNeedVerifyWithFaceIDAndBiometrics = false;
bool lastLoginCheck = false;
@override
void initState() {
_getAvailableBiometrics();
super.initState();
}
String getVerificationType(int type) {
if (type == 1) {
return LocaleKeys.sms.tr();
} else if (type == 2) {
return LocaleKeys.whatsapp.tr();
} else if (type == 3) {
return LocaleKeys.face.tr();
} else if (type == 4) {
return LocaleKeys.fingerPrint.tr();
}
return "";
}
Future<void> _getAvailableBiometrics() async {
try {
_availableBioMetricType = await localAuth.getAvailableBiometrics();
} on PlatformException catch (e) {
// AppToast.showErrorToast(message: e.message);
if (kDebugMode) {
print(e);
}
}
if (mounted) setState(() {});
}
Future<bool> loginWithFaceIDAndBiometrics() async {
bool authenticated = false;
try {
authenticated = await localAuth.authenticate(
localizedReason: 'Scan your fingerprint to authenticate',
);
} on PlatformException catch (e) {
if (kDebugMode) {
print(e);
}
Utils.hideLoading(context);
Utils.showToast("Please enable your Touch or Face ID");
}
return authenticated;
}
Widget _loginOptionButton(String title, String icon, int flag, int? loginIndex) {
bool isDisable = ((flag == 3 && !checkBiometricIsAvailable(BiometricType.face)) || (flag == 4 && !checkBiometricIsAvailable(BiometricType.fingerprint)));
return InkWell(
onTap: isDisable
? null
: () async {
if (flag == 0) {
setState(() {
// isMoreOption = true;
});
} else {
if (flag == 3 || flag == 4) {
bool authenticateWithFaceAndTouchID = await loginWithFaceIDAndBiometrics();
if (!authenticateWithFaceAndTouchID) {
return;
} else {
isNeedVerifyWithFaceIDAndBiometrics = true;
selectedFlag = flag;
setState(() {
return;
});
}
} else {
if (isNeedVerifyWithFaceIDAndBiometrics) {
// performApiCall(_title, _icon, selectedFlag, _flag);
// TODO: CALL API AND THEN SHOW DIALOG
OtpDialog(
type: 1,
mobileNo: 0504278212,
onSuccess: (string, TextEditingController pinPutController) {},
onFailure: () {},
onResendCode: () {},
).displayDialog(context);
} else {
// performApiCall(_title, _icon, _flag, _flag);
// TODO: CALL API AND THEN SHOW DIALOG
OtpDialog(
type: 1,
mobileNo: 0504278212,
onSuccess: (string, TextEditingController pinPutController) {},
onFailure: () {},
onResendCode: () {},
).displayDialog(context);
}
}
}
},
child: Container(
padding: const EdgeInsets.only(left: 20, right: 20, bottom: 15, top: 28),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: isDisable ? Colors.grey.withOpacity(0.3) : Colors.white,
border: Border.all(color: MyColors.lightGreyEFColor, width: 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SvgPicture.asset(
icon,
height: 38,
width: 38,
color: isDisable ? MyColors.darkTextColor.withOpacity(0.7) : null,
),
title.toText14()
],
),
),
);
}
Widget getButton(int flag) {
switch (flag) {
case 1:
return _loginOptionButton(LocaleKeys.verifyThroughSMS.tr(), 'assets/images/login/verify_sms.svg', flag, null);
case 2:
return _loginOptionButton(LocaleKeys.verifyThroughWhatsapp.tr(), 'assets/images/login/verify_whatsapp.svg', flag, null);
case 3:
return _loginOptionButton(LocaleKeys.verifyThroughFace.tr(), 'assets/images/login/verify_face.svg', flag, BiometricType.face.index);
case 4:
return _loginOptionButton(LocaleKeys.verifyThroughFingerprint.tr(), 'assets/images/login/verify_thumb.svg', flag, BiometricType.fingerprint.index);
default:
return const SizedBox();
}
}
bool checkBiometricIsAvailable(BiometricType biometricType) {
bool isAvailable = false;
for (int i = 0; i < _availableBioMetricType.length; i++) {
if (biometricType == _availableBioMetricType[i]) {
isAvailable = true;
break;
}
}
return isAvailable;
}
bool isMoreOption = false;
bool onlySMSBox = false;
late AuthMethodTypes fingerPrintBefore;
late AuthMethodTypes selectedOption;
late LoginViewModel loginViewModel;
@override
Widget build(BuildContext context) {
loginViewModel = context.read<LoginViewModel>();
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
@ -191,84 +47,243 @@ class LoginMethodsPageState extends State<LoginMethodsPage> {
),
// actions: [Center(child: "Employee Digital ID".toText12(color: MyColors.textMixColor, isUnderLine: true).onPress(() {})), 21.width],
),
body: ListView(
padding: const EdgeInsets.all(21),
physics: const BouncingScrollPhysics(),
children: [
if (lastLoginCheck)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.welcomeBack.tr().toText12(),
"Faiz Hashmi".toText24(isBold: true),
10.height,
LocaleKeys.wouldYouLikeToLoginWithCurrentUsername.tr().toText18(),
Container(
height: 72,
margin: const EdgeInsets.only(top: 23, bottom: 23),
alignment: Alignment.center,
padding: const EdgeInsets.only(left: 17, right: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(
color: const Color(0xffefefef),
width: 1,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
LocaleKeys.lastLoginDetails.tr().toText16(),
//TODO: DATE
// DateUtil.formatDateToDate(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn!), false).toText12(),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color),
//TODO: VERIFICATION TYPE
// getVerificationType(mobileLoginInfoListModel!.loginType!).toText12(),
const Expanded(child: SizedBox()),
//TODO: DATE
body: SingleChildScrollView(
child: Center(
child: FractionallySizedBox(
widthFactor: 0.9,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 1.h),
if (loginViewModel.isFromLogin)
InkWell(
onTap: () {
loginViewModel.setUnverified(false, isFromLogin: false);
loginViewModel.setAppStatus(APPSTATUS.unAuthenticated);
},
child: const Icon(Icons.arrow_back_ios, color: MyColors.darkTextColor)),
Column(
children: <Widget>[
SizedBox(height: 1.h),
isMoreOption != null && isMoreOption == false
? Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LocaleKeys.welcomeBack.tr().toText12(),
SizedBox(height: 1.h),
"Faiz Hashmi".toText24(isBold: true),
SizedBox(height: 3.h),
LocaleKeys.wouldYouLikeToLoginWithCurrentUsername.tr().toText18(),
SizedBox(height: 3.h),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(10)),
border: Border.all(color: HexColor('#707070'), width: 0.1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
LocaleKeys.lastLoginDetails.tr(),
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: 'Poppins',
fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * 4.8,
color: Color(0xFF2E303A),
fontWeight: FontWeight.w600,
letterSpacing: -0.4),
),
Container(
width: MediaQuery.of(context).size.width * 0.55,
child: RichText(
text: TextSpan(
text: "${LocaleKeys.verificationType.tr()} ",
style: TextStyle(
color: const Color(0xFF575757),
fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * 3.5,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
letterSpacing: -0.4),
children: <TextSpan>[
TextSpan(
text: loginViewModel.getType(1),
style: TextStyle(
color: MyColors.darkTextColor,
fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * 3.5,
fontFamily: 'Poppins',
fontWeight: FontWeight.w600,
letterSpacing: -0.48),
)
]),
),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"12 NOV, 2022",
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: 'Poppins',
fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * 4.2,
color: Color(0xFF2E303A),
fontWeight: FontWeight.bold,
letterSpacing: -0.4),
),
Text(
"09:56 AM",
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: 'Poppins',
fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * 3.5,
color: Color(0xFF2E303A),
fontWeight: FontWeight.w600,
letterSpacing: -0.4),
),
// AppText(
// loginViewModel.user.editedOn != null
// ? AppDateUtils.getDayMonthYearDateFormatted(AppDateUtils.convertStringToDate(loginViewModel.user.editedOn), isMonthShort: true)
// : loginViewModel.user.createdOn != null
// ? AppDateUtils.getDayMonthYearDateFormatted(AppDateUtils.convertStringToDate(loginViewModel.user.createdOn), isMonthShort: true)
// : '--',
// textAlign: TextAlign.right,
// fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * 4.5,
// color: Color(0xFF2E303A),
// fontWeight: FontWeight.w700,
// letterSpacing: -0.48,
// ),
// AppText(
// loginViewModel.user.editedOn != null
// ? AppDateUtils.getHour(AppDateUtils.convertStringToDate(loginViewModel.user.editedOn))
// : loginViewModel.user.createdOn != null
// ? AppDateUtils.getHour(AppDateUtils.convertStringToDate(loginViewModel.user.createdOn))
// : '--',
// textAlign: TextAlign.right,
// fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * 3.5,
// fontWeight: FontWeight.w600,
// letterSpacing: -0.48,
// color: Color(0xFF575757),
// )
],
)
],
),
),
SizedBox(height: SizeConfig.heightMultiplier! * 3),
Row(
children: [
//todo add translation
LocaleKeys.pleaseVerify.tr().toText18().paddingOnly(left: 8),
],
),
SizedBox(
height: SizeConfig.heightMultiplier! * 2,
),
],
)
: Column(mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
onlySMSBox == true
? Container(
margin: const EdgeInsets.only(bottom: 20, top: 30),
child: LocaleKeys.pleaseVerify.tr().toText16(),
)
: LocaleKeys.pleaseVerify.tr().toText16(),
]),
isMoreOption != null && isMoreOption == false
? Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Expanded(
child: VerificationMethodsList(
loginViewModel: loginViewModel,
authMethodType: SelectedAuthMethodTypesService.getMethodsTypeService(1),
authenticateUser: (AuthMethodTypes authMethodType, isActive) {
loginViewModel.startSMSService(authMethodType, context: context);
},
),
),
Expanded(
child: VerificationMethodsList(
loginViewModel: loginViewModel,
authMethodType: AuthMethodTypes.moreOptions,
onShowMore: () {
setState(() {
isMoreOption = true;
});
},
))
]),
])
: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
onlySMSBox == false
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: VerificationMethodsList(
loginViewModel: loginViewModel,
authMethodType: AuthMethodTypes.fingerPrint,
authenticateUser: (AuthMethodTypes authMethodType, isActive) {
loginViewModel.startSMSService(authMethodType, context: context);
},
)),
Expanded(
child: VerificationMethodsList(
loginViewModel: loginViewModel,
authMethodType: AuthMethodTypes.faceID,
authenticateUser: (AuthMethodTypes authMethodType, isActive) {
loginViewModel.startSMSService(authMethodType, context: context);
},
))
],
)
: const SizedBox(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: VerificationMethodsList(
loginViewModel: loginViewModel,
authMethodType: AuthMethodTypes.sms,
authenticateUser: (AuthMethodTypes authMethodType, isActive) {
loginViewModel.startSMSService(authMethodType, context: context);
},
)),
Expanded(
child: VerificationMethodsList(
loginViewModel: loginViewModel,
authMethodType: AuthMethodTypes.whatsApp,
authenticateUser: (AuthMethodTypes authMethodType, isActive) {
loginViewModel.startSMSService(authMethodType, context: context);
},
))
],
),
]),
// DateUtil.formatDateToTime(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn!)).toText12(),
],
)
// )
],
),
),
],
),
if (!lastLoginCheck)
Text(
LocaleKeys.pleaseVerifyForBio.tr(),
style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.w600),
],
),
),
if (isNeedVerifyWithFaceIDAndBiometrics) Text(LocaleKeys.pleaseVerifyForBio.tr()),
GridView(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 13, mainAxisSpacing: 9),
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(top: 9),
shrinkWrap: true,
children: [
if (!isNeedVerifyWithFaceIDAndBiometrics) getButton(3),
if (!isNeedVerifyWithFaceIDAndBiometrics) getButton(4),
getButton(2),
getButton(1),
],
),
SizedBox(height: 10.h),
],
),
),
bottomSheet: !lastLoginCheck
bottomSheet: !isMoreOption
? null
: Container(
height: 10.h,

@ -1,7 +1,141 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hmg_nurses/base/base_vm.dart';
import 'package:hmg_nurses/classes/enums.dart';
import 'package:hmg_nurses/classes/utils.dart';
import 'package:hmg_nurses/dialogs/otp_dialog.dart';
import 'package:hmg_nurses/generated/locale_keys.g.dart';
import 'package:local_auth/local_auth.dart';
class LoginViewModel extends BaseViewModel {
bool isLogin = false;
bool unverified = false;
bool isFromLogin = false;
APPSTATUS appStatus = APPSTATUS.loading;
String localToken = "";
String nurseProfile = "";
final LocalAuthentication auth = LocalAuthentication();
late List<BiometricType> _availableBiometrics;
LoginViewModel();
/// get type name based on id.
getType(type) {
switch (type) {
case 1:
return LocaleKeys.sms.tr();
case 3:
return LocaleKeys.fingerPrint.tr();
case 4:
return LocaleKeys.face.tr();
case 2:
return LocaleKeys.whatsapp.tr();
default:
return LocaleKeys.sms.tr();
}
}
/// ask user to add his biometric
showIOSAuthMessages() async {
try {
await auth.authenticate(
localizedReason: 'Scan your fingerprint to authenticate',
);
} on PlatformException catch (e) {
if (kDebugMode) {
print(e);
}
}
}
/// check specific biometric if it available or not
Future<bool> checkIfBiometricAvailable(BiometricType biometricType) async {
bool isAvailable = false;
await _getAvailableBiometrics();
if (_availableBiometrics != null) {
for (var i = 0; i < _availableBiometrics.length; i++) {
if (biometricType == _availableBiometrics[i]) isAvailable = true;
}
}
return isAvailable;
}
/// get all available biometric on the device for local Auth service
Future<void> _getAvailableBiometrics() async {
try {
_availableBiometrics = await auth.getAvailableBiometrics();
} on PlatformException catch (e) {
if (kDebugMode) {
print(e);
}
}
}
/// determine the status of the app
APPSTATUS get status {
if (state == ViewState.busy) {
appStatus = APPSTATUS.loading;
} else {
if (nurseProfile != null) {
appStatus = APPSTATUS.authenticated;
} else if (unverified) {
appStatus = APPSTATUS.unverified;
} else if (isLogin) {
appStatus = APPSTATUS.authenticated;
} else {
appStatus = APPSTATUS.unAuthenticated;
}
}
return appStatus;
}
setAppStatus(APPSTATUS status) {
appStatus = status;
notifyListeners();
}
setUnverified(bool unverified, {bool isFromLogin = false}) {
this.unverified = unverified;
this.isFromLogin = isFromLogin;
notifyListeners();
}
class LoginVM extends ChangeNotifier {
/// logout function
logout({bool isFromLogin = false}) async {
// localToken = "";
// String lang = await sharedPref.getString(APP_Language);
// await Utils.clearSharedPref();
// doctorProfile = null;
// sharedPref.setString(APP_Language, lang);
// deleteUser();
// await getDeviceInfoFromFirebase();
// this.isFromLogin = isFromLogin;
// appStatus = APPSTATUS.unAuthenticated;
// setState(ViewState.Idle);
}
deleteUser() {
// user = null;
// unverified = false;
// isLogin = false;
}
}
startSMSService(AuthMethodTypes type, {isSilentLogin = false, required BuildContext context}) {
OtpDialog(
type: 1,
mobileNo: 0504278212,
onSuccess: (String otpCode, TextEditingController pinPut) {
Utils.showLoading();
//TODO: API CALL
// performDirectApiCall(_title, _icon, _flag, value);
},
onFailure: () => Navigator.pop(context),
onResendCode: () {})
.displayDialog(context);
}
}

@ -0,0 +1,135 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:hmg_nurses/classes/colors.dart';
import 'package:hmg_nurses/classes/enums.dart';
import 'package:hmg_nurses/classes/size_config.dart';
import 'package:hmg_nurses/generated/locale_keys.g.dart';
import 'package:hmg_nurses/ui/login/login_vm.dart';
import 'package:local_auth/local_auth.dart';
class VerificationMethodsList extends StatefulWidget {
final AuthMethodTypes authMethodType;
final Function(AuthMethodTypes type, bool isActive)? authenticateUser;
final Function? onShowMore;
final LoginViewModel loginViewModel;
const VerificationMethodsList({Key? key, required this.authMethodType, this.authenticateUser, this.onShowMore, required this.loginViewModel}) : super(key: key);
@override
VerificationMethodsListState createState() => VerificationMethodsListState();
}
class VerificationMethodsListState extends State<VerificationMethodsList> {
final LocalAuthentication auth = LocalAuthentication();
@override
Widget build(BuildContext context) {
switch (widget.authMethodType) {
case AuthMethodTypes.whatsApp:
return MethodTypeCard(
assetPath: 'assets/images/login/verify_whatsapp.svg',
onTap: () => {widget.authenticateUser!(AuthMethodTypes.whatsApp, true)},
label: LocaleKeys.verifyThroughWhatsapp.tr(),
);
case AuthMethodTypes.sms:
return MethodTypeCard(
assetPath: "assets/images/login/verify_sms.svg",
onTap: () => {widget.authenticateUser!(AuthMethodTypes.sms, true)},
label: LocaleKeys.verifyThroughSMS.tr(),
);
case AuthMethodTypes.fingerPrint:
return MethodTypeCard(
assetPath: 'assets/images/login/verify_thumb.svg',
onTap: () => {widget.authenticateUser!(AuthMethodTypes.fingerPrint, true)},
label: LocaleKeys.verifyThroughFingerprint.tr(),
);
case AuthMethodTypes.faceID:
return MethodTypeCard(
assetPath: 'assets/images/login/verify_face.svg',
onTap: () => {widget.authenticateUser!(AuthMethodTypes.faceID, true)},
label: LocaleKeys.verifyThroughFace.tr(),
);
default:
return MethodTypeCard(
assetPath: 'assets/images/login/more_icon.png',
onTap: () {
widget.onShowMore!();
},
isSvg: false,
label: LocaleKeys.moreVerificationOpts.tr(),
height: 0,
);
}
}
}
class MethodTypeCard extends StatelessWidget {
const MethodTypeCard({
Key? key,
required this.assetPath,
required this.onTap,
required this.label,
this.height = 20,
this.isSvg = true,
}) : super(key: key);
final String assetPath;
final GestureTapCallback onTap;
final String label;
final double height;
final bool isSvg;
@override
Widget build(BuildContext context) {
double cardHeight = SizeConfig.heightMultiplier! *
(SizeConfig.isHeightVeryShort
? 22
: SizeConfig.isHeightLarge
? 25
: 20);
return InkWell(
onTap: onTap,
child: Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(10)),
border: Border.all(color: HexColor('#707070'), width: 0.1),
),
height: cardHeight,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
isSvg
? SvgPicture.asset(assetPath, width: SizeConfig.widthMultiplier! * (14), height: cardHeight * 0.20)
: Image.asset(
assetPath,
width: SizeConfig.widthMultiplier! * (12),
height: cardHeight * 0.35,
// height: ,
),
SizedBox(
height: height,
),
Text(
label,
style: TextStyle(
fontSize: SizeConfig.getTextMultiplierBasedOnWidth() * (SizeConfig.isHeightVeryShort ? 3 : 3.7),
color: MyColors.darkTextColor,
fontWeight: FontWeight.w600,
letterSpacing: -0.48,
),
)
],
),
),
)),
);
}
}

@ -147,6 +147,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.1"
get_it:
dependency: "direct main"
description:
name: get_it
url: "https://pub.dartlang.org"
source: hosted
version: "7.2.0"
hexcolor:
dependency: "direct main"
description:
name: hexcolor
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
http:
dependency: "direct main"
description:

@ -50,6 +50,9 @@ dependencies:
shimmer: ^2.0.0
sizer: ^2.0.15
local_auth: ^2.1.2
get_it: ^7.2.0
hexcolor: ^2.0.4
dev_dependencies:
flutter_test:

Loading…
Cancel
Save