From ea15bd915222d1b29fe493ea699e05b0c931ba36 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Mon, 1 Sep 2025 17:01:38 +0300 Subject: [PATCH 01/16] bottom sheet --- .gitignore | 1 + assets/langs/ar-SA.json | 9 +- assets/langs/en-US.json | 7 +- lib/extensions/string_extensions.dart | 10 +- lib/generated/locale_keys.g.dart | 5 + lib/presentation/authantication/login.dart | 229 +++++++++++------- lib/theme/colors.dart | 26 +- .../bottomsheet/generic_bottom_sheet.dart | 145 +++++++++++ 8 files changed, 321 insertions(+), 111 deletions(-) create mode 100644 lib/widgets/bottomsheet/generic_bottom_sheet.dart diff --git a/.gitignore b/.gitignore index 79c113f..3ada50c 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release +/android/ diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 8ff2eb4..361413e 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -776,5 +776,12 @@ "validPassportNumber": "يرجى إدخال رقم جواز سفر صالح", "continuePlan": "متابعة خطة العلاج؟", "aboutApp": "حول التطبيق", - "dontHaveAccount": "ليس لديك حساب؟" + "dontHaveAccount": "ليس لديك حساب؟", + "receiveOtpToast": "أين تود تلقي رمز التحقق OTP؟", + "enterPhoneNumber": "أدخل رقم الهاتف", + "enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي", + "enterPhoneDesc": "أدخل رقم هاتفك لتلقي رمز التحقق ", + "pleaseChooseOption": "الرجاء اختيار من الخيارات أدناه لتلقي رمز التحقق OTP" + + } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 423c67b..a8c263c 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -772,5 +772,10 @@ "aboutApp": "About the app", "aboutPoints": "Online Appointment Booking & rescheduling, Insurance approval status, Find A doctor, Ask your doctor, Medical prescriptions, Lab results, Hospitals contact numbers, Doctor profiles, Hospitals locations, Pharmacies Locations, Hospital's Virtual Tour, Official Social Media, Vaccines Schedule, Health Calculators, Other Services", "termsConditions": "These Online Services Terms of Use (Service Terms) govern certain online services provided by Dr Sulaiman Al Habib Medical Services Group Company (HMG, we, us, our)...", - "dontHaveAccount": "Don't have an account?" + "dontHaveAccount": "Don't have an account?", + "receiveOtpToast": "Where would you like to receive OTP?", + "enterPhoneNumber": "Enter Phone Number", + "enterEmailDesc": "Enter your email address to complete the process of creating a medical file", + "enterPhoneDesc": "Enter your phone number to receive OTP verification code", + "pleaseChooseOption": "Please select from the below options to receive OTP" } \ No newline at end of file diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 2e74d25..2722901 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -198,31 +198,31 @@ extension EmailValidator on String { Widget toText20({Color? color, bool isBold = false}) => Text( this, - style: TextStyle(fontSize: 20.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: 0.64), + style: TextStyle(fontSize: 20.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -1), ); Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( this, maxLines: maxlines, - style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.fSize, letterSpacing: 0.64, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)), + style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.fSize, letterSpacing: -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)), ); Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); Widget toText24({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); Widget toText44({Color? color, bool isBold = false}) => Text( diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index b2d7492..6b1ada7 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -775,5 +775,10 @@ abstract class LocaleKeys { static const continuePlan = 'continuePlan'; static const aboutApp = 'aboutApp'; static const dontHaveAccount = 'dontHaveAccount'; + static const receiveOtpToast = 'receiveOtpToast'; + static const enterPhoneNumber = 'enterPhoneNumber'; + static const enterEmailDesc = 'enterEmailDesc'; + static const enterPhoneDesc = 'enterPhoneDesc'; + static const pleaseChooseOption = 'pleaseChooseOption'; } diff --git a/lib/presentation/authantication/login.dart b/lib/presentation/authantication/login.dart index 150d446..84e1da3 100644 --- a/lib/presentation/authantication/login.dart +++ b/lib/presentation/authantication/login.dart @@ -3,16 +3,16 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; -import 'package:hmg_patient_app_new/widgets/language_switcher.dart'; -import 'package:sizer/sizer.dart'; // Import sizer class LoginScreen extends StatefulWidget { @override @@ -32,98 +32,145 @@ class _LoginScreen extends State { @override Widget build(BuildContext context) { - return Sizer(// Wrap with Sizer - builder: (context, orientation, deviceType) { - return Scaffold( - appBar: CustomAppBar( - onBackPressed: () { - // Navigator.of(context).pop(); - }, - onLanguageChanged: (String value) { - print(value); - context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); - }, - ), - body: GestureDetector( - onTap: () { - FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside - }, - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.only(left: 6.w, right: 6.w), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.w, height: 22.h, repeat: true, fit: BoxFit.cover), - SizedBox(height: 19.h), // Adjusted to sizer unit - Text( - LocaleKeys.welcomeToDrSulaiman.tr(), - style: context.dynamicTextStyle( - fontSize: 22.sp, - fontWeight: FontWeight.w600, - color: AppColors.textColor, - letterSpacing: -0.4, - height: 40 / 28, - ), - ), - SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px) - TextInputWidget( - labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", - hintText: "xxxxxxxxx", - controller: TextEditingController(), - keyboardType: TextInputType.number, - isEnable: true, - prefix: null, - autoFocus: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - padding: EdgeInsets.symmetric(vertical: 1.h, horizontal: 2.w), - leadingIcon: AppAssets.student_card, - errorMessage: "Please enter a valid national ID or file number", - hasError: true, - ), - SizedBox(height: 2.h), // Adjusted to sizer unit (approx 16px) - CustomButton( - text: LocaleKeys.login.tr(), - icon: AppAssets.login1, - iconColor: Colors.white, - onPressed: () {}, - ), - SizedBox(height: 1.8.h), // Adjusted to sizer unit (approx 14px) - Center( - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - style: context.dynamicTextStyle( - color: Colors.black, - fontSize: 14.sp, // Adjusted to sizer unit - height: 26 / 16, // This height is a ratio, may need re-evaluation - fontWeight: FontWeight.w500, - ), - children: [ - TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()), - TextSpan(text: " "), - TextSpan( - text: LocaleKeys.registernow.tr(), - style: context.dynamicTextStyle( - color: AppColors.primaryRedColor, - fontSize: 14.sp, // Adjusted to sizer unit - height: 26 / 16, // Ratio - fontWeight: FontWeight.w500, - ), - recognizer: TapGestureRecognizer()..onTap = () {}, - ), - ], + return Scaffold( + appBar: CustomAppBar( + onBackPressed: () { + // Navigator.of(context).pop(); + }, + onLanguageChanged: (String value) { + print(value); + context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); + }, + ), + body: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside + }, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.only(left: 6.h, right: 6.h), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.h, height: 22.h, repeat: true, fit: BoxFit.cover), + SizedBox(height: 19.h), // Adjusted to sizer unit + LocaleKeys.welcomeToDrSulaiman.tr().toText24(), + SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px) + TextInputWidget( + labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", + hintText: "xxxxxxxxx", + controller: TextEditingController(), + keyboardType: TextInputType.number, + isEnable: true, + prefix: null, + autoFocus: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + padding: EdgeInsets.symmetric(vertical: 1.h, horizontal: 2.h), + leadingIcon: AppAssets.student_card, + errorMessage: "Please enter a valid national ID or file number", + hasError: true, + ), + SizedBox(height: 2.h), // Adjusted to sizer unit (approx 16px) + CustomButton( + text: LocaleKeys.login.tr(), + icon: AppAssets.login1, + iconColor: Colors.white, + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + enableDrag: false, + builder: (bottomSheetContext) => StatefulBuilder( + builder: (BuildContext context, StateSetter setModalState) { + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + countryCode: "selectedCountry.countryCode", + initialPhoneNumber: "", + textController: TextEditingController(), + isEnableCountryDropdown: true, + onCountryChange: (value) {}, + onChange: (String? value) {}, + buttons: [ + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: CustomButton( + text: "TranslationBase.of(context).sendOTPSMS", + onPressed: () {}, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: "LocaleKeys.oR.tr()".toText16(), + ), + ], + ), + Padding( + padding: const EdgeInsets.only(bottom: 10, top: 10), + child: CustomButton( + text: "LocaleKeys.sendOTPWHATSAPP.tr(),", + onPressed: () {}, + backgroundColor: Colors.white, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.textColor, + icon: AppAssets.whatsapp, + ), + ), + ], + ), + )); + }, ), - ).withVerticalPadding(2.h), // Adjusted to sizer unit - ), - ], - ), + ); + }, + ), + SizedBox(height: 1.8.h), // Adjusted to sizer unit (approx 14px) + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: context.dynamicTextStyle( + color: Colors.black, + fontSize: 14.fSize, // Adjusted to sizer unit + height: 26 / 16, // This height is a ratio, may need re-evaluation + fontWeight: FontWeight.w500, + ), + children: [ + TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()), + TextSpan(text: " "), + TextSpan( + text: LocaleKeys.registernow.tr(), + style: context.dynamicTextStyle( + color: AppColors.primaryRedColor, + fontSize: 14.fSize, // Adjusted to sizer unit + height: 26 / 16, // Ratio + fontWeight: FontWeight.w500, + ), + recognizer: TapGestureRecognizer()..onTap = () {}, + ), + ], + ), + ).withVerticalPadding(2.h), // Adjusted to sizer unit + ), + ], ), ), ), - ); - }); + ), + ); } } diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 4987ffe..d20c5f2 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -8,7 +8,6 @@ class AppColors { static const buttonColor = Color(0xFF6A46F5); static const splashBgColor = Color(0xFF3C355D); - static const blackColor = Color(0xFF000000); static const lightGray = Color(0xFFF4F5F7); static const lightPurple = Color(0xFFB7A3E6); static const scaffoldBgColor = Color(0xFFF8F8F8); @@ -28,19 +27,20 @@ class AppColors { static const Color bgGreenColor = Color(0xFF18C273); static const Color textColor = Color(0xFF2E3039); static const Color borderOnlyColor = Color(0xFF2E3039); + static const blackColor = textColor; //Chips -static const Color successColor = Color(0xff18C273); -static const Color errorColor = Color(0xFFED1C2B); -static const Color alertColor = Color(0xFFD48D05); -static const Color infoColor = Color(0xFF0B85F7); -static const Color warningColor = Color(0xFFFFCC00); -static const Color greyColor = Color(0xFFEFEFF0); + static const Color successColor = Color(0xff18C273); + static const Color errorColor = Color(0xFFED1C2B); + static const Color alertColor = Color(0xFFD48D05); + static const Color infoColor = Color(0xFF0B85F7); + static const Color warningColor = Color(0xFFFFCC00); + static const Color greyColor = Color(0xFFEFEFF0); -static const Color successLightColor = Color(0xFF18C27326); -static const Color errorLightColor = Color(0xFFED1C2B1A); -static const Color alertLightColor = Color(0xFFD48D0526); -static const Color infoLightColor = Color(0xFF0B85F726); -static const Color warningLightColor = Color(0xFFFFCC0026); -static const Color greyLightColor = Color(0xFFEFEFF026); + static const Color successLightColor = Color(0xFF18C27326); + static const Color errorLightColor = Color(0xFFED1C2B1A); + static const Color alertLightColor = Color(0xFFD48D0526); + static const Color infoLightColor = Color(0xFF0B85F726); + static const Color warningLightColor = Color(0xFFFFCC0026); + static const Color greyLightColor = Color(0xFFEFEFF026); } diff --git a/lib/widgets/bottomsheet/generic_bottom_sheet.dart b/lib/widgets/bottomsheet/generic_bottom_sheet.dart new file mode 100644 index 0000000..13a02a5 --- /dev/null +++ b/lib/widgets/bottomsheet/generic_bottom_sheet.dart @@ -0,0 +1,145 @@ +import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; + +class GenericBottomSheet extends StatefulWidget { + String? countryCode; + String? initialPhoneNumber; + final List buttons; + TextEditingController? textController; + final bool isForEmail; + Function(Country)? onCountryChange; + final bool isEnableCountryDropdown; + final bool isFromSavedLogin; + Function(String?)? onChange; + // FocusNode myFocusNode; + + GenericBottomSheet( + {this.countryCode = "", + this.initialPhoneNumber = "", + required this.buttons, + this.textController, + this.isForEmail = false, + this.onCountryChange, + this.isEnableCountryDropdown = false, + this.isFromSavedLogin = false, + this.onChange, + // required this.myFocusNode + }); + + @override + _GenericBottomSheetState createState() => _GenericBottomSheetState(); +} + +class _GenericBottomSheetState extends State { + @override + void initState() { + super.initState(); + + if (!widget.isForEmail) { + widget.textController = TextEditingController(text: widget.initialPhoneNumber); + } + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + top: false, + bottom: Platform.isIOS ? false : true, + child: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + }, + child: Directionality( + textDirection: Directionality.of(context), + child: Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: Color(0xFFF8F8FA), + borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Title + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: widget.isFromSavedLogin + ? LocaleKeys.receiveOtpToast.tr().toText24() + : widget.isForEmail + ? LocaleKeys.enterEmail.tr().toText24() + : LocaleKeys.enterPhoneNumber.tr().toText24()), + InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Padding( + padding: const EdgeInsets.only(top: 10), + child: Utils.buildSvgWithAssets( + icon: AppAssets.cross_circle, + ), + )), + ], + ), + const SizedBox(height: 8), + // Subtitle + widget.isFromSavedLogin + ? LocaleKeys.pleaseChooseOption.tr().toText16() + : widget.isForEmail + ? LocaleKeys.enterEmailDesc.tr().toText16() + : LocaleKeys.enterPhoneDesc.tr().toText16(), + + if (widget.isFromSavedLogin) + ...[] + else ...[ + widget.textController != null + ? TextInputWidget( + labelText: widget.isForEmail ? LocaleKeys.email : LocaleKeys.phoneNumber, + hintText: widget.isForEmail ? "demo@gmail.com" : "5xxxxxxxx", + controller: widget.textController!, + padding: EdgeInsets.only(top: 8, bottom: 8, left: 8, right: 8), + keyboardType: widget.isForEmail ? TextInputType.emailAddress : TextInputType.number, + onChange: (value) { + widget.textController!.text = value!; + if (widget.onChange != null) { + widget.onChange!(value); + } + }, + isEnable: true, + // focusNode: widget.myFocusNode, + isReadOnly: widget.isFromSavedLogin, + prefix: widget.isForEmail ? null : widget.countryCode, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isCountryDropDown: widget.isEnableCountryDropdown, + leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone, + ) + : SizedBox(), + ], + + SizedBox(height: 24), + ...widget.buttons, + ], + ), + ), + ), + ), + ); + } +} From a4da9db6c371f2ce504c734ffb1f905c19cf8f87 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Tue, 2 Sep 2025 08:55:19 +0300 Subject: [PATCH 02/16] register & changes --- lib/extensions/string_extensions.dart | 27 +- lib/main.dart | 7 +- lib/presentation/authantication/login.dart | 2 +- lib/presentation/authantication/register.dart | 385 ++++++++++++++++++ 4 files changed, 405 insertions(+), 16 deletions(-) create mode 100644 lib/presentation/authantication/register.dart diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 2722901..1fdd9e7 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -165,18 +165,15 @@ extension EmailValidator on String { int? maxlines, TextAlign? textAlign, }) => - Text( - this, - maxLines: maxlines, - textAlign: isCenter ? TextAlign.center : null, - style: TextStyle( - color: color ?? AppColors.blackColor, - fontSize: 16.fSize, - letterSpacing: 0.64, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal, - decoration: isUnderLine ? TextDecoration.underline : null, - ), - ); + Text(this, + maxLines: maxlines, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle( + color: color ?? AppColors.blackColor, + fontSize: 16.fSize, + letterSpacing: 0.64, + fontWeight: isBold ? FontWeight.bold : FontWeight.normal, + decoration: isUnderLine ? TextDecoration.underline : null)); Widget toText17({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, @@ -219,6 +216,12 @@ extension EmailValidator on String { style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); + Widget toText28({Color? color, bool isBold = false, bool isCenter = false}) => Text( + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); + Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, diff --git a/lib/main.dart b/lib/main.dart index 1996653..ee8beaa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/utils/size_config.dart'; import 'package:hmg_patient_app_new/providers/authentication_view_model.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/theme/app_theme.dart'; @@ -77,20 +78,20 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( - top: false, // Set to true if you want to avoid the notch area as well + top: false, bottom: Platform.isIOS ? false : true, child: LayoutBuilder( builder: (context, constraints) { return Sizer( builder: (context, orientation, deviceType) { - // SizeConfig().init(constraints, orientation); + SizeConfig().init(constraints, orientation); return MaterialApp( title: 'Dr. AlHabib', builder: (context, mchild) { return MediaQuery( data: MediaQuery.of(context).copyWith( textScaler: TextScaler.linear(1.0), - ), //set desired text scale factor here + ), child: mchild!); }, showSemanticsDebugger: false, diff --git a/lib/presentation/authantication/login.dart b/lib/presentation/authantication/login.dart index 84e1da3..ac599b0 100644 --- a/lib/presentation/authantication/login.dart +++ b/lib/presentation/authantication/login.dart @@ -54,7 +54,7 @@ class _LoginScreen extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.h, height: 22.h, repeat: true, fit: BoxFit.cover), - SizedBox(height: 19.h), // Adjusted to sizer unit + SizedBox(height: 20.h), // Adjusted to sizer unit LocaleKeys.welcomeToDrSulaiman.tr().toText24(), SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px) TextInputWidget( diff --git a/lib/presentation/authantication/register.dart b/lib/presentation/authantication/register.dart new file mode 100644 index 0000000..bd4cb78 --- /dev/null +++ b/lib/presentation/authantication/register.dart @@ -0,0 +1,385 @@ +import 'dart:convert'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:provider/provider.dart'; + +class Register extends StatefulWidget { + final Function? changePageViewIndex; + + const Register({Key? key, this.changePageViewIndex}) : super(key: key); + + @override + _Register createState() => _Register(); +} + +class _Register extends State { + @override + void initState() { + getPatientOccupationList(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + onBackPressed: () {}, + onLanguageChanged: (String value) {}, + ), + body: Column( + children: [ + Expanded( + child: ListView( + padding: EdgeInsets.all(21), + physics: BouncingScrollPhysics(), + children: [ + SizedBox(height: 10), + Padding( + padding: EdgeInsets.all(10), + child: Text( + LocaleKeys.enterNationalId.tr(), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 23 / 16), + )), + SizedBox(height: 10), + PhoneNumberSelectorWidget(onNumberChange: (value) => {}, onCountryChange: (value) => ), + SizedBox(height: 12), + TextInputWidget(labelText: LocaleKeys.nationalIdNumber.tr(), hintText: "Xxxxxxxxx", controller: TextEditingController()), + SizedBox(height: 20), + Row( + children: [ + Expanded( + child: Row( + children: [ + Radio( + value: 1, + groupValue: isHijri, + onChanged: (value) { + }, + ), + Text(LocaleKeys.hijriDate.tr()), + ], + ), + ), + Expanded( + child: Row( + children: [ + Radio( + value: 0, + groupValue: isHijri, + onChanged: (value) { + }, + ), + Text(LocaleKeys.gregorianDate.tr()), + ], + ), + ), + ], + ), + Row(children: [ + Container( + width: SizeConfig.realScreenWidth! * .89, + child: isHijri == 1 + ? Directionality( + textDirection: TextDirection.ltr, + child: inputWidget(TranslationBase.of(context).dob, "DD/MM/YYYYY", dob, + isNumber: false, + suffix: Icon( + Icons.calendar_today, + size: 16, + ))) + : Container( + child: InkWell( + onTap: () { + if (isHijri != null) _selectDate(context); + }, + child: Directionality( + textDirection: TextDirection.ltr, + child: inputWidget(TranslationBase.of(context).dob, "DD/MM/YYYYY", dobEn, + isNumber: false, + isEnable: false, + suffix: Icon( + Icons.calendar_today, + size: 16, + )))))), + ]) + ], + ), + ), + Container( + width: double.maxFinite, + // height: 80.0, + color: Colors.white, + // margin: EdgeInsets.only(bottom: 50.0), + child: Row( + children: [ + Expanded( + child: Padding( + padding: EdgeInsets.all(10), + child: DefaultButton(TranslationBase.of(context).cancel, () { + Navigator.of(context).pop(); + locator().loginRegistration.registration_cancel(step: 'enter details'); + }, textColor: Colors.white, color: Color(0xffD02127))), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all(10), + child: DefaultButton(TranslationBase.of(context).next, () { + startRegistration(); + locator().loginRegistration.registration_enter_details(); + }, textColor: Colors.white, color: isButtonDisabled == true ? Colors.grey : Color(0xff359846))), + ), + ], + ), + ) + ], + ), + ); + } + + Future _selectDate(BuildContext context) async { + DatePicker.showDatePicker( + context, + showTitleActions: true, + minTime: DateTime(DateTime.now().year - 100, 1, 1), + maxTime: DateTime.now(), + onConfirm: (date) { + selectedDate = date; + setState(() { + final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); + dobEn.text = dateFormat.format(date); + }); + }, + currentTime: DateTime.now(), + ); + } + + Widget inputWidget(String _labelText, String _hintText, TextEditingController _controller, {String? prefix, bool isEnable = true, bool hasSelection = false, bool isNumber = true, Icon? suffix}) { + return Container( + padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.white, + border: Border.all( + color: Color(0xffefefef), + width: 1, + ), + ), + child: InkWell( + onTap: hasSelection ? () {} : null, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _labelText, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: Color(0xff2B353E), + letterSpacing: -0.44, + ), + ), + TextField( + enabled: isEnable, + scrollPadding: EdgeInsets.zero, + keyboardType: isNumber ? TextInputType.numberWithOptions(signed: true) : TextInputType.datetime, + controller: _controller, + onChanged: (value) => {validateForm()}, + style: TextStyle( + fontSize: 14, + height: 21 / 14, + fontWeight: FontWeight.w400, + color: Color(0xff2B353E), + letterSpacing: -0.44, + ), + decoration: InputDecoration( + isDense: true, + hintText: _hintText, + hintStyle: TextStyle( + fontSize: 14, + height: 21 / 14, + fontWeight: FontWeight.w400, + color: Color(0xff575757), + letterSpacing: -0.56, + ), + prefixIconConstraints: BoxConstraints(minWidth: 50), + prefixIcon: prefix == null + ? null + : Text( + "+" + prefix, + style: TextStyle( + fontSize: 14, + height: 21 / 14, + fontWeight: FontWeight.w500, + color: Color(0xff2E303A), + letterSpacing: -0.56, + ), + ), + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ], + ), + ), + if (hasSelection) Icon(Icons.keyboard_arrow_down_outlined), + if (suffix != null) suffix + ], + ), + ), + ); + } + + startRegistration() { + final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); + if (isButtonDisabled == false) { + var request = CheckPatientForRegistration(); + request.patientMobileNumber = int.parse(mobileNo); + request.zipCode = countryCode; + request.patientOutSA = countryCode == '966' ? 0 : 1; + + request.patientIdentificationID = int.parse(nationalIDorFile.text); + request.patientID = 0; + request.isRegister = true; + request.dob = isHijri == 1 ? dob.text : dateFormat.format(selectedDate); + request.isHijri = isHijri; + this.checkPatientForRegisteration(request); + } + } + + checkPatientForRegisteration(request) { + int languageID = Provider.of(context, listen: false).isArabic ? 1 : 2; + GifLoaderDialogUtils.showMyDialog(context); + this.authService.checkPatientForRegisteration(request, languageID).then((response) => {checkUserStatus(response, request)}).catchError((err) { + GifLoaderDialogUtils.hideDialog(context); + ConfirmDialog dialog = new ConfirmDialog( + context: context, + confirmMessage: err, + okText: TranslationBase.of(context).confirm, + cancelText: TranslationBase.of(context).cancel_nocaps, + okFunction: () => { + ConfirmDialog.closeAlertDialog(context), + Navigator.of(context).push(FadePage(page: Register())), + }, + cancelFunction: () => {ConfirmDialog.closeAlertDialog(context)}); + dialog.showAlertDialog(context); + }); + } + + void validateForm() { + if (util.validateIDBox(nationalIDorFile.text, loginType) == true && + mobileNo.length >= 9 && + util.isSAUDIIDValid(nationalIDorFile.text, loginType) == true && + isHijri != null && + (dobEn.text != null || dob.text != null)) { + setState(() { + isButtonDisabled = false; + }); + } else { + setState(() { + isButtonDisabled = true; + }); + } + } + + checkUserStatus(response, CheckPatientForRegistration request) async { + GifLoaderDialogUtils.hideDialog(context); + if (response is Map) { + var nRequest = request.toJson(); + nRequest['LogInTokenID'] = response['LogInTokenID']; + if (response['hasFile'] == true) { + ConfirmDialog dialog = new ConfirmDialog( + context: context, + confirmMessage: response['ErrorEndUserMessage'], + okText: TranslationBase.of(context).ok, + cancelText: TranslationBase.of(context).cancel, + okFunction: () { + AlertDialogBox.closeAlertDialog(context); + sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, nRequest); + LoginRegistration.loginMethod = 1; // 1=NationalID, by default from Registration + Navigator.of(context).push(FadePage(page: Login())); + }, + cancelFunction: () {}) + .showAlertDialog(context); + } else { + final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); + nRequest['forRegister'] = true; + nRequest['isRegister'] = true; + nRequest["PatientIdentificationID"] = nRequest["PatientIdentificationID"].toString(); + nRequest['dob'] = isHijri == 1 ? dob.text : dateFormat.format(selectedDate); + nRequest['isHijri'] = isHijri; + sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, nRequest); + sharedPref.setString(LOGIN_TOKEN_ID, response['LogInTokenID']); + if (request.patientOutSA == 0) { + this.chekUserData(response['LogInTokenID']); + } else { + Navigator.of(context).push(FadePage(page: ConfirmLogin(changePageViewIndex: widget.changePageViewIndex, fromRegistration: true, isDubai: true))); + } + } + } else { + // if (response['ErrorCode'] == '-986') { + //AppToast.showErrorToast(message: response); + AlertDialogBox( + context: context, + confirmMessage: response, + okText: TranslationBase.of(context).ok, + okFunction: () { + AlertDialogBox.closeAlertDialog(context); + Navigator.of(context).pop(); + }).showAlertDialog(context); + //} + } + } + + chekUserData(loginToken) { + // let m = hijri(this.dateOfBirth).locale('en'); + // // const dateHijri = m.format('iDD/iMM/iYYYY'); + // const request = { + // PatientIdentificationID: this.id.toString(), + // // TokenID: token, + // DOB: this.dateOption === '1' ? this.dateOfBirth : moment(this.dateOfBirth).format('DD/MM/YYYY'), + // IsHijri: Number(this.dateOption) + // } + + final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); + GifLoaderDialogUtils.showMyDialog(context); + var request = CheckUserStatusRequest(); + request.patientIdentificationID = nationalIDorFile.text; + request.dOB = isHijri == 1 ? dob.text : dateFormat.format(selectedDate); + request.isHijri = isHijri; + request.patientOutSA = countryCode == '966' ? 0 : 1; + this.authService.checkUserStatus(request).then((result) => { + GifLoaderDialogUtils.hideDialog(context), + if (result is Map) + { + result = CheckUserStatusResponse.fromJson(result as Map), + sharedPref.setObject(NHIC_DATA, result), + // widget.changePageViewIndex!(1), + Navigator.of(context).push(FadePage(page: ConfirmLogin(changePageViewIndex: widget.changePageViewIndex, fromRegistration: true))), + } + else + { + AppToast.showErrorToast(message: result != null ? result : TranslationBase.of(context).somethingWentWrong), + } + }); + } + + getPatientOccupationList() async { + patientOccupationList.clear(); + await authService.getPatientOccupationList().then((result) { + sharedPref.setString(PATIENT_OCCUPATION_LIST, json.encode(result['GetOccupationLst'])); + }).catchError((err) { + AppToast.showErrorToast(message: err); + }); + } +} From 6f98690929142b52aa105ba14ffb0065db71640b Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Tue, 2 Sep 2025 09:29:59 +0300 Subject: [PATCH 03/16] register & changes --- assets/langs/ar-SA.json | 3 +- assets/langs/en-US.json | 3 +- lib/extensions/string_extensions.dart | 61 ++ lib/generated/locale_keys.g.dart | 3 +- lib/presentation/authantication/login.dart | 23 +- lib/presentation/authantication/register.dart | 964 +++++++++++------- lib/providers/authentication_view_model.dart | 5 +- 7 files changed, 660 insertions(+), 402 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 16373bf..ebcee14 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -782,7 +782,8 @@ "enterPhoneNumber": "أدخل رقم الهاتف", "enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي", "enterPhoneDesc": "أدخل رقم هاتفك لتلقي رمز التحقق ", - "pleaseChooseOption": "الرجاء اختيار من الخيارات أدناه لتلقي رمز التحقق OTP" + "pleaseChooseOption": "الرجاء اختيار من الخيارات أدناه لتلقي رمز التحقق OTP", + "prepareToElevate": "هل أنت مستعد لتحسين صحتك ورفاهتك؟" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 1c78177..b45c697 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -778,5 +778,6 @@ "enterPhoneDesc": "Enter your phone number to receive OTP verification code", "pleaseChooseOption": "Please select from the below options to receive OTP" "dontHaveAccount": "Don't have an account?", - "loginOrRegister": "Login or Register" + "loginOrRegister": "Login or Register", + "prepareToElevate": "Prepared to elevate your health and well-being?" } \ No newline at end of file diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index b9d4ba8..ec9ff99 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -1,3 +1,4 @@ +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/cupertino.dart'; @@ -219,6 +220,13 @@ extension EmailValidator on String { style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); + Widget toText28({Color? color, bool isBold = false, bool isCenter = false, TextScaler? textScaler}) => Text( + this, + textAlign: isCenter ? TextAlign.center : null, + textScaler: textScaler, + style: TextStyle(height: 40 / 28, color: color ?? AppColors.blackColor, fontSize: 28.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); + Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, @@ -365,3 +373,56 @@ class FontUtils { return isArabic ? 'Cairo' : 'Poppins'; } } + + +extension CountryExtension on Country { + String get displayName { + switch (this) { + case Country.saudiArabia: + return "Kingdom Of Saudi Arabia"; + case Country.unitedArabEmirates: + return "United Arab Emirates"; + } + } + + String get nameArabic { + switch (this) { + case Country.saudiArabia: + return "المملكة العربية السعودية"; + case Country.unitedArabEmirates: + return "الإمارات العربية المتحدة"; + } + } + + String get iconPath { + switch (this) { + case Country.saudiArabia: + return "assets/images/svg/ksa.svg"; + case Country.unitedArabEmirates: + return "assets/images/svg/uae.svg"; + } + } + + String get countryCode { + switch (this) { + case Country.saudiArabia: + return "966"; + case Country.unitedArabEmirates: + return "971"; + } + } + + static Country fromDisplayName(String name) { + switch (name) { + case "Kingdom Of Saudi Arabia": + case "المملكة العربية السعودية": + return Country.saudiArabia; + case "United Arab Emirates": + case "الإمارات العربية المتحدة": + return Country.unitedArabEmirates; + default: + throw Exception("Invalid country name"); + } + } +} + diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index e818aa4..819fb22 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -774,12 +774,13 @@ abstract class LocaleKeys { static const validPassportNumber = 'validPassportNumber'; static const continuePlan = 'continuePlan'; static const aboutApp = 'aboutApp'; + static const loginOrRegister = 'loginOrRegister'; static const dontHaveAccount = 'dontHaveAccount'; static const receiveOtpToast = 'receiveOtpToast'; static const enterPhoneNumber = 'enterPhoneNumber'; static const enterEmailDesc = 'enterEmailDesc'; static const enterPhoneDesc = 'enterPhoneDesc'; static const pleaseChooseOption = 'pleaseChooseOption'; - static const loginOrRegister = 'loginOrRegister'; + static const prepareToElevate = 'prepareToElevate'; } diff --git a/lib/presentation/authantication/login.dart b/lib/presentation/authantication/login.dart index 3e9273b..3ac0d63 100644 --- a/lib/presentation/authantication/login.dart +++ b/lib/presentation/authantication/login.dart @@ -3,16 +3,16 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; -import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart' hide Sizer; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; -import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:sizer/sizer.dart'; class LoginScreen extends StatefulWidget { @override @@ -41,8 +41,7 @@ class _LoginScreen extends State { // Navigator.of(context).pop(); }, onLanguageChanged: (String value) { - print(value); - context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); + // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); }, ), body: GestureDetector( @@ -51,13 +50,13 @@ class _LoginScreen extends State { }, child: SingleChildScrollView( child: Padding( - padding: EdgeInsets.only(left: 6.w, right: 6.w), + padding: EdgeInsets.only(left: 6.sp, right: 6.sp), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.w, height: 22.h, repeat: true, fit: BoxFit.cover), - SizedBox(height: 19.h), // Adjusted to sizer unit + Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.w, height: 22.sp, repeat: true, fit: BoxFit.cover), + SizedBox(height: 19.sp), // Adjusted to sizer unit LocaleKeys.welcomeToDrSulaiman.tr().toText22(isBold: true, color: AppColors.textColor), // Text( // LocaleKeys.welcomeToDrSulaiman.tr(), @@ -69,7 +68,7 @@ class _LoginScreen extends State { // height: 40 / 28, // ), // ), - SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px) + SizedBox(height: 4.sp), // Adjusted to sizer unit (approx 32px) TextInputWidget( labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", hintText: "xxxxxxxxx", @@ -80,19 +79,19 @@ class _LoginScreen extends State { autoFocus: true, isBorderAllowed: false, isAllowLeadingIcon: true, - padding: EdgeInsets.symmetric(vertical: 1.h, horizontal: 2.w), + padding: EdgeInsets.symmetric(vertical: 1.sp, horizontal: 2.w), leadingIcon: AppAssets.student_card, errorMessage: "Please enter a valid national ID or file number", hasError: true, ), - SizedBox(height: 2.h), // Adjusted to sizer unit (approx 16px) + SizedBox(height: 2.sp), // Adjusted to sizer unit (approx 16px) CustomButton( text: LocaleKeys.login.tr(), icon: AppAssets.login1, iconColor: Colors.white, onPressed: () {}, ), - SizedBox(height: 1.8.h), // Adjusted to sizer unit (approx 14px) + SizedBox(height: 2.sp), // Adjusted to sizer unit (approx 14px) Center( child: RichText( textAlign: TextAlign.center, @@ -118,7 +117,7 @@ class _LoginScreen extends State { ), ], ), - ).withVerticalPadding(2.h), // Adjusted to sizer unit + ).withVerticalPadding(2.sp), // Adjusted to sizer unit ), ], ), diff --git a/lib/presentation/authantication/register.dart b/lib/presentation/authantication/register.dart index bd4cb78..9b900b5 100644 --- a/lib/presentation/authantication/register.dart +++ b/lib/presentation/authantication/register.dart @@ -1,385 +1,579 @@ -import 'dart:convert'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; -import 'package:hmg_patient_app_new/widgets/input_widget.dart'; -import 'package:provider/provider.dart'; - -class Register extends StatefulWidget { - final Function? changePageViewIndex; - - const Register({Key? key, this.changePageViewIndex}) : super(key: key); - - @override - _Register createState() => _Register(); -} - -class _Register extends State { - @override - void initState() { - getPatientOccupationList(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: CustomAppBar( - onBackPressed: () {}, - onLanguageChanged: (String value) {}, - ), - body: Column( - children: [ - Expanded( - child: ListView( - padding: EdgeInsets.all(21), - physics: BouncingScrollPhysics(), - children: [ - SizedBox(height: 10), - Padding( - padding: EdgeInsets.all(10), - child: Text( - LocaleKeys.enterNationalId.tr(), - style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 23 / 16), - )), - SizedBox(height: 10), - PhoneNumberSelectorWidget(onNumberChange: (value) => {}, onCountryChange: (value) => ), - SizedBox(height: 12), - TextInputWidget(labelText: LocaleKeys.nationalIdNumber.tr(), hintText: "Xxxxxxxxx", controller: TextEditingController()), - SizedBox(height: 20), - Row( - children: [ - Expanded( - child: Row( - children: [ - Radio( - value: 1, - groupValue: isHijri, - onChanged: (value) { - }, - ), - Text(LocaleKeys.hijriDate.tr()), - ], - ), - ), - Expanded( - child: Row( - children: [ - Radio( - value: 0, - groupValue: isHijri, - onChanged: (value) { - }, - ), - Text(LocaleKeys.gregorianDate.tr()), - ], - ), - ), - ], - ), - Row(children: [ - Container( - width: SizeConfig.realScreenWidth! * .89, - child: isHijri == 1 - ? Directionality( - textDirection: TextDirection.ltr, - child: inputWidget(TranslationBase.of(context).dob, "DD/MM/YYYYY", dob, - isNumber: false, - suffix: Icon( - Icons.calendar_today, - size: 16, - ))) - : Container( - child: InkWell( - onTap: () { - if (isHijri != null) _selectDate(context); - }, - child: Directionality( - textDirection: TextDirection.ltr, - child: inputWidget(TranslationBase.of(context).dob, "DD/MM/YYYYY", dobEn, - isNumber: false, - isEnable: false, - suffix: Icon( - Icons.calendar_today, - size: 16, - )))))), - ]) - ], - ), - ), - Container( - width: double.maxFinite, - // height: 80.0, - color: Colors.white, - // margin: EdgeInsets.only(bottom: 50.0), - child: Row( - children: [ - Expanded( - child: Padding( - padding: EdgeInsets.all(10), - child: DefaultButton(TranslationBase.of(context).cancel, () { - Navigator.of(context).pop(); - locator().loginRegistration.registration_cancel(step: 'enter details'); - }, textColor: Colors.white, color: Color(0xffD02127))), - ), - Expanded( - child: Padding( - padding: EdgeInsets.all(10), - child: DefaultButton(TranslationBase.of(context).next, () { - startRegistration(); - locator().loginRegistration.registration_enter_details(); - }, textColor: Colors.white, color: isButtonDisabled == true ? Colors.grey : Color(0xff359846))), - ), - ], - ), - ) - ], - ), - ); - } - - Future _selectDate(BuildContext context) async { - DatePicker.showDatePicker( - context, - showTitleActions: true, - minTime: DateTime(DateTime.now().year - 100, 1, 1), - maxTime: DateTime.now(), - onConfirm: (date) { - selectedDate = date; - setState(() { - final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); - dobEn.text = dateFormat.format(date); - }); - }, - currentTime: DateTime.now(), - ); - } - - Widget inputWidget(String _labelText, String _hintText, TextEditingController _controller, {String? prefix, bool isEnable = true, bool hasSelection = false, bool isNumber = true, Icon? suffix}) { - return Container( - padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - color: Colors.white, - border: Border.all( - color: Color(0xffefefef), - width: 1, - ), - ), - child: InkWell( - onTap: hasSelection ? () {} : null, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _labelText, - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Color(0xff2B353E), - letterSpacing: -0.44, - ), - ), - TextField( - enabled: isEnable, - scrollPadding: EdgeInsets.zero, - keyboardType: isNumber ? TextInputType.numberWithOptions(signed: true) : TextInputType.datetime, - controller: _controller, - onChanged: (value) => {validateForm()}, - style: TextStyle( - fontSize: 14, - height: 21 / 14, - fontWeight: FontWeight.w400, - color: Color(0xff2B353E), - letterSpacing: -0.44, - ), - decoration: InputDecoration( - isDense: true, - hintText: _hintText, - hintStyle: TextStyle( - fontSize: 14, - height: 21 / 14, - fontWeight: FontWeight.w400, - color: Color(0xff575757), - letterSpacing: -0.56, - ), - prefixIconConstraints: BoxConstraints(minWidth: 50), - prefixIcon: prefix == null - ? null - : Text( - "+" + prefix, - style: TextStyle( - fontSize: 14, - height: 21 / 14, - fontWeight: FontWeight.w500, - color: Color(0xff2E303A), - letterSpacing: -0.56, - ), - ), - contentPadding: EdgeInsets.zero, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - ), - ), - ], - ), - ), - if (hasSelection) Icon(Icons.keyboard_arrow_down_outlined), - if (suffix != null) suffix - ], - ), - ), - ); - } - - startRegistration() { - final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); - if (isButtonDisabled == false) { - var request = CheckPatientForRegistration(); - request.patientMobileNumber = int.parse(mobileNo); - request.zipCode = countryCode; - request.patientOutSA = countryCode == '966' ? 0 : 1; - - request.patientIdentificationID = int.parse(nationalIDorFile.text); - request.patientID = 0; - request.isRegister = true; - request.dob = isHijri == 1 ? dob.text : dateFormat.format(selectedDate); - request.isHijri = isHijri; - this.checkPatientForRegisteration(request); - } - } - - checkPatientForRegisteration(request) { - int languageID = Provider.of(context, listen: false).isArabic ? 1 : 2; - GifLoaderDialogUtils.showMyDialog(context); - this.authService.checkPatientForRegisteration(request, languageID).then((response) => {checkUserStatus(response, request)}).catchError((err) { - GifLoaderDialogUtils.hideDialog(context); - ConfirmDialog dialog = new ConfirmDialog( - context: context, - confirmMessage: err, - okText: TranslationBase.of(context).confirm, - cancelText: TranslationBase.of(context).cancel_nocaps, - okFunction: () => { - ConfirmDialog.closeAlertDialog(context), - Navigator.of(context).push(FadePage(page: Register())), - }, - cancelFunction: () => {ConfirmDialog.closeAlertDialog(context)}); - dialog.showAlertDialog(context); - }); - } - - void validateForm() { - if (util.validateIDBox(nationalIDorFile.text, loginType) == true && - mobileNo.length >= 9 && - util.isSAUDIIDValid(nationalIDorFile.text, loginType) == true && - isHijri != null && - (dobEn.text != null || dob.text != null)) { - setState(() { - isButtonDisabled = false; - }); - } else { - setState(() { - isButtonDisabled = true; - }); - } - } - - checkUserStatus(response, CheckPatientForRegistration request) async { - GifLoaderDialogUtils.hideDialog(context); - if (response is Map) { - var nRequest = request.toJson(); - nRequest['LogInTokenID'] = response['LogInTokenID']; - if (response['hasFile'] == true) { - ConfirmDialog dialog = new ConfirmDialog( - context: context, - confirmMessage: response['ErrorEndUserMessage'], - okText: TranslationBase.of(context).ok, - cancelText: TranslationBase.of(context).cancel, - okFunction: () { - AlertDialogBox.closeAlertDialog(context); - sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, nRequest); - LoginRegistration.loginMethod = 1; // 1=NationalID, by default from Registration - Navigator.of(context).push(FadePage(page: Login())); - }, - cancelFunction: () {}) - .showAlertDialog(context); - } else { - final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); - nRequest['forRegister'] = true; - nRequest['isRegister'] = true; - nRequest["PatientIdentificationID"] = nRequest["PatientIdentificationID"].toString(); - nRequest['dob'] = isHijri == 1 ? dob.text : dateFormat.format(selectedDate); - nRequest['isHijri'] = isHijri; - sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, nRequest); - sharedPref.setString(LOGIN_TOKEN_ID, response['LogInTokenID']); - if (request.patientOutSA == 0) { - this.chekUserData(response['LogInTokenID']); - } else { - Navigator.of(context).push(FadePage(page: ConfirmLogin(changePageViewIndex: widget.changePageViewIndex, fromRegistration: true, isDubai: true))); - } - } - } else { - // if (response['ErrorCode'] == '-986') { - //AppToast.showErrorToast(message: response); - AlertDialogBox( - context: context, - confirmMessage: response, - okText: TranslationBase.of(context).ok, - okFunction: () { - AlertDialogBox.closeAlertDialog(context); - Navigator.of(context).pop(); - }).showAlertDialog(context); - //} - } - } - - chekUserData(loginToken) { - // let m = hijri(this.dateOfBirth).locale('en'); - // // const dateHijri = m.format('iDD/iMM/iYYYY'); - // const request = { - // PatientIdentificationID: this.id.toString(), - // // TokenID: token, - // DOB: this.dateOption === '1' ? this.dateOfBirth : moment(this.dateOfBirth).format('DD/MM/YYYY'), - // IsHijri: Number(this.dateOption) - // } - - final intl.DateFormat dateFormat = intl.DateFormat('dd/MM/yyyy'); - GifLoaderDialogUtils.showMyDialog(context); - var request = CheckUserStatusRequest(); - request.patientIdentificationID = nationalIDorFile.text; - request.dOB = isHijri == 1 ? dob.text : dateFormat.format(selectedDate); - request.isHijri = isHijri; - request.patientOutSA = countryCode == '966' ? 0 : 1; - this.authService.checkUserStatus(request).then((result) => { - GifLoaderDialogUtils.hideDialog(context), - if (result is Map) - { - result = CheckUserStatusResponse.fromJson(result as Map), - sharedPref.setObject(NHIC_DATA, result), - // widget.changePageViewIndex!(1), - Navigator.of(context).push(FadePage(page: ConfirmLogin(changePageViewIndex: widget.changePageViewIndex, fromRegistration: true))), - } - else - { - AppToast.showErrorToast(message: result != null ? result : TranslationBase.of(context).somethingWentWrong), - } - }); - } - - getPatientOccupationList() async { - patientOccupationList.clear(); - await authService.getPatientOccupationList().then((result) { - sharedPref.setString(PATIENT_OCCUPATION_LIST, json.encode(result['GetOccupationLst'])); - }).catchError((err) { - AppToast.showErrorToast(message: err); - }); - } -} +// import'dart:convert'; +// +// import 'package:easy_localization/easy_localization.dart'; +// import 'package:flutter/gestures.dart'; +// import 'package:flutter_svg/flutter_svg.dart'; +// import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart'; +// import 'package:hmg_patient_app/config/config.dart'; +// import 'package:hmg_patient_app/config/shared_pref_kay.dart'; +// import 'package:hmg_patient_app/config/size_config.dart'; +// import 'package:hmg_patient_app/core/service/client/base_app_client.dart'; +// import 'package:hmg_patient_app/core/viewModels/project_view_model.dart'; +// import 'package:hmg_patient_app/models/Appointments/toDoCountProviderModel.dart'; +// import 'package:hmg_patient_app/models/Authentication/check_activation_code_response.dart'; +// import 'package:hmg_patient_app/models/Authentication/check_paitent_authentication_req.dart'; +// import 'package:hmg_patient_app/models/Authentication/check_user_status_reponse.dart'; +// import 'package:hmg_patient_app/models/Authentication/check_user_status_req.dart'; +// import 'package:hmg_patient_app/models/Authentication/checkpatient_for_registration.dart'; +// import 'package:hmg_patient_app/models/Authentication/register_info_response.dart'; +// import 'package:hmg_patient_app/models/Authentication/send_activation_request.dart'; +// import 'package:hmg_patient_app/new_ui/new_ext.dart'; +// import 'package:hmg_patient_app/new_ui/otp/otp_validation_bootmsheet_widget.dart'; +// import 'package:hmg_patient_app/pages/AlHabibMedicalService/health_calculator/carbs/carbs.dart'; +// import 'package:hmg_patient_app/pages/login/login-type.dart'; +// import 'package:hmg_patient_app/pages/login/register-info.dart'; +// import 'package:hmg_patient_app/pages/login/register.dart'; +// import 'package:hmg_patient_app/pages/login/register_new_step_2.dart'; +// import 'package:hmg_patient_app/pages/login/user-login-agreement-page.dart'; +// import 'package:hmg_patient_app/pages/login/welcome.dart'; +// import 'package:hmg_patient_app/services/authentication/auth_provider.dart'; +// import 'package:hmg_patient_app/theme/colors.dart'; +// import 'package:hmg_patient_app/uitl/app_shared_preferences.dart'; +// import 'package:hmg_patient_app/uitl/app_toast.dart'; +// import 'package:hmg_patient_app/uitl/font_utils.dart'; +// import 'package:hmg_patient_app/uitl/gif_loader_dialog_utils.dart'; +// import 'package:hmg_patient_app/uitl/translations_delegate_base.dart'; +// import 'package:hmg_patient_app/uitl/utils.dart'; +// import 'package:hmg_patient_app/uitl/utils_new.dart'; +// import 'package:hmg_patient_app/widgets/drawer/langauge_picker.dart'; +// import 'package:hmg_patient_app/widgets/others/app_scaffold_widget.dart'; +// import 'package:flutter/material.dart'; +// import 'package:hmg_patient_app/widgets/otp/sms-popup.dart'; +// import 'package:hmg_patient_app/widgets/text/app_texts_widget.dart'; +// import 'package:hmg_patient_app_new/core/app_state.dart'; +// import 'package:hmg_patient_app_new/core/enums.dart'; +// import 'package:hmg_patient_app_new/core/utils/utils.dart'; +// import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +// import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +// import 'package:hmg_patient_app_new/widgets/dropdown_widget.dart'; +// import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +// import 'package:provider/provider.dart'; +// +// import '../../core/viewModels/appointment_rate_view_model.dart'; +// import '../../locator.dart'; +// import '../../models/Authentication/authenticated_user.dart'; +// import '../../models/Authentication/select_device_imei_res.dart'; +// import '../../models/InPatientServices/get_admission_info_response_model.dart'; +// import '../../models/InPatientServices/get_admission_request_info_response_model.dart'; +// import '../../new_ui/exception_widget/ExceptionBottomSheet.dart'; +// import '../../services/clinic_services/get_clinic_service.dart'; +// import '../../widgets/dialogs/alert_dialog.dart'; +// import '../../widgets/dialogs/confirm_dialog.dart'; +// import '../../widgets/transitions/fade_page.dart'; +// import 'package:intl/intl.dart' as intl; +// +// import '../landing/landing_page.dart'; +// import '../rateAppointment/rate_appointment_doctor.dart'; +// +// class RegisterNew extends StatefulWidget { +// @override +// _RegisterNew createState() => _RegisterNew(); +// } +// +// class _RegisterNew extends State { +// @override +// void initState() { +// super.initState(); +// } +// +// @override +// void dispose() { +// super.dispose(); +// } +// +// @override +// Widget build(BuildContext context) { +// +// AppState appState = getIt.get(); +// return Scaffold( +// body: GestureDetector( +// onTap: () { +// FocusScope.of(context).unfocus(); +// }, +// child: ScrollConfiguration( +// behavior: ScrollConfiguration.of(context).copyWith(overscroll: false, physics: const ClampingScrollPhysics()), +// child: NotificationListener( +// onNotification: (notification) { +// notification.disallowIndicator(); +// return true; +// }, +// child: SingleChildScrollView( +// physics: ClampingScrollPhysics(), +// padding: EdgeInsets.only( +// left: 24, +// right: 24, +// ), +// child: Column( +// mainAxisSize: MainAxisSize.min, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Utils.showLottie( +// context: context, +// assetPath: 'assets/animations/lottie/register.json', +// width: MediaQuery +// .of(context) +// .size +// .width * 0.45, +// height: MediaQuery +// .of(context) +// .size +// .height * 0.22, +// fit: BoxFit.cover, +// repeat: true), +// SizedBox(height: 8), +// LocaleKeys.prepareToElevate.tr().toText28( +// textScaler: TextScaler.linear(MediaQuery.textScalerOf(context).scale(1)), +// ), +// SizedBox(height: 24), +// Directionality( +// textDirection: Directionality.of(context), +// child: Container( +// decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), +// padding: EdgeInsets.only(left: 16, right: 16, top: 0, bottom: 0), +// child: Column( +// children: [ +// DropdownWidget( +// labelText: LocaleKeys.country.tr(), +// hintText: LocaleKeys.ksa.tr(), +// isEnable: true, +// selectedValue: AppStat.selectedLanguage == "ar" ? selectedCountry.nameArabic : selectedCountry.displayName, +// dropdownItems: Country.values.map((e) => "ar" ? e.displayName : e.displayName).toList(), +// +// // dropdownItems: Country.values.map((e) => (e.name).first).toList(), +// // dropdownItems: Country.values.map((e) => "ar" ? e.nameArabic : e.displayName).toList(), +// // selectedValue: context.selectedLanguage == "ar" ? selectedCountry.nameArabic : selectedCountry.displayName, +// // selectionType: SelectionType.dropdown, +// onChange: (val) { +// if (val != null) { +// } +// }, +// isBorderAllowed: false, +// isAllowLeadingIcon: true, +// hasSelectionCustomIcon: true, +// removePadding: true, +// isLeadingCountry: true, +// isAllowRadius: false, +// padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), +// selectionCustomIcon: "assets/images/svg/arrow-down.svg", +// leadingIcon: selectedCountry.iconPath, +// ).withVerticalPadding(8), +// Divider(height: 1), +// Directionality( +// textDirection: TextDirection.ltr, +// child: newInputWidget(TranslationBase +// .of(context) +// .nationalIdNumber, "xxxxxxxxx", nationalIDorFile, +// isEnable: true, +// prefix: null, +// removePadding: true, +// isAllowRadius: false, +// hasSelection: false, +// isBorderAllowed: false, +// isAllowLeadingIcon: true, +// autoFocus: true, +// padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), +// leadingIcon: "assets/images/svg/student-card.svg", +// onChange: (value) { +// print(value); +// }).withVerticalPadding(8), +// ), +// Divider(height: 1), +// Directionality( +// textDirection: TextDirection.ltr, +// child: newInputWidget(TranslationBase +// .of(context) +// .dob, "11 July, 1994", nationalIDorFile, +// isEnable: true, +// prefix: null, +// hasSelection: true, +// removePadding: true, +// isBorderAllowed: false, +// isAllowLeadingIcon: true, +// hasSelectionCustomIcon: true, +// selectionType: SelectionType.calendar, +// selectedValue: selectedDOB != null +// ? isHijri == 1 +// ? Utils.formatHijriDateToDisplay(selectedDOB!.toIso8601String()) +// : Utils.formatDateToDisplay(selectedDOB.toString()) +// : null, +// selectionCustomIcon: "assets/images/svg/calendar.svg", +// lang: context.selectedLanguage, +// padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), +// leadingIcon: "assets/images/svg/birthday-cake.svg", +// onChange: (value) { +// selectedDOB = DateTime.parse(value!); +// if (isHijri == 1) { +// var hijriDate = HijriGregConverter.gregorianToHijri(DateTime.parse(value)); +// selectedDOB = DateTime(hijriDate.year, hijriDate.month, hijriDate.day); +// } else { +// selectedDOB = DateTime.parse(value); +// } +// print(selectedDOB!.toIso8601String()); +// setState(() {}); +// }, +// onCalendarTypeChanged: (bool value) { +// if (value) { +// isHijri = 0; +// } else { +// isHijri = 1; +// } +// }).withVerticalPadding(8), +// ), +// ], +// ), +// ), +// ), +// SizedBox(height: 25), +// GestureDetector( +// onTap: () { +// setState(() { +// isTermsAccepted = !isTermsAccepted; +// }); +// }, +// child: Row( +// children: [ +// AnimatedContainer( +// duration: const Duration(milliseconds: 200), +// height: 24, +// width: 24, +// decoration: BoxDecoration( +// color: isTermsAccepted ? const Color(0xFFE92227) : Colors.transparent, +// borderRadius: BorderRadius.circular(6), +// border: Border.all( +// color: isTermsAccepted ? const Color(0xFFE92227) : Colors.grey, +// width: 2, +// ), +// ), +// child: isTermsAccepted ? const Icon(Icons.check, size: 16, color: Colors.white) : null, +// ), +// SizedBox(width: 12), +// Expanded( +// child: Text( +// TranslationBase +// .of(context) +// .iAcceptTermsConditions, +// style: context.dynamicTextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), +// ), +// ), +// ], +// ), +// ), +// SizedBox(height: 25), +// CustomButton( +// text: TranslationBase +// .of(context) +// .register, +// icon: "assets/images/svg/note-edit.svg", +// onPressed: () { +// // bool isValid = Utils.validateIqama(nationalIDorFile.text); +// if (nationalIDorFile == null || nationalIDorFile.text.isEmpty) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseEnterNationalId, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// // Utils.showErrorToast(TranslationBase.of(context).pleaseEnterNationalId); +// return; +// } +// if ((!Utils.validateIqama(nationalIDorFile.text) && selectedCountry.countryCode == '966') || +// (!Utils.validateUaeNationalId(nationalIDorFile.text) && selectedCountry.countryCode == '971')) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .incorrectNationalId, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// return; +// } +// if (selectedCountry == null || selectedCountry.countryCode.isEmpty) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseSelectCountry, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// return; +// } +// +// if (selectedDOB == null) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseSelectDOB, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// return; +// } +// if (!isTermsAccepted) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseAcceptTermsConditions, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// return; +// } +// +// if (phoneController != null) { +// phoneController.clear(); +// } +// showModalBottomSheet( +// context: context, +// isScrollControlled: true, +// isDismissible: false, +// backgroundColor: Colors.transparent, +// builder: (bottomSheetContext) => +// Padding( +// padding: EdgeInsets.only(bottom: MediaQuery +// .of(bottomSheetContext) +// .viewInsets +// .bottom), +// child: SingleChildScrollView( +// child: GenericBottomSheet( +// countryCode: selectedCountry.countryCode, +// initialPhoneNumber: "", +// textController: phoneController, +// onChange: (String? value) { +// mobileNo = value!; +// }, +// buttons: [ +// Padding( +// padding: const EdgeInsets.only(bottom: 10), +// child: CustomButton( +// text: TranslationBase +// .of(context) +// .sendOTPSMS, +// onPressed: () { +// if (mobileNo.isEmpty) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseEnterMobile, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// } else if (!Utils.validateMobileNumber(mobileNo)) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseEnterValidMobile, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// } else { +// registerUser(1); +// } +// }, +// backgroundColor: CustomColors.bgRedColor, +// borderColor: CustomColors.bgRedBorderColor, +// textColor: Colors.white, +// icon: "assets/images/svg/message.svg", +// ), +// ), +// Row( +// crossAxisAlignment: CrossAxisAlignment.center, +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Padding( +// padding: const EdgeInsets.symmetric(horizontal: 8), +// child: AppText( +// TranslationBase +// .of(context) +// .oR, +// fontSize: 16, +// fontFamily: context.fontFamily, +// color: Color(0xFF2E3039), +// fontWeight: FontWeight.w500, +// ), +// ), +// ], +// ), +// Padding( +// padding: const EdgeInsets.only(bottom: 10, top: 10), +// child: CustomButton( +// text: TranslationBase +// .of(context) +// .sendOTPWHATSAPP, +// onPressed: () { +// if (mobileNo.isEmpty) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseEnterMobile, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// } else if (!Utils.validateMobileNumber(mobileNo)) { +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: TranslationBase +// .of(context) +// .pleaseEnterValidMobile, +// showCancel: false, +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ); +// } else { +// registerUser(4); +// } +// // int? val = Utils.onOtpBtnPressed(OTPType.whatsapp, mobileNo, context); +// // registerUser(val); +// }, +// backgroundColor: Colors.white, +// borderColor: Color(0xFF2E3039), +// textColor: Color(0xFF2E3039), +// icon: "assets/images/svg/whatsapp.svg", +// ), +// ), +// ], +// myFocusNode: myFocusNode, +// ), +// ), +// ), +// ); +// Future.delayed(Duration(milliseconds: 500), () { +// myFocusNode.requestFocus(); +// }); +// }, +// fontFamily: context.fontFamily, +// ), +// SizedBox(height: 14), +// Center( +// child: RichText( +// textAlign: TextAlign.center, +// text: TextSpan( +// style: context.dynamicTextStyle( +// color: Colors.black, +// fontSize: 16, +// height: 26 / 16, +// fontWeight: FontWeight.w500, +// ), +// children: [ +// TextSpan(text: TranslationBase +// .of(context) +// .alreadyHaveAccount, style: context.dynamicTextStyle()), +// TextSpan(text: " "), +// TextSpan( +// text: TranslationBase +// .of(context) +// .loginNow, +// style: context.dynamicTextStyle( +// color: CustomColors.bgRedColor, +// fontSize: 16, +// height: 26 / 16, +// fontWeight: FontWeight.w500, +// ), +// recognizer: TapGestureRecognizer() +// ..onTap = () { +// Navigator.of(context).pop(); +// }, +// ), +// ], +// ), +// ), +// ), +// SizedBox(height: 30), +// ], +// ), +// ), +// ), +// ),) +// ); +// } +// +// Widget showProgress({String? title, String? status, Color? color, bool isNeedBorder = true}) { +// return Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Row( +// children: [ +// Container( +// width: 26, +// height: 26, +// decoration: containerRadius(color!, 200), +// child: Icon( +// Icons.done, +// color: Colors.white, +// size: 16, +// ), +// ), +// if (isNeedBorder) +// Expanded( +// child: Padding( +// padding: const EdgeInsets.all(8.0), +// child: mDivider(Colors.grey), +// )), +// ], +// ), +// mHeight(8), +// Text( +// title!, +// style: TextStyle( +// fontSize: 11, +// fontWeight: FontWeight.w600, +// letterSpacing: -0.44, +// ), +// ), +// mHeight(2), +// Container( +// padding: EdgeInsets.all(5), +// decoration: containerRadius(color.withOpacity(0.2), 4), +// child: Text( +// status!, +// style: TextStyle( +// fontSize: 8, +// fontWeight: FontWeight.w600, +// letterSpacing: -0.32, +// color: color, +// ), +// ), +// ), +// ], +// ) +// ], +// ); +// } +// } diff --git a/lib/providers/authentication_view_model.dart b/lib/providers/authentication_view_model.dart index af542ac..ecc3e04 100644 --- a/lib/providers/authentication_view_model.dart +++ b/lib/providers/authentication_view_model.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; class AuthenticationViewModel extends ChangeNotifier { - // Add properties and methods related to authentication here -} \ No newline at end of file +// Add properties and methods related to authentication here +} From 8f1e4500cc212b626509482cae4d65d1eda1c559 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Tue, 2 Sep 2025 12:17:34 +0300 Subject: [PATCH 04/16] register & Exception bottom sheet, country dropdown. --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 8 +- lib/extensions/string_extensions.dart | 13 +- lib/generated/locale_keys.g.dart | 4 + lib/main.dart | 3 - lib/presentation/authentication/login.dart | 177 ++-- lib/presentation/authentication/register.dart | 790 +++++------------- lib/presentation/home/landing_page.dart | 5 +- lib/providers/authentication_view_model.dart | 6 - lib/theme/colors.dart | 1 + lib/widgets/appbar/app_bar_widget.dart | 65 +- .../bottomsheet/exception_bottom_sheet.dart | 111 +++ .../dropdown/country_dropdown_widget.dart | 165 ++++ .../{ => dropdown}/dropdown_widget.dart | 88 +- lib/widgets/input_widget.dart | 47 +- lib/widgets/language_switcher.dart | 7 +- pubspec.lock | 30 +- 17 files changed, 721 insertions(+), 805 deletions(-) delete mode 100644 lib/providers/authentication_view_model.dart create mode 100644 lib/widgets/bottomsheet/exception_bottom_sheet.dart create mode 100644 lib/widgets/dropdown/country_dropdown_widget.dart rename lib/widgets/{ => dropdown}/dropdown_widget.dart (56%) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index ebcee14..57f9bcd 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -783,7 +783,11 @@ "enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي", "enterPhoneDesc": "أدخل رقم هاتفك لتلقي رمز التحقق ", "pleaseChooseOption": "الرجاء اختيار من الخيارات أدناه لتلقي رمز التحقق OTP", - "prepareToElevate": "هل أنت مستعد لتحسين صحتك ورفاهتك؟" + "prepareToElevate": "هل أنت مستعد لتحسين صحتك ورفاهتك؟", + "iAcceptTermsConditions": "أوافق على الشروط والأحكام", + "alreadyHaveAccount": "هل لديك حساب بالفعل؟", + "loginNow": "تسجيل الدخول الآن", + "notice": "إشعار" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index b45c697..311db65 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -776,8 +776,12 @@ "enterPhoneNumber": "Enter Phone Number", "enterEmailDesc": "Enter your email address to complete the process of creating a medical file", "enterPhoneDesc": "Enter your phone number to receive OTP verification code", - "pleaseChooseOption": "Please select from the below options to receive OTP" + "pleaseChooseOption": "Please select from the below options to receive OTP", "dontHaveAccount": "Don't have an account?", "loginOrRegister": "Login or Register", - "prepareToElevate": "Prepared to elevate your health and well-being?" + "prepareToElevate": "Prepared to elevate your health and well-being?", + "iAcceptTermsConditions": "I Accept the Terms and Conditions", + "alreadyHaveAccount": "Already have an account?", + "loginNow": "Login Now", + "notice": "Notice" } \ No newline at end of file diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index ec9ff99..187939e 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -1,3 +1,4 @@ +import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:auto_size_text/auto_size_text.dart'; @@ -233,6 +234,12 @@ extension EmailValidator on String { style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); + Widget toText36({Color? color, bool isBold = false, bool isCenter = false}) => Text( + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: 47 / 36, color: color ?? AppColors.blackColor, fontSize: 36.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); + Widget toText44({Color? color, bool isBold = false}) => Text( this, style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), @@ -374,7 +381,6 @@ class FontUtils { } } - extension CountryExtension on Country { String get displayName { switch (this) { @@ -397,9 +403,9 @@ extension CountryExtension on Country { String get iconPath { switch (this) { case Country.saudiArabia: - return "assets/images/svg/ksa.svg"; + return AppAssets.ksa; case Country.unitedArabEmirates: - return "assets/images/svg/uae.svg"; + return AppAssets.uae; } } @@ -425,4 +431,3 @@ extension CountryExtension on Country { } } } - diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 819fb22..f219526 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -782,5 +782,9 @@ abstract class LocaleKeys { static const enterPhoneDesc = 'enterPhoneDesc'; static const pleaseChooseOption = 'pleaseChooseOption'; static const prepareToElevate = 'prepareToElevate'; + static const iAcceptTermsConditions = 'iAcceptTermsConditions'; + static const alreadyHaveAccount = 'alreadyHaveAccount'; + static const loginNow = 'loginNow'; + static const notice = 'notice'; } diff --git a/lib/main.dart b/lib/main.dart index 31c5c56..a9afd60 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,9 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; -import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/utils/size_config.dart'; -import 'package:hmg_patient_app_new/providers/authentication_view_model.dart'; import 'package:hmg_patient_app_new/providers/bottom_navigation_provider.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; @@ -22,7 +20,6 @@ import 'firebase_options.dart'; var globalMessengerKey = GlobalKey(); final navigatorKey = GlobalKey(); - @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index da85baa..87f40ca 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -1,18 +1,17 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/gestures.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; -import 'package:hmg_patient_app_new/core/utils/size_utils.dart' hide Sizer; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; -import 'package:sizer/sizer.dart'; class LoginScreen extends StatefulWidget { @override @@ -32,98 +31,92 @@ class _LoginScreen extends State { @override Widget build(BuildContext context) { - return Sizer(// Wrap with Sizer - builder: (context, orientation, deviceType) { - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - appBar: CustomAppBar( - onBackPressed: () { - }, - onLanguageChanged: (String value) { - // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); - }, - ), - body: GestureDetector( - onTap: () { - FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside - }, - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.only(left: 6.sp, right: 6.sp), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.w, height: 22.sp, repeat: true, fit: BoxFit.cover), - SizedBox(height: 19.sp), // Adjusted to sizer unit - LocaleKeys.welcomeToDrSulaiman.tr().toText22(isBold: true, color: AppColors.textColor), - // Text( - // LocaleKeys.welcomeToDrSulaiman.tr(), - // style: context.dynamicTextStyle( - // fontSize: 22, - // fontWeight: FontWeight.w600, - // color: AppColors.textColor, - // letterSpacing: -0.4, - // height: 40 / 28, - // ), - // ), - SizedBox(height: 4.sp), // Adjusted to sizer unit (approx 32px) - TextInputWidget( - labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", - hintText: "xxxxxxxxx", - controller: TextEditingController(), - keyboardType: TextInputType.number, - isEnable: true, - prefix: null, - autoFocus: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - padding: EdgeInsets.symmetric(vertical: 1.sp, horizontal: 2.w), - leadingIcon: AppAssets.student_card, - errorMessage: "Please enter a valid national ID or file number", - hasError: true, - ), - SizedBox(height: 2.sp), // Adjusted to sizer unit (approx 16px) - CustomButton( - text: LocaleKeys.login.tr(), - icon: AppAssets.login1, - iconColor: Colors.white, - onPressed: () {}, - ), - SizedBox(height: 2.sp), // Adjusted to sizer unit (approx 14px) - Center( - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - style: context.dynamicTextStyle( - color: Colors.black, - fontSize: 14.sp, // Adjusted to sizer unit - height: 26 / 16, // This height is a ratio, may need re-evaluation - fontWeight: FontWeight.w500, - ), - children: [ - TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()), - TextSpan(text: " "), - TextSpan( - text: LocaleKeys.registernow.tr(), - style: context.dynamicTextStyle( - color: AppColors.primaryRedColor, - fontSize: 14.sp, // Adjusted to sizer unit - height: 26 / 16, // Ratio - fontWeight: FontWeight.w500, - ), - recognizer: TapGestureRecognizer()..onTap = () {}, - ), - ], + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: CustomAppBar( + onBackPressed: () { + Navigator.of(context).pop(); + }, + onLanguageChanged: (String value) { + // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); + }, + ), + body: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside + }, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 200.h, height: 200.h, repeat: true, fit: BoxFit.cover), + SizedBox(height: 130.h), // Adjusted to sizer unit + LocaleKeys.welcomeToDrSulaiman.tr().toText32(isBold: true, color: AppColors.textColor), + SizedBox(height: 32.h), + TextInputWidget( + labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", + hintText: "xxxxxxxxx", + controller: TextEditingController(), + keyboardType: TextInputType.number, + isEnable: true, + prefix: null, + autoFocus: true, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 10.h), + leadingIcon: AppAssets.student_card, + errorMessage: "Please enter a valid national ID or file number", + hasError: true, + ), + SizedBox(height: 16.h), // Adjusted to sizer unit (approx 16px) + CustomButton( + text: LocaleKeys.login.tr(), + icon: AppAssets.login1, + iconColor: Colors.white, + onPressed: () {}, + ), + SizedBox(height: 10.h), // Adjusted to sizer unit (approx 14px) + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: context.dynamicTextStyle( + color: Colors.black, + fontSize: 14.fSize, // Adjusted to sizer unit + height: 26 / 16, // This height is a ratio, may need re-evaluation + fontWeight: FontWeight.w500, ), - ).withVerticalPadding(2.sp), // Adjusted to sizer unit - ), - ], - ), + children: [ + TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()), + TextSpan(text: " "), + TextSpan( + text: LocaleKeys.registernow.tr(), + style: context.dynamicTextStyle( + color: AppColors.primaryRedColor, + fontSize: 14.fSize, // Adjusted to sizer unit + height: 26 / 16, // Ratio + fontWeight: FontWeight.w500, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) => RegisterNew()), + ); + }, + ), + ], + ), + ).withVerticalPadding(2), // Adjusted to sizer unit + ), + ], ), ), ), - ); - }); + ), + ); } } diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 9b900b5..5564c46 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -1,579 +1,211 @@ -// import'dart:convert'; -// -// import 'package:easy_localization/easy_localization.dart'; -// import 'package:flutter/gestures.dart'; -// import 'package:flutter_svg/flutter_svg.dart'; -// import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart'; -// import 'package:hmg_patient_app/config/config.dart'; -// import 'package:hmg_patient_app/config/shared_pref_kay.dart'; -// import 'package:hmg_patient_app/config/size_config.dart'; -// import 'package:hmg_patient_app/core/service/client/base_app_client.dart'; -// import 'package:hmg_patient_app/core/viewModels/project_view_model.dart'; -// import 'package:hmg_patient_app/models/Appointments/toDoCountProviderModel.dart'; -// import 'package:hmg_patient_app/models/Authentication/check_activation_code_response.dart'; -// import 'package:hmg_patient_app/models/Authentication/check_paitent_authentication_req.dart'; -// import 'package:hmg_patient_app/models/Authentication/check_user_status_reponse.dart'; -// import 'package:hmg_patient_app/models/Authentication/check_user_status_req.dart'; -// import 'package:hmg_patient_app/models/Authentication/checkpatient_for_registration.dart'; -// import 'package:hmg_patient_app/models/Authentication/register_info_response.dart'; -// import 'package:hmg_patient_app/models/Authentication/send_activation_request.dart'; -// import 'package:hmg_patient_app/new_ui/new_ext.dart'; -// import 'package:hmg_patient_app/new_ui/otp/otp_validation_bootmsheet_widget.dart'; -// import 'package:hmg_patient_app/pages/AlHabibMedicalService/health_calculator/carbs/carbs.dart'; -// import 'package:hmg_patient_app/pages/login/login-type.dart'; -// import 'package:hmg_patient_app/pages/login/register-info.dart'; -// import 'package:hmg_patient_app/pages/login/register.dart'; -// import 'package:hmg_patient_app/pages/login/register_new_step_2.dart'; -// import 'package:hmg_patient_app/pages/login/user-login-agreement-page.dart'; -// import 'package:hmg_patient_app/pages/login/welcome.dart'; -// import 'package:hmg_patient_app/services/authentication/auth_provider.dart'; -// import 'package:hmg_patient_app/theme/colors.dart'; -// import 'package:hmg_patient_app/uitl/app_shared_preferences.dart'; -// import 'package:hmg_patient_app/uitl/app_toast.dart'; -// import 'package:hmg_patient_app/uitl/font_utils.dart'; -// import 'package:hmg_patient_app/uitl/gif_loader_dialog_utils.dart'; -// import 'package:hmg_patient_app/uitl/translations_delegate_base.dart'; -// import 'package:hmg_patient_app/uitl/utils.dart'; -// import 'package:hmg_patient_app/uitl/utils_new.dart'; -// import 'package:hmg_patient_app/widgets/drawer/langauge_picker.dart'; -// import 'package:hmg_patient_app/widgets/others/app_scaffold_widget.dart'; -// import 'package:flutter/material.dart'; -// import 'package:hmg_patient_app/widgets/otp/sms-popup.dart'; -// import 'package:hmg_patient_app/widgets/text/app_texts_widget.dart'; -// import 'package:hmg_patient_app_new/core/app_state.dart'; -// import 'package:hmg_patient_app_new/core/enums.dart'; -// import 'package:hmg_patient_app_new/core/utils/utils.dart'; -// import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; -// import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; -// import 'package:hmg_patient_app_new/widgets/dropdown_widget.dart'; -// import 'package:hmg_patient_app_new/widgets/input_widget.dart'; -// import 'package:provider/provider.dart'; -// -// import '../../core/viewModels/appointment_rate_view_model.dart'; -// import '../../locator.dart'; -// import '../../models/Authentication/authenticated_user.dart'; -// import '../../models/Authentication/select_device_imei_res.dart'; -// import '../../models/InPatientServices/get_admission_info_response_model.dart'; -// import '../../models/InPatientServices/get_admission_request_info_response_model.dart'; -// import '../../new_ui/exception_widget/ExceptionBottomSheet.dart'; -// import '../../services/clinic_services/get_clinic_service.dart'; -// import '../../widgets/dialogs/alert_dialog.dart'; -// import '../../widgets/dialogs/confirm_dialog.dart'; -// import '../../widgets/transitions/fade_page.dart'; -// import 'package:intl/intl.dart' as intl; -// -// import '../landing/landing_page.dart'; -// import '../rateAppointment/rate_appointment_doctor.dart'; -// -// class RegisterNew extends StatefulWidget { -// @override -// _RegisterNew createState() => _RegisterNew(); -// } -// -// class _RegisterNew extends State { -// @override -// void initState() { -// super.initState(); -// } -// -// @override -// void dispose() { -// super.dispose(); -// } -// -// @override -// Widget build(BuildContext context) { -// -// AppState appState = getIt.get(); -// return Scaffold( -// body: GestureDetector( -// onTap: () { -// FocusScope.of(context).unfocus(); -// }, -// child: ScrollConfiguration( -// behavior: ScrollConfiguration.of(context).copyWith(overscroll: false, physics: const ClampingScrollPhysics()), -// child: NotificationListener( -// onNotification: (notification) { -// notification.disallowIndicator(); -// return true; -// }, -// child: SingleChildScrollView( -// physics: ClampingScrollPhysics(), -// padding: EdgeInsets.only( -// left: 24, -// right: 24, -// ), -// child: Column( -// mainAxisSize: MainAxisSize.min, -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Utils.showLottie( -// context: context, -// assetPath: 'assets/animations/lottie/register.json', -// width: MediaQuery -// .of(context) -// .size -// .width * 0.45, -// height: MediaQuery -// .of(context) -// .size -// .height * 0.22, -// fit: BoxFit.cover, -// repeat: true), -// SizedBox(height: 8), -// LocaleKeys.prepareToElevate.tr().toText28( -// textScaler: TextScaler.linear(MediaQuery.textScalerOf(context).scale(1)), -// ), -// SizedBox(height: 24), -// Directionality( -// textDirection: Directionality.of(context), -// child: Container( -// decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), -// padding: EdgeInsets.only(left: 16, right: 16, top: 0, bottom: 0), -// child: Column( -// children: [ -// DropdownWidget( -// labelText: LocaleKeys.country.tr(), -// hintText: LocaleKeys.ksa.tr(), -// isEnable: true, -// selectedValue: AppStat.selectedLanguage == "ar" ? selectedCountry.nameArabic : selectedCountry.displayName, -// dropdownItems: Country.values.map((e) => "ar" ? e.displayName : e.displayName).toList(), -// -// // dropdownItems: Country.values.map((e) => (e.name).first).toList(), -// // dropdownItems: Country.values.map((e) => "ar" ? e.nameArabic : e.displayName).toList(), -// // selectedValue: context.selectedLanguage == "ar" ? selectedCountry.nameArabic : selectedCountry.displayName, -// // selectionType: SelectionType.dropdown, -// onChange: (val) { -// if (val != null) { -// } -// }, -// isBorderAllowed: false, -// isAllowLeadingIcon: true, -// hasSelectionCustomIcon: true, -// removePadding: true, -// isLeadingCountry: true, -// isAllowRadius: false, -// padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), -// selectionCustomIcon: "assets/images/svg/arrow-down.svg", -// leadingIcon: selectedCountry.iconPath, -// ).withVerticalPadding(8), -// Divider(height: 1), -// Directionality( -// textDirection: TextDirection.ltr, -// child: newInputWidget(TranslationBase -// .of(context) -// .nationalIdNumber, "xxxxxxxxx", nationalIDorFile, -// isEnable: true, -// prefix: null, -// removePadding: true, -// isAllowRadius: false, -// hasSelection: false, -// isBorderAllowed: false, -// isAllowLeadingIcon: true, -// autoFocus: true, -// padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), -// leadingIcon: "assets/images/svg/student-card.svg", -// onChange: (value) { -// print(value); -// }).withVerticalPadding(8), -// ), -// Divider(height: 1), -// Directionality( -// textDirection: TextDirection.ltr, -// child: newInputWidget(TranslationBase -// .of(context) -// .dob, "11 July, 1994", nationalIDorFile, -// isEnable: true, -// prefix: null, -// hasSelection: true, -// removePadding: true, -// isBorderAllowed: false, -// isAllowLeadingIcon: true, -// hasSelectionCustomIcon: true, -// selectionType: SelectionType.calendar, -// selectedValue: selectedDOB != null -// ? isHijri == 1 -// ? Utils.formatHijriDateToDisplay(selectedDOB!.toIso8601String()) -// : Utils.formatDateToDisplay(selectedDOB.toString()) -// : null, -// selectionCustomIcon: "assets/images/svg/calendar.svg", -// lang: context.selectedLanguage, -// padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), -// leadingIcon: "assets/images/svg/birthday-cake.svg", -// onChange: (value) { -// selectedDOB = DateTime.parse(value!); -// if (isHijri == 1) { -// var hijriDate = HijriGregConverter.gregorianToHijri(DateTime.parse(value)); -// selectedDOB = DateTime(hijriDate.year, hijriDate.month, hijriDate.day); -// } else { -// selectedDOB = DateTime.parse(value); -// } -// print(selectedDOB!.toIso8601String()); -// setState(() {}); -// }, -// onCalendarTypeChanged: (bool value) { -// if (value) { -// isHijri = 0; -// } else { -// isHijri = 1; -// } -// }).withVerticalPadding(8), -// ), -// ], -// ), -// ), -// ), -// SizedBox(height: 25), -// GestureDetector( -// onTap: () { -// setState(() { -// isTermsAccepted = !isTermsAccepted; -// }); -// }, -// child: Row( -// children: [ -// AnimatedContainer( -// duration: const Duration(milliseconds: 200), -// height: 24, -// width: 24, -// decoration: BoxDecoration( -// color: isTermsAccepted ? const Color(0xFFE92227) : Colors.transparent, -// borderRadius: BorderRadius.circular(6), -// border: Border.all( -// color: isTermsAccepted ? const Color(0xFFE92227) : Colors.grey, -// width: 2, -// ), -// ), -// child: isTermsAccepted ? const Icon(Icons.check, size: 16, color: Colors.white) : null, -// ), -// SizedBox(width: 12), -// Expanded( -// child: Text( -// TranslationBase -// .of(context) -// .iAcceptTermsConditions, -// style: context.dynamicTextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), -// ), -// ), -// ], -// ), -// ), -// SizedBox(height: 25), -// CustomButton( -// text: TranslationBase -// .of(context) -// .register, -// icon: "assets/images/svg/note-edit.svg", -// onPressed: () { -// // bool isValid = Utils.validateIqama(nationalIDorFile.text); -// if (nationalIDorFile == null || nationalIDorFile.text.isEmpty) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseEnterNationalId, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// // Utils.showErrorToast(TranslationBase.of(context).pleaseEnterNationalId); -// return; -// } -// if ((!Utils.validateIqama(nationalIDorFile.text) && selectedCountry.countryCode == '966') || -// (!Utils.validateUaeNationalId(nationalIDorFile.text) && selectedCountry.countryCode == '971')) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .incorrectNationalId, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// return; -// } -// if (selectedCountry == null || selectedCountry.countryCode.isEmpty) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseSelectCountry, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// return; -// } -// -// if (selectedDOB == null) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseSelectDOB, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// return; -// } -// if (!isTermsAccepted) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseAcceptTermsConditions, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// return; -// } -// -// if (phoneController != null) { -// phoneController.clear(); -// } -// showModalBottomSheet( -// context: context, -// isScrollControlled: true, -// isDismissible: false, -// backgroundColor: Colors.transparent, -// builder: (bottomSheetContext) => -// Padding( -// padding: EdgeInsets.only(bottom: MediaQuery -// .of(bottomSheetContext) -// .viewInsets -// .bottom), -// child: SingleChildScrollView( -// child: GenericBottomSheet( -// countryCode: selectedCountry.countryCode, -// initialPhoneNumber: "", -// textController: phoneController, -// onChange: (String? value) { -// mobileNo = value!; -// }, -// buttons: [ -// Padding( -// padding: const EdgeInsets.only(bottom: 10), -// child: CustomButton( -// text: TranslationBase -// .of(context) -// .sendOTPSMS, -// onPressed: () { -// if (mobileNo.isEmpty) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseEnterMobile, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// } else if (!Utils.validateMobileNumber(mobileNo)) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseEnterValidMobile, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// } else { -// registerUser(1); -// } -// }, -// backgroundColor: CustomColors.bgRedColor, -// borderColor: CustomColors.bgRedBorderColor, -// textColor: Colors.white, -// icon: "assets/images/svg/message.svg", -// ), -// ), -// Row( -// crossAxisAlignment: CrossAxisAlignment.center, -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Padding( -// padding: const EdgeInsets.symmetric(horizontal: 8), -// child: AppText( -// TranslationBase -// .of(context) -// .oR, -// fontSize: 16, -// fontFamily: context.fontFamily, -// color: Color(0xFF2E3039), -// fontWeight: FontWeight.w500, -// ), -// ), -// ], -// ), -// Padding( -// padding: const EdgeInsets.only(bottom: 10, top: 10), -// child: CustomButton( -// text: TranslationBase -// .of(context) -// .sendOTPWHATSAPP, -// onPressed: () { -// if (mobileNo.isEmpty) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseEnterMobile, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// } else if (!Utils.validateMobileNumber(mobileNo)) { -// context.showBottomSheet( -// child: ExceptionBottomSheet( -// message: TranslationBase -// .of(context) -// .pleaseEnterValidMobile, -// showCancel: false, -// onOkPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// ); -// } else { -// registerUser(4); -// } -// // int? val = Utils.onOtpBtnPressed(OTPType.whatsapp, mobileNo, context); -// // registerUser(val); -// }, -// backgroundColor: Colors.white, -// borderColor: Color(0xFF2E3039), -// textColor: Color(0xFF2E3039), -// icon: "assets/images/svg/whatsapp.svg", -// ), -// ), -// ], -// myFocusNode: myFocusNode, -// ), -// ), -// ), -// ); -// Future.delayed(Duration(milliseconds: 500), () { -// myFocusNode.requestFocus(); -// }); -// }, -// fontFamily: context.fontFamily, -// ), -// SizedBox(height: 14), -// Center( -// child: RichText( -// textAlign: TextAlign.center, -// text: TextSpan( -// style: context.dynamicTextStyle( -// color: Colors.black, -// fontSize: 16, -// height: 26 / 16, -// fontWeight: FontWeight.w500, -// ), -// children: [ -// TextSpan(text: TranslationBase -// .of(context) -// .alreadyHaveAccount, style: context.dynamicTextStyle()), -// TextSpan(text: " "), -// TextSpan( -// text: TranslationBase -// .of(context) -// .loginNow, -// style: context.dynamicTextStyle( -// color: CustomColors.bgRedColor, -// fontSize: 16, -// height: 26 / 16, -// fontWeight: FontWeight.w500, -// ), -// recognizer: TapGestureRecognizer() -// ..onTap = () { -// Navigator.of(context).pop(); -// }, -// ), -// ], -// ), -// ), -// ), -// SizedBox(height: 30), -// ], -// ), -// ), -// ), -// ),) -// ); -// } -// -// Widget showProgress({String? title, String? status, Color? color, bool isNeedBorder = true}) { -// return Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Row( -// children: [ -// Container( -// width: 26, -// height: 26, -// decoration: containerRadius(color!, 200), -// child: Icon( -// Icons.done, -// color: Colors.white, -// size: 16, -// ), -// ), -// if (isNeedBorder) -// Expanded( -// child: Padding( -// padding: const EdgeInsets.all(8.0), -// child: mDivider(Colors.grey), -// )), -// ], -// ), -// mHeight(8), -// Text( -// title!, -// style: TextStyle( -// fontSize: 11, -// fontWeight: FontWeight.w600, -// letterSpacing: -0.44, -// ), -// ), -// mHeight(2), -// Container( -// padding: EdgeInsets.all(5), -// decoration: containerRadius(color.withOpacity(0.2), 4), -// child: Text( -// status!, -// style: TextStyle( -// fontSize: 8, -// fontWeight: FontWeight.w600, -// letterSpacing: -0.32, -// color: color, -// ), -// ), -// ), -// ], -// ) -// ], -// ); -// } -// } +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart' show CustomButton; +import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; +import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; + +class RegisterNew extends StatefulWidget { + @override + _RegisterNew createState() => _RegisterNew(); +} + +class _RegisterNew extends State { + bool isTermsAccepted = true; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + AppState appState = getIt.get(); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: CustomAppBar( + onBackPressed: () { + Navigator.of(context).pop(); + }, + onLanguageChanged: (String value) { + // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); + }, + ), + body: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + }, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith(overscroll: false, physics: const ClampingScrollPhysics()), + child: NotificationListener( + onNotification: (notification) { + notification.disallowIndicator(); + return true; + }, + child: SingleChildScrollView( + physics: ClampingScrollPhysics(), + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.showLottie(context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.h, height: 200.h, fit: BoxFit.cover, repeat: true), + SizedBox(height: 16.h), + LocaleKeys.prepareToElevate.tr().toText32(isBold: true), + SizedBox(height: 24.h), + Directionality( + textDirection: Directionality.of(context), + child: Container( + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), + padding: EdgeInsets.symmetric(horizontal: 16.h), + child: Column( + children: [ + CustomCountryDropdown( + countryList: Country.values, + onCountryChange: (Country? value) {}, + isRtl: Directionality.of(context) == TextDirection.LTR, + ).withVerticalPadding(8.h), + + // DropdownWidget( + // labelText: LocaleKeys.country.tr(), + // hintText: LocaleKeys.ksa.tr(), + // isEnable: true, + // selectedValue: appState.getLanguageID(context) == "1" ? "selectedCountry.nameArabic" : "selectedCountry.displayName", + // dropdownItems: Country.values.map((e) => appState.getLanguageID(context) == " 1" ? e.displayName : e.displayName).toList(), + // onChange: (val) { + // if (val != null) {} + // }, + // isBorderAllowed: false, + // hasSelectionCustomIcon: true, + // isAllowRadius: false, + // padding: EdgeInsets.symmetric(vertical: 8.h), + // selectionCustomIcon: AppAssets.arrow_down, + // ).withVerticalPadding(8), + Divider(height: 1.h), + TextInputWidget( + labelText: LocaleKeys.nationalIdNumber.tr(), + hintText: "xxxxxxxxx", + controller: TextEditingController(), + isEnable: true, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + + autoFocus: true, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.student_card, + onChange: (value) { + print(value); + }).withVerticalPadding(8), + Divider(height: 1), + TextInputWidget( + labelText: LocaleKeys.dob.tr(), + hintText: "11 July, 1994", + controller: TextEditingController(), + isEnable: true, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.birthday_cake, + onChange: (value) {}, + ).withVerticalPadding(8), + ], + ), + ), + ), + SizedBox(height: 25.h), + GestureDetector( + onTap: () {}, + child: Row( + children: [ + AnimatedContainer( + duration: const Duration(milliseconds: 200), + height: 24.h, + width: 24.h, + decoration: BoxDecoration( + color: isTermsAccepted ? const Color(0xFFE92227) : Colors.transparent, + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: isTermsAccepted ? const Color(0xFFE92227) : Colors.grey, + width: 2.h, + ), + ), + child: isTermsAccepted ? Icon(Icons.check, size: 16.fSize, color: Colors.white) : null, + ), + SizedBox(width: 12.h), + Expanded( + child: Text( + LocaleKeys.iAcceptTermsConditions.tr(), + style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + ), + ), + ], + ), + ), + SizedBox(height: 25.h), + CustomButton( + text: "Register", + icon: AppAssets.note_edit, + onPressed: () {}, + ), + SizedBox(height: 14), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: context.dynamicTextStyle( + color: Colors.black, + fontSize: 16.fSize, + height: 26 / 16, + fontWeight: FontWeight.w500, + ), + children: [ + TextSpan(text: LocaleKeys.alreadyHaveAccount.tr(), style: context.dynamicTextStyle()), + TextSpan(text: " "), + TextSpan( + text: LocaleKeys.loginNow.tr(), + style: context.dynamicTextStyle( + color: AppColors.primaryRedColor, + fontSize: 16.fSize, + height: 26 / 16, + fontWeight: FontWeight.w500, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ), + SizedBox(height: 30), + ], + ), + ), + ),) + , + ) + ); + } +} diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index feb7f69..dd9f85c 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart'; import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart'; @@ -44,8 +45,8 @@ class _LandingPageState extends State { CustomButton( text: LocaleKeys.loginOrRegister.tr(context: context), onPressed: () { - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (BuildContext context) => LandingPage()), + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), ); }, backgroundColor: Color(0xffFEE9EA), diff --git a/lib/providers/authentication_view_model.dart b/lib/providers/authentication_view_model.dart deleted file mode 100644 index 1641148..0000000 --- a/lib/providers/authentication_view_model.dart +++ /dev/null @@ -1,6 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:hmg_patient_app_new/core/app_state.dart'; -class AuthenticationViewModel extends ChangeNotifier { -// Add properties and methods related to authentication here -} diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index b7a1fb4..252d887 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -30,6 +30,7 @@ class AppColors { static const Color textColor = Color(0xFF2E3039); static const Color borderOnlyColor = Color(0xFF2E3039); static const blackColor = textColor; + static const inputLabelTextColor = Color(0xff898A8D); //Chips static const Color successColor = Color(0xff18C273); diff --git a/lib/widgets/appbar/app_bar_widget.dart b/lib/widgets/appbar/app_bar_widget.dart index dde02ed..2fdbc94 100644 --- a/lib/widgets/appbar/app_bar_widget.dart +++ b/lib/widgets/appbar/app_bar_widget.dart @@ -1,7 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/widgets/language_switcher.dart'; import '../../generated/locale_keys.g.dart'; @@ -24,45 +26,44 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { return AppBar( backgroundColor: Colors.transparent, leading: null, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // Arrow Back with click handler - Expanded( - child: Align( - alignment: Alignment.centerLeft, - child: GestureDetector( - onTap: onBackPressed, - child: Utils.buildSvgWithAssets( - icon: AppAssets.arrow_back, - width: 32, - height: 32, + automaticallyImplyLeading: false, + title: Padding( + padding: EdgeInsets.symmetric(horizontal: 10.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: GestureDetector( + onTap: onBackPressed, + child: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 32.h, height: 32.h), ), ), ), - ), - // Logo - Utils.buildSvgWithAssets( - icon: AppAssets.habiblogo, - ), + // Logo + Utils.buildSvgWithAssets( + icon: AppAssets.habiblogo, + ), - // Language Selector - Expanded( - child: Align( - alignment: Alignment.centerRight, - child: LanguageSelector( - currentLanguage: context.locale.languageCode, - showOnlyIcon: false, - onLanguageChanged: onLanguageChanged, - languages: [ - {'code': 'ar', 'name': LocaleKeys.arabic.tr()}, - {'code': 'en', 'name': LocaleKeys.english.tr()} - ], + // Language Selector + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: LanguageSelector( + currentLanguage: context.locale.languageCode, + showOnlyIcon: false, + onLanguageChanged: onLanguageChanged, + languages: [ + {'code': 'ar', 'name': LocaleKeys.arabic.tr()}, + {'code': 'en', 'name': LocaleKeys.english.tr()} + ], + ), ), ), - ), - ], + ], + ), ), centerTitle: true, ); diff --git a/lib/widgets/bottomsheet/exception_bottom_sheet.dart b/lib/widgets/bottomsheet/exception_bottom_sheet.dart new file mode 100644 index 0000000..f0ea651 --- /dev/null +++ b/lib/widgets/bottomsheet/exception_bottom_sheet.dart @@ -0,0 +1,111 @@ +import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class ExceptionBottomSheet extends StatelessWidget { + String message; + bool showOKButton; + bool showCancel; + Function() onOkPressed; + Function()? onCancelPressed; + + ExceptionBottomSheet({Key? key, required this.message, this.showOKButton = true, this.showCancel = false, required this.onOkPressed, this.onCancelPressed}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SafeArea( + bottom: Platform.isIOS ? false : true, // Adjust for iOS to avoid bottom padding + child: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside + }, + child: Builder(builder: (context) { + return Directionality( + textDirection: Directionality.of(context), + child: Container( + padding: EdgeInsets.all(24.h), + decoration: BoxDecoration( + color: Color(0xFFF8F8FA), + borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.notice.tr().toText28(), + InkWell( + onTap: () { + Navigator.of(context).pop(); + }, + child: Utils.buildSvgWithAssets(icon: AppAssets.cross_circle), + ) + ], + ), + SizedBox(height: 10.h), + (message ?? "").toText16(isBold: false, color: AppColors.textColor), + SizedBox(height: 10.h), + SizedBox(height: 24.h), + if (showOKButton && showCancel) + Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.cancel.tr(), + onPressed: onCancelPressed != null + ? onCancelPressed! + : () { + Navigator.of(context).pop(); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + icon: AppAssets.cancel, + iconColor: AppColors.primaryRedColor, + ), + ), + SizedBox(width: 5.h), + Expanded( + child: CustomButton( + text: showCancel ? LocaleKeys.confirm.tr() : LocaleKeys.ok.tr(), + onPressed: onOkPressed, + backgroundColor: AppColors.bgGreenColor, + borderColor: AppColors.bgGreenColor, + textColor: Colors.white, + icon: AppAssets.confirm, + ), + ), + ], + ), + if (showOKButton && !showCancel) + Padding( + padding: EdgeInsets.only(bottom: 10.h), + child: CustomButton( + text: LocaleKeys.ok.tr(), + onPressed: onOkPressed, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: Colors.white, + icon: AppAssets.confirm, + ), + ), + ], + ), + ), + ); + }), + ), + ); + } +} diff --git a/lib/widgets/dropdown/country_dropdown_widget.dart b/lib/widgets/dropdown/country_dropdown_widget.dart new file mode 100644 index 0000000..81ac60a --- /dev/null +++ b/lib/widgets/dropdown/country_dropdown_widget.dart @@ -0,0 +1,165 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; + +class CustomCountryDropdown extends StatefulWidget { + final List countryList; + final Function(Country)? onCountryChange; + final bool isRtl; + + const CustomCountryDropdown({ + Key? key, + required this.countryList, + this.onCountryChange, + required this.isRtl, + }) : super(key: key); + + @override + _CustomCountryDropdownState createState() => _CustomCountryDropdownState(); +} + +class _CustomCountryDropdownState extends State { + Country? selectedCountry; + late OverlayEntry _overlayEntry; + bool _isDropdownOpen = false; + + @override + void initState() { + super.initState(); + selectedCountry = Country.saudiArabia; + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (_isDropdownOpen) { + _closeDropdown(); + } else { + _openDropdown(); + } + }, + child: Container( + height: 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h), + child: Row( + children: [ + Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h), + SizedBox(width: 8.h), + Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), + SizedBox(width: 4.h), + Text( + selectedCountry != null ? selectedCountry!.displayName : "Select Country", + style: TextStyle( + fontSize: 14.fSize, + height: 21 / 14, + fontWeight: FontWeight.w500, + letterSpacing: -0.2, + ), + ), + ], + ), + ), + ); + } + + void _openDropdown() { + RenderBox renderBox = context.findRenderObject() as RenderBox; + Offset offset = renderBox.localToGlobal(Offset.zero); + + _overlayEntry = OverlayEntry( + builder: (context) => Stack( + children: [ + // Dismiss dropdown when tapping outside + Positioned.fill( + child: GestureDetector( + onTap: _closeDropdown, + behavior: HitTestBehavior.translucent, + child: Container(), + ), + ), + Positioned( + top: offset.dy + renderBox.size.height, + left: widget.isRtl ? offset.dx + 15.h : offset.dx - 15.h, + width: renderBox.size.width, + child: Material( + child: Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + borderRadius: 12, + ), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(12), + // boxShadow: [ + // BoxShadow( + // color: Color(0xFFF8F8FA), + // blurRadius: 8.h, + // offset: Offset( + // 0, + // 2, + // ), + // ), + // ], + // ), + child: Column( + children: widget.countryList + .map( + (country) => GestureDetector( + onTap: () { + setState(() { + selectedCountry = country; + }); + widget.onCountryChange?.call(country); + _closeDropdown(); + }, + child: Container( + padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 16.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + borderRadius: 16.h, + ), + child: Row( + children: [ + Utils.buildSvgWithAssets( + icon: country.iconPath, + width: 38.h, + height: 38.h, + ), + SizedBox(width: 12.h), + Text(country.displayName, + style: TextStyle( + fontSize: 14.fSize, + height: 21 / 14, + fontWeight: FontWeight.w500, + letterSpacing: -0.2, + )), + ], + ), + ), + ), + ) + .toList(), + ), + ), + ), + ), + ], + ), + ); + + Overlay.of(context)?.insert(_overlayEntry); + setState(() { + _isDropdownOpen = true; + }); + } + + void _closeDropdown() { + _overlayEntry.remove(); + setState(() { + _isDropdownOpen = false; + }); + } +} diff --git a/lib/widgets/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart similarity index 56% rename from lib/widgets/dropdown_widget.dart rename to lib/widgets/dropdown/dropdown_widget.dart index a778f2e..268e44a 100644 --- a/lib/widgets/dropdown_widget.dart +++ b/lib/widgets/dropdown/dropdown_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart' show Icons, PopupMenuItem, showMenu, Colors; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; @@ -38,10 +39,8 @@ class DropdownWidget extends StatelessWidget { alignment: Alignment.center, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: Colors.white, - borderRadius: isAllowRadius ? 15 : null, - side: isBorderAllowed - ? BorderSide(color: const Color(0xffefefef), width: 1) - : null, + borderRadius: isAllowRadius ? 15.h : null, + side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null, ), child: Column( mainAxisSize: MainAxisSize.min, @@ -57,8 +56,8 @@ class DropdownWidget extends StatelessWidget { Widget _buildLabelText() { return Text( labelText, - style: const TextStyle( - fontSize: 12, + style: TextStyle( + fontSize: 12.fSize, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2, @@ -71,33 +70,41 @@ class DropdownWidget extends StatelessWidget { return GestureDetector( onTap: isEnable ? () async { - final renderBox = context.findRenderObject() as RenderBox; - final offset = renderBox.localToGlobal(Offset.zero); - final selected = await showMenu( - context: context, - position: RelativeRect.fromLTRB( - offset.dx, - offset.dy + renderBox.size.height, - offset.dx + renderBox.size.width, - 0, - ), - items: dropdownItems - .map( - (e) => PopupMenuItem( - value: e, - child: Text(e), - ), - ) - .toList(), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ); + final renderBox = context.findRenderObject() as RenderBox; + final offset = renderBox.localToGlobal(Offset.zero); + final selected = await showMenu( + context: context, + position: RelativeRect.fromLTRB( + offset.dx, + offset.dy + renderBox.size.height, + offset.dx + renderBox.size.width, + 0, + ), + items: dropdownItems + .map( + (e) => PopupMenuItem( + value: e, + child: Text( + e, + style: TextStyle( + fontSize: 14.fSize, + height: 21 / 14, + fontWeight: FontWeight.w500, + letterSpacing: -0.2, + ), + ), + ), + ) + .toList(), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ); - if (selected != null && onChange != null) { - onChange!(selected); - } - } + if (selected != null && onChange != null) { + onChange!(selected); + } + } : null, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -105,28 +112,21 @@ class DropdownWidget extends StatelessWidget { children: [ Expanded( child: Text( - (selectedValue == null || selectedValue!.isEmpty) - ? hintText - : selectedValue!, + (selectedValue == null || selectedValue!.isEmpty) ? hintText : selectedValue!, textAlign: TextAlign.left, textDirection: TextDirection.ltr, style: TextStyle( - fontSize: 14, + fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, - color: (selectedValue != null && selectedValue!.isNotEmpty) - ? const Color(0xff2E3039) - : const Color(0xffB0B0B0), + color: (selectedValue != null && selectedValue!.isNotEmpty) ? const Color(0xff2E3039) : const Color(0xffB0B0B0), letterSpacing: -0.2, ), ), ), - if (hasSelectionCustomIcon && selectionCustomIcon != null) - Utils.buildSvgWithAssets(icon: selectionCustomIcon!) - else - const Icon(Icons.keyboard_arrow_down_outlined), + if (hasSelectionCustomIcon && selectionCustomIcon != null) Utils.buildSvgWithAssets(icon: selectionCustomIcon!) else const Icon(Icons.keyboard_arrow_down_outlined), ], ), ); } -} \ No newline at end of file +} diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index f2446a7..ca72fe8 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; // TODO: Import AppColors if bgRedColor is defined there // import 'package:hmg_patient_app_new/core/ui_utils/app_colors.dart'; @@ -54,9 +56,7 @@ class TextInputWidget extends StatelessWidget { @override Widget build(BuildContext context) { - // Assuming AppColors.bgRedColor exists, otherwise using Colors.red - final errorColor = Colors.red; // Replace with AppColors.bgRedColor if available - + final errorColor = AppColors.primaryRedColor; return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -66,7 +66,7 @@ class TextInputWidget extends StatelessWidget { alignment: Alignment.center, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: Colors.white, - borderRadius: isAllowRadius ? 15 : null, + borderRadius: isAllowRadius ? 12 : null, side: isBorderAllowed ? BorderSide(color: hasError ? errorColor : const Color(0xffefefef), width: 1) : null, ), child: Row( @@ -88,12 +88,12 @@ class TextInputWidget extends StatelessWidget { ), if (hasError && errorMessage != null) Padding( - padding: const EdgeInsets.only(top: 4.0, left: 12.0), // Adjust padding as needed + padding: EdgeInsets.only(top: 4.h, left: 12.h), // Adjust padding as needed child: Text( errorMessage!, style: TextStyle( color: errorColor, - fontSize: 12, + fontSize: 12.fSize, ), ), ), @@ -103,21 +103,24 @@ class TextInputWidget extends StatelessWidget { Widget _buildLeadingIcon(BuildContext context) { return Container( - height: 40, - width: 40, - margin: const EdgeInsets.only(right: 10), - padding: const EdgeInsets.all(8), - decoration: const BoxDecoration(color: Color(0xFFEFEFF0), borderRadius: BorderRadius.all(Radius.circular(10))), + height: 40.h, + width: 40.h, + margin: EdgeInsets.only(right: 10.h), + padding: EdgeInsets.all(8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + borderRadius: 10.h, + color: AppColors.greyColor, + ), child: Utils.buildSvgWithAssets(icon: leadingIcon!)); } Widget _buildLabelText() { return Text( labelText, - style: const TextStyle( - fontSize: 12, + style: TextStyle( + fontSize: 12.fSize, fontWeight: FontWeight.w500, - color: Color(0xff898A8D), + color: AppColors.inputLabelTextColor, letterSpacing: -0.2, height: 18 / 12, ), @@ -137,30 +140,30 @@ class TextInputWidget extends StatelessWidget { onChanged: onChange, focusNode: focusNode, autofocus: autoFocus, - style: const TextStyle( - fontSize: 14, + style: TextStyle( + fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, - color: Color(0xff2E3039), + color: AppColors.textColor, letterSpacing: -0.2, ), decoration: InputDecoration( isDense: true, hintText: hintText, - hintStyle: const TextStyle( - fontSize: 14, + hintStyle: TextStyle( + fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2, ), - prefixIconConstraints: const BoxConstraints(minWidth: 45), + prefixIconConstraints: BoxConstraints(minWidth: 45.h), prefixIcon: prefix == null ? null : Text( "+" + prefix!, - style: const TextStyle( - fontSize: 14, + style: TextStyle( + fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, color: Color(0xff2E303A), diff --git a/lib/widgets/language_switcher.dart b/lib/widgets/language_switcher.dart index 4c621b7..4a81217 100644 --- a/lib/widgets/language_switcher.dart +++ b/lib/widgets/language_switcher.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -51,18 +52,18 @@ class _LanguageSelectorState extends State { widget.onLanguageChanged(newLanguage); }, child: Container( - padding: EdgeInsets.all(8), + padding: EdgeInsets.all(8.h), decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)), child: Row( mainAxisSize: MainAxisSize.min, children: [ Utils.buildSvgWithAssets(icon: AppAssets.language), - const SizedBox(width: 6), + SizedBox(width: 6.h), Text( currentLangData['name']?.toUpperCase() ?? 'EN', style: context.dynamicTextStyle( fontWeight: FontWeight.w500, - fontSize: 14, + fontSize: 14.fSize, color: AppColors.primaryRedColor, letterSpacing: 0.1, isLanguageSwitcher: true, diff --git a/pubspec.lock b/pubspec.lock index c503e1b..679a57e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" audio_session: dependency: transitive description: @@ -874,26 +874,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -1455,10 +1455,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.4" time: dependency: transitive description: @@ -1583,18 +1583,18 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "15.0.0" web: dependency: "direct main" description: @@ -1636,5 +1636,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.7.0 <4.0.0" flutter: ">=3.29.0" From 67e580447e30a07f393a2d47137fbfefec5681ed Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Tue, 2 Sep 2025 16:09:18 +0300 Subject: [PATCH 05/16] changes --- assets/langs/ar-SA.json | 7 +- assets/langs/en-US.json | 7 +- lib/extensions/context_extensions.dart | 33 +- lib/generated/locale_keys.g.dart | 3 + lib/presentation/authentication/login.dart | 76 +++- lib/presentation/authentication/register.dart | 408 +++++++++++------- lib/theme/colors.dart | 1 + lib/widgets/appbar/app_bar_widget.dart | 39 +- .../bottomsheet/generic_bottom_sheet.dart | 58 +-- lib/widgets/buttons/custom_button.dart | 76 ++-- lib/widgets/otp/otp.dart | 227 ++++++++++ 11 files changed, 679 insertions(+), 256 deletions(-) create mode 100644 lib/widgets/otp/otp.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 57f9bcd..64477a3 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -787,7 +787,8 @@ "iAcceptTermsConditions": "أوافق على الشروط والأحكام", "alreadyHaveAccount": "هل لديك حساب بالفعل؟", "loginNow": "تسجيل الدخول الآن", - "notice": "إشعار" - - + "notice": "إشعار", + "oR": "أو", + "sendOTPWHATSAPP": "أرسل لي OTP عبر واتساب", + "sendOTPSMS": "أرسل لي OTP عبر الرسائل القصيرة" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 311db65..cd15a5a 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -774,7 +774,7 @@ "termsConditions": "These Online Services Terms of Use (Service Terms) govern certain online services provided by Dr Sulaiman Al Habib Medical Services Group Company (HMG, we, us, our)...", "receiveOtpToast": "Where would you like to receive OTP?", "enterPhoneNumber": "Enter Phone Number", - "enterEmailDesc": "Enter your email address to complete the process of creating a medical file", + "enterEmailDesc": "Enter your email address to complete the process of creating a medical file", "enterPhoneDesc": "Enter your phone number to receive OTP verification code", "pleaseChooseOption": "Please select from the below options to receive OTP", "dontHaveAccount": "Don't have an account?", @@ -783,5 +783,8 @@ "iAcceptTermsConditions": "I Accept the Terms and Conditions", "alreadyHaveAccount": "Already have an account?", "loginNow": "Login Now", - "notice": "Notice" + "notice": "Notice", + "oR": "OR", + "sendOTPWHATSAPP": "Send me OTP on Whatsapp", + "sendOTPSMS": "Send me OTP on SMS" } \ No newline at end of file diff --git a/lib/extensions/context_extensions.dart b/lib/extensions/context_extensions.dart index 5783ce3..4e9da0d 100644 --- a/lib/extensions/context_extensions.dart +++ b/lib/extensions/context_extensions.dart @@ -2,15 +2,32 @@ import 'package:flutter/material.dart'; extension ContextUtils on BuildContext { double get screenHeight => MediaQuery.of(this).size.height; + double get screenWidth => MediaQuery.of(this).size.width; + ThemeData get theme => Theme.of(this); + TextTheme get textTheme => theme.textTheme; - // TextStyle get headline1 => textTheme.headline1!; - // TextStyle get headline2 => textTheme.headline2!; - // TextStyle get headline3 => textTheme.headline3!; - // TextStyle get headline4 => textTheme.headline4!; - // TextStyle get headline5 => textTheme.headline5!; - // TextStyle get headline6 => textTheme.headline6!; - // TextStyle get bodyText1 => textTheme.bodyText1!; - // TextStyle get bodyText2 => textTheme.bodyText2!; +// TextStyle get headline1 => textTheme.headline1!; +// TextStyle get headline2 => textTheme.headline2!; +// TextStyle get headline3 => textTheme.headline3!; +// TextStyle get headline4 => textTheme.headline4!; +// TextStyle get headline5 => textTheme.headline5!; +// TextStyle get headline6 => textTheme.headline6!; +// TextStyle get bodyText1 => textTheme.bodyText1!; +// TextStyle get bodyText2 => textTheme.bodyText2!; +} + +extension ShowBottomSheet on BuildContext { + Future showBottomSheet({isScrollControlled = true, isDismissible = false, required Widget child, Color? backgroundColor, enableDra = false, useSafeArea = false}) { + return showModalBottomSheet( + context: this, + isScrollControlled: isScrollControlled, + isDismissible: isDismissible, + enableDrag: enableDra, + useSafeArea: useSafeArea, + backgroundColor: backgroundColor ?? Colors.transparent, + builder: (_) => child, + ); + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index f219526..c5f2592 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -786,5 +786,8 @@ abstract class LocaleKeys { static const alreadyHaveAccount = 'alreadyHaveAccount'; static const loginNow = 'loginNow'; static const notice = 'notice'; + static const oR = 'oR'; + static const sendOTPWHATSAPP = 'sendOTPWHATSAPP'; + static const sendOTPSMS = 'sendOTPSMS'; } diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index 87f40ca..f74348f 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -4,12 +4,14 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/context_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; @@ -77,7 +79,22 @@ class _LoginScreen extends State { text: LocaleKeys.login.tr(), icon: AppAssets.login1, iconColor: Colors.white, - onPressed: () {}, + onPressed: () { + showLoginModel(context: context); + // if (nationIdController.text.isNotEmpty) { + + // } else { + // showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterNationalIdOrFileNo, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } + }, ), SizedBox(height: 10.h), // Adjusted to sizer unit (approx 14px) Center( @@ -119,4 +136,61 @@ class _LoginScreen extends State { ), ); } + + void showLoginModel({required BuildContext context, TextEditingController? textController}) { + context.showBottomSheet( + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + child: StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + countryCode: "966", + initialPhoneNumber: "", + textController: TextEditingController(), + isEnableCountryDropdown: true, + onCountryChange: (value) {}, + onChange: (String? value) {}, + buttons: [ + Padding( + padding: EdgeInsets.only(bottom: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () {}, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 8.h), + child: LocaleKeys.oR.tr().toText16(color: AppColors.textColor), + ), + ], + ), + Padding( + padding: EdgeInsets.only(bottom: 10.h, top: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPWHATSAPP.tr(), + onPressed: () {}, + backgroundColor: Colors.white, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.textColor, + icon: AppAssets.whatsapp, + ), + ), + ], + ), + ), + ); + })); + } } diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 5564c46..0fcad5c 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -12,10 +12,12 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart' show CustomButton; import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:hmg_patient_app_new/widgets/otp/otp.dart'; class RegisterNew extends StatefulWidget { @override @@ -39,173 +41,275 @@ class _RegisterNew extends State { Widget build(BuildContext context) { AppState appState = getIt.get(); return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - appBar: CustomAppBar( - onBackPressed: () { - Navigator.of(context).pop(); - }, - onLanguageChanged: (String value) { - // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); - }, - ), - body: GestureDetector( - onTap: () { - FocusScope.of(context).unfocus(); - }, - child: ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith(overscroll: false, physics: const ClampingScrollPhysics()), - child: NotificationListener( - onNotification: (notification) { - notification.disallowIndicator(); - return true; - }, - child: SingleChildScrollView( - physics: ClampingScrollPhysics(), - padding: EdgeInsets.symmetric(horizontal: 24.h), - child: Column( + backgroundColor: AppColors.bgScaffoldColor, + appBar: CustomAppBar( + onBackPressed: () { + Navigator.of(context).pop(); + }, + onLanguageChanged: (String value) { + // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); + }, + ), + body: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + }, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith(overscroll: false, physics: const ClampingScrollPhysics()), + child: NotificationListener( + onNotification: (notification) { + notification.disallowIndicator(); + return true; + }, + child: SingleChildScrollView( + physics: ClampingScrollPhysics(), + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.showLottie(context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.h, height: 200.h, fit: BoxFit.cover, repeat: true), - SizedBox(height: 16.h), - LocaleKeys.prepareToElevate.tr().toText32(isBold: true), - SizedBox(height: 24.h), - Directionality( - textDirection: Directionality.of(context), - child: Container( - decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), - padding: EdgeInsets.symmetric(horizontal: 16.h), - child: Column( - children: [ - CustomCountryDropdown( - countryList: Country.values, - onCountryChange: (Country? value) {}, - isRtl: Directionality.of(context) == TextDirection.LTR, - ).withVerticalPadding(8.h), - - // DropdownWidget( - // labelText: LocaleKeys.country.tr(), - // hintText: LocaleKeys.ksa.tr(), - // isEnable: true, - // selectedValue: appState.getLanguageID(context) == "1" ? "selectedCountry.nameArabic" : "selectedCountry.displayName", - // dropdownItems: Country.values.map((e) => appState.getLanguageID(context) == " 1" ? e.displayName : e.displayName).toList(), - // onChange: (val) { - // if (val != null) {} - // }, - // isBorderAllowed: false, - // hasSelectionCustomIcon: true, - // isAllowRadius: false, - // padding: EdgeInsets.symmetric(vertical: 8.h), - // selectionCustomIcon: AppAssets.arrow_down, - // ).withVerticalPadding(8), - Divider(height: 1.h), - TextInputWidget( - labelText: LocaleKeys.nationalIdNumber.tr(), - hintText: "xxxxxxxxx", - controller: TextEditingController(), - isEnable: true, - prefix: null, - isAllowRadius: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - - autoFocus: true, - padding: EdgeInsets.symmetric(vertical: 8.h), - leadingIcon: AppAssets.student_card, - onChange: (value) { - print(value); - }).withVerticalPadding(8), - Divider(height: 1), - TextInputWidget( - labelText: LocaleKeys.dob.tr(), - hintText: "11 July, 1994", - controller: TextEditingController(), - isEnable: true, - prefix: null, - isAllowRadius: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - padding: EdgeInsets.symmetric(vertical: 8.h), - leadingIcon: AppAssets.birthday_cake, - onChange: (value) {}, - ).withVerticalPadding(8), - ], + Utils.showLottie(context: context, + assetPath: 'assets/animations/lottie/register.json', + width: 200.h, + height: 200.h, + fit: BoxFit.cover, + repeat: true), + SizedBox(height: 16.h), + LocaleKeys.prepareToElevate.tr().toText32(isBold: true), + SizedBox(height: 24.h), + Directionality( + textDirection: Directionality.of(context), + child: Container( + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), + padding: EdgeInsets.symmetric(horizontal: 16.h), + child: Column( + children: [ + CustomCountryDropdown( + countryList: Country.values, + onCountryChange: (Country? value) {}, + isRtl: Directionality.of(context) == TextDirection.LTR, + ).withVerticalPadding(8.h), + Divider(height: 1.h), + TextInputWidget( + labelText: LocaleKeys.nationalIdNumber.tr(), + hintText: "xxxxxxxxx", + controller: TextEditingController(), + isEnable: true, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + autoFocus: true, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.student_card, + onChange: (value) { + print(value); + }).withVerticalPadding(8), + Divider(height: 1), + TextInputWidget( + labelText: LocaleKeys.dob.tr(), + hintText: "11 July, 1994", + controller: TextEditingController(), + isEnable: true, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.birthday_cake, + onChange: (value) {}, + ).withVerticalPadding(8), + ], + ), + ), + ), + SizedBox(height: 25.h), + GestureDetector( + onTap: () {}, + child: Row( + children: [ + AnimatedContainer( + duration: const Duration(milliseconds: 200), + height: 24.h, + width: 24.h, + decoration: BoxDecoration( + color: isTermsAccepted ? const Color(0xFFE92227) : Colors.transparent, + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: isTermsAccepted ? const Color(0xFFE92227) : Colors.grey, + width: 2.h, + ), + ), + child: isTermsAccepted ? Icon(Icons.check, size: 16.fSize, color: Colors.white) : null, + ), + SizedBox(width: 12.h), + Expanded( + child: Text( + LocaleKeys.iAcceptTermsConditions.tr(), + style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + ), + ), + ], + ), + ), + SizedBox(height: 25.h), + CustomButton( + text: "Register", + icon: AppAssets.note_edit, + onPressed: () { + showRegisterModel(context: context); + }, + ), + SizedBox(height: 14), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: context.dynamicTextStyle( + color: Colors.black, + fontSize: 16.fSize, + height: 26 / 16, + fontWeight: FontWeight.w500, + ), + children: [ + TextSpan(text: LocaleKeys.alreadyHaveAccount.tr(), style: context.dynamicTextStyle()), + TextSpan(text: " "), + TextSpan( + text: LocaleKeys.loginNow.tr(), + style: context.dynamicTextStyle( + color: AppColors.primaryRedColor, + fontSize: 16.fSize, + height: 26 / 16, + fontWeight: FontWeight.w500, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ), + SizedBox(height: 30), + ], + ), ), ), ), - SizedBox(height: 25.h), - GestureDetector( - onTap: () {}, - child: Row( - children: [ - AnimatedContainer( - duration: const Duration(milliseconds: 200), - height: 24.h, - width: 24.h, - decoration: BoxDecoration( - color: isTermsAccepted ? const Color(0xFFE92227) : Colors.transparent, - borderRadius: BorderRadius.circular(6), - border: Border.all( - color: isTermsAccepted ? const Color(0xFFE92227) : Colors.grey, - width: 2.h, + )); + } + + void showRegisterModel({required BuildContext context, TextEditingController? textController}) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + builder: (bottomSheetContext) => + Padding( + padding: EdgeInsets.only(bottom: MediaQuery + .of(bottomSheetContext) + .viewInsets + .bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + countryCode: "966", + initialPhoneNumber: "", + textController: TextEditingController(), + onChange: (String? value) {}, + buttons: [ + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) => OTPVerificationPage(phoneNumber: '12234567',))); + + + // if (mobileNo.isEmpty) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else if (!Utils.validateMobileNumber(mobileNo)) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterValidMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else { + // registerUser(1); + // } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, ), ), - child: isTermsAccepted ? Icon(Icons.check, size: 16.fSize, color: Colors.white) : null, - ), - SizedBox(width: 12.h), - Expanded( - child: Text( - LocaleKeys.iAcceptTermsConditions.tr(), - style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 8.h), + child: LocaleKeys.oR.tr().toText16(color: AppColors.textColor), + ), + ], ), - ), - ], - ), - ), - SizedBox(height: 25.h), - CustomButton( - text: "Register", - icon: AppAssets.note_edit, - onPressed: () {}, - ), - SizedBox(height: 14), - Center( - child: RichText( - textAlign: TextAlign.center, - text: TextSpan( - style: context.dynamicTextStyle( - color: Colors.black, - fontSize: 16.fSize, - height: 26 / 16, - fontWeight: FontWeight.w500, - ), - children: [ - TextSpan(text: LocaleKeys.alreadyHaveAccount.tr(), style: context.dynamicTextStyle()), - TextSpan(text: " "), - TextSpan( - text: LocaleKeys.loginNow.tr(), - style: context.dynamicTextStyle( - color: AppColors.primaryRedColor, - fontSize: 16.fSize, - height: 26 / 16, - fontWeight: FontWeight.w500, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - Navigator.of(context).pop(); + Padding( + padding: EdgeInsets.only(bottom: 10.h, top: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPWHATSAPP.tr(), + onPressed: () { + // if (mobileNo.isEmpty) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else if (!Utils.validateMobileNumber(mobileNo)) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterValidMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else { + // registerUser(4); + // } + // int? val = Utils.onOtpBtnPressed(OTPType.whatsapp, mobileNo, context); + // registerUser(val); }, + backgroundColor: AppColors.whiteColor, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.textColor, + icon: AppAssets.whatsapp, + ), ), ], ), ), ), - SizedBox(height: 30), - ], - ), - ), - ),) - , - ) ); } } diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 252d887..2c8a1fc 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -29,6 +29,7 @@ class AppColors { static const Color bgGreenColor = Color(0xFF18C273); static const Color textColor = Color(0xFF2E3039); static const Color borderOnlyColor = Color(0xFF2E3039); + static const Color blackBgColor = Color(0xFF2E3039); static const blackColor = textColor; static const inputLabelTextColor = Color(0xff898A8D); diff --git a/lib/widgets/appbar/app_bar_widget.dart b/lib/widgets/appbar/app_bar_widget.dart index 2fdbc94..4bc08a9 100644 --- a/lib/widgets/appbar/app_bar_widget.dart +++ b/lib/widgets/appbar/app_bar_widget.dart @@ -11,11 +11,13 @@ import '../../generated/locale_keys.g.dart'; class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { final VoidCallback onBackPressed; final ValueChanged onLanguageChanged; + bool hideLogoAndLang; - const CustomAppBar({ + CustomAppBar({ Key? key, required this.onBackPressed, required this.onLanguageChanged, + this.hideLogoAndLang = false, }) : super(key: key); @override @@ -28,7 +30,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { leading: null, automaticallyImplyLeading: false, title: Padding( - padding: EdgeInsets.symmetric(horizontal: 10.h), + padding: EdgeInsets.symmetric(horizontal: 10.h), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -43,25 +45,26 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ), // Logo - Utils.buildSvgWithAssets( - icon: AppAssets.habiblogo, - ), + if (!hideLogoAndLang) + Utils.buildSvgWithAssets( + icon: AppAssets.habiblogo, + ), - // Language Selector - Expanded( - child: Align( - alignment: Alignment.centerRight, - child: LanguageSelector( - currentLanguage: context.locale.languageCode, - showOnlyIcon: false, - onLanguageChanged: onLanguageChanged, - languages: [ - {'code': 'ar', 'name': LocaleKeys.arabic.tr()}, - {'code': 'en', 'name': LocaleKeys.english.tr()} - ], + if (!hideLogoAndLang) + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: LanguageSelector( + currentLanguage: context.locale.languageCode, + showOnlyIcon: false, + onLanguageChanged: onLanguageChanged, + languages: [ + {'code': 'ar', 'name': LocaleKeys.arabic.tr()}, + {'code': 'en', 'name': LocaleKeys.english.tr()} + ], + ), ), ), - ), ], ), ), diff --git a/lib/widgets/bottomsheet/generic_bottom_sheet.dart b/lib/widgets/bottomsheet/generic_bottom_sheet.dart index 13a02a5..eefb187 100644 --- a/lib/widgets/bottomsheet/generic_bottom_sheet.dart +++ b/lib/widgets/bottomsheet/generic_bottom_sheet.dart @@ -3,9 +3,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; class GenericBottomSheet extends StatefulWidget { @@ -18,20 +21,21 @@ class GenericBottomSheet extends StatefulWidget { final bool isEnableCountryDropdown; final bool isFromSavedLogin; Function(String?)? onChange; + // FocusNode myFocusNode; - GenericBottomSheet( - {this.countryCode = "", - this.initialPhoneNumber = "", - required this.buttons, - this.textController, - this.isForEmail = false, - this.onCountryChange, - this.isEnableCountryDropdown = false, - this.isFromSavedLogin = false, - this.onChange, - // required this.myFocusNode - }); + GenericBottomSheet({ + this.countryCode = "", + this.initialPhoneNumber = "", + required this.buttons, + this.textController, + this.isForEmail = false, + this.onCountryChange, + this.isEnableCountryDropdown = false, + this.isFromSavedLogin = false, + this.onChange, + // required this.myFocusNode + }); @override _GenericBottomSheetState createState() => _GenericBottomSheetState(); @@ -64,11 +68,8 @@ class _GenericBottomSheetState extends State { child: Directionality( textDirection: Directionality.of(context), child: Container( - padding: const EdgeInsets.all(24), - decoration: BoxDecoration( - color: Color(0xFFF8F8FA), - borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), - ), + padding: EdgeInsets.all(24.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bgScaffoldColor, borderRadius: 16), child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, @@ -86,18 +87,17 @@ class _GenericBottomSheetState extends State { ? LocaleKeys.enterEmail.tr().toText24() : LocaleKeys.enterPhoneNumber.tr().toText24()), InkWell( - onTap: () { - Navigator.of(context).pop(); - }, - child: Padding( - padding: const EdgeInsets.only(top: 10), - child: Utils.buildSvgWithAssets( - icon: AppAssets.cross_circle, - ), - )), + onTap: () { + Navigator.of(context).pop(); + }, + child: Padding( + padding: EdgeInsets.only(top: 10.h), + child: Utils.buildSvgWithAssets(icon: AppAssets.cross_circle), + ), + ), ], ), - const SizedBox(height: 8), + SizedBox(height: 8.h), // Subtitle widget.isFromSavedLogin ? LocaleKeys.pleaseChooseOption.tr().toText16() @@ -113,7 +113,7 @@ class _GenericBottomSheetState extends State { labelText: widget.isForEmail ? LocaleKeys.email : LocaleKeys.phoneNumber, hintText: widget.isForEmail ? "demo@gmail.com" : "5xxxxxxxx", controller: widget.textController!, - padding: EdgeInsets.only(top: 8, bottom: 8, left: 8, right: 8), + padding: EdgeInsets.all(8.h), keyboardType: widget.isForEmail ? TextInputType.emailAddress : TextInputType.number, onChange: (value) { widget.textController!.text = value!; @@ -133,7 +133,7 @@ class _GenericBottomSheetState extends State { : SizedBox(), ], - SizedBox(height: 24), + SizedBox(height: 24.h), ...widget.buttons, ], ), diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index e08189a..8a08a5e 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -43,50 +43,40 @@ class CustomButton extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( - onTap: isDisabled ? null : onPressed, - child: Container( - height: height, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: isDisabled ? Colors.transparent : backgroundColor, - borderRadius: borderRadius, - side: BorderSide( - width: borderWidth, - color: isDisabled ? borderColor.withOpacity(0.5) : borderColor, - )), - child: Padding( - padding: padding, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (icon != null) - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled), - ), - Text( - text, - style: context.dynamicTextStyle( - fontSize: fontSize.fSize, - color: isDisabled ? textColor.withOpacity(0.5) : textColor, - letterSpacing: -0.4, - fontWeight: fontWeight, - ), + onTap: isDisabled ? null : onPressed, + child: Container( + height: height, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: isDisabled ? Colors.transparent : backgroundColor, + borderRadius: borderRadius, + side: BorderSide( + width: borderWidth, + color: isDisabled ? borderColor.withOpacity(0.5) : borderColor, + )), + child: Padding( + padding: padding, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (icon != null) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled), ), - ], - ), + Text( + text, + style: context.dynamicTextStyle( + fontSize: fontSize.fSize, + color: isDisabled ? textColor.withOpacity(0.5) : textColor, + letterSpacing: -0.4, + fontWeight: fontWeight, + ), + ), + ], ), - ) - - // .toSmoothContainer( - // smoothness: 1, - // side: BorderSide(width: borderWidth, color: backgroundColor), - // borderRadius: BorderRadius.circular(borderRadius * 1.2), - // foregroundDecoration: BoxDecoration( - // color: isDisabled ? backgroundColor.withOpacity(0.5) : Colors.transparent, - // borderRadius: BorderRadius.circular(borderRadius), - // ), - // ), - ); + ), + ), + ); } } diff --git a/lib/widgets/otp/otp.dart b/lib/widgets/otp/otp.dart new file mode 100644 index 0000000..5cfd3e9 --- /dev/null +++ b/lib/widgets/otp/otp.dart @@ -0,0 +1,227 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; + +class OTPVerificationPage extends StatefulWidget { + final String phoneNumber; + + const OTPVerificationPage({Key? key, required this.phoneNumber}) : super(key: key); + + @override + State createState() => _OTPVerificationPageState(); +} + +class _OTPVerificationPageState extends State { + final int _otpLength = 4; + late final List _controllers; + late final List _focusNodes; + + Timer? _resendTimer; + int _resendTime = 60; + bool _isOtpComplete = false; + + @override + void initState() { + super.initState(); + _controllers = List.generate(_otpLength, (_) => TextEditingController()); + _focusNodes = List.generate(_otpLength, (_) => FocusNode()); + _startResendTimer(); + + // Focus the first field once the screen is built + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_focusNodes.isNotEmpty) { + FocusScope.of(context).requestFocus(_focusNodes[0]); + } + }); + } + + @override + void dispose() { + for (final c in _controllers) c.dispose(); + for (final f in _focusNodes) f.dispose(); + _resendTimer?.cancel(); + super.dispose(); + } + + void _startResendTimer() { + _resendTimer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (_resendTime > 0) { + setState(() => _resendTime--); + } else { + timer.cancel(); + } + }); + } + + void _onOtpChanged(int index, String value) { + if (value.length == 1 && index < _otpLength - 1) { + _focusNodes[index + 1].requestFocus(); + } else if (value.isEmpty && index > 0) { + _focusNodes[index - 1].requestFocus(); + } + _checkOtpCompletion(); + } + + void _checkOtpCompletion() { + final isComplete = _controllers.every((c) => c.text.isNotEmpty); + + if (isComplete != _isOtpComplete) { + setState(() => _isOtpComplete = isComplete); + + if (isComplete) { + _verifyOtp(); + } + } + } + + void _resendOtp() { + if (_resendTime == 0) { + setState(() => _resendTime = 60); + _startResendTimer(); + autoFillOtp("1234"); + + // call resend API here + } + } + + String _getMaskedPhoneNumber() { + final phone = widget.phoneNumber; + return phone.length > 4 ? '05xxxxxx${phone.substring(phone.length - 2)}' : phone; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + hideLogoAndLang: true, + onBackPressed: () { + Navigator.of(context).pop(); + }, + onLanguageChanged: (lang) {}, + ), + body: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 40.h), + Text( + 'OTP Verification', + style: TextStyle(fontSize: 24.fSize, fontWeight: FontWeight.bold), + ), + SizedBox(height: 16.h), + Text( + 'We have sent you the OTP code on ${_getMaskedPhoneNumber()} via SMS for registration verification', + style: TextStyle(fontSize: 16.fSize, color: Colors.grey), + ), + SizedBox(height: 40.h), + + // OTP Input Fields + SizedBox( + height: 100, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate(_otpLength, (index) { + return ValueListenableBuilder( + valueListenable: _controllers[index], + builder: (context, value, _) { + final hasText = value.text.isNotEmpty; + + return AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + width: 70.h, + margin: EdgeInsets.symmetric(horizontal: 4.h), + decoration: RoundedRectangleBorder() + .toSmoothCornerDecoration(color: _isOtpComplete ? AppColors.successColor : (hasText ? AppColors.blackBgColor : AppColors.whiteColor), borderRadius: 16), + child: Center( + child: TextField( + controller: _controllers[index], + focusNode: _focusNodes[index], + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + maxLength: 1, + style: TextStyle( + fontSize: 40.fSize, + fontWeight: FontWeight.bold, + color: AppColors.whiteColor, + ), + decoration: InputDecoration( + counterText: '', + filled: true, + fillColor: Colors.transparent, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(18), + borderSide: BorderSide.none, + ), + ), + onChanged: (v) => _onOtpChanged(index, v), + ), + ), + ); + }, + ); + }), + ), + ), + + const SizedBox(height: 32), + + // Resend OTP + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text("Didn't receive it? "), + if (_resendTime > 0) + Text( + 'resend in (${_resendTime.toString().padLeft(2, '0')}:00). ', + style: const TextStyle(color: Colors.grey), + ) + else + GestureDetector( + onTap: _resendOtp, + child: const Text( + 'Resend', + style: TextStyle( + color: AppColors.primaryRedColor, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } + + void _verifyOtp() { + final otp = _controllers.map((c) => c.text).join(); + debugPrint('Verifying OTP: $otp'); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Verifying OTP: $otp')), + ); + } + + /// Auto fill OTP into text fields + void autoFillOtp(String otp) { + if (otp.length != _otpLength) return; + + for (int i = 0; i < _otpLength; i++) { + _controllers[i].text = otp[i]; + } + + // Move focus to the last field + _focusNodes[_otpLength - 1].requestFocus(); + + // Trigger completion check and color update + _checkOtpCompletion(); + } +} From d1e1154bdb781fdccf12e0a5b3114285a4380734 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Tue, 2 Sep 2025 17:00:03 +0300 Subject: [PATCH 06/16] changes --- lib/core/utils/utils.dart | 74 +++++-- lib/extensions/string_extensions.dart | 36 ++-- .../authentication/authentication_repo.dart | 6 +- .../authentication_view_model.dart | 193 +++++++++--------- lib/presentation/authentication/login.dart | 9 +- lib/presentation/authentication/register.dart | 4 +- lib/presentation/home/landing_page.dart | 4 +- .../medical_file/medical_file_page.dart | 1 - .../bottomsheet/generic_bottom_sheet.dart | 2 +- .../dropdown/country_dropdown_widget.dart | 8 +- 10 files changed, 193 insertions(+), 144 deletions(-) diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index bdca5ff..ed4770d 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart'; @@ -59,14 +60,13 @@ class Utils { // )); return !isAddHours ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") - .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) + .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) : DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") - .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( - Duration( - hours: isAddHours ? 3 : 0, - ), - )); - ; + .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( + Duration( + hours: isAddHours ? 3 : 0, + ), + )); } static String convertStringToDateTime(String dateTimeString) { @@ -209,12 +209,12 @@ class Utils { builder: (BuildContext context) => LoadingDialog(), ) .then((value) { - _isLoadingVisible = false; - }) + _isLoadingVisible = false; + }) .catchError((e) {}) .onError( (error, stackTrace) {}, - ); + ); } static void hideLoading() { @@ -236,11 +236,12 @@ class Utils { showDialog( barrierDismissible: false, context: context, - builder: (cxt) => ConfirmDialog( - title: title!, - message: message!, - onTap: onTap, - ), + builder: (cxt) => + ConfirmDialog( + title: title!, + message: message!, + onTap: onTap, + ), ); } @@ -371,7 +372,9 @@ class Utils { static String formatHijriDateToDisplay(String hijriDateString) { try { // Assuming hijriDateString is in the format yyyy-MM-dd - final datePart = hijriDateString.split("T").first; + final datePart = hijriDateString + .split("T") + .first; final parts = datePart.split('-'); if (parts.length != 3) return ""; @@ -429,8 +432,14 @@ class Utils { void Function(LottieComposition)? onLoaded, }) { return Lottie.asset(assetPath, - height: height ?? MediaQuery.of(context).size.height * 0.26, - width: width ?? MediaQuery.of(context).size.width, + height: height ?? MediaQuery + .of(context) + .size + .height * 0.26, + width: width ?? MediaQuery + .of(context) + .size + .width, fit: fit, alignment: alignment, repeat: repeat, @@ -487,9 +496,13 @@ class Utils { ], ); } + static Future isGoogleServicesAvailable() async { GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); - String status = availability.toString().split('.').last; + String status = availability + .toString() + .split('.') + .last; if (status == "success") { return true; } @@ -510,3 +523,26 @@ class Utils { return crypto.md5.convert(utf8.encode(input)).toString(); } } + +class ValidationUtils { + static DialogService dialogService = getIt.get(); + + + static bool isValidatePhoneAndId({ + String? nationalId, + String? phoneNumber + }) { + if (nationalId == null || nationalId.isEmpty) { + dialogService.showErrorDialog(message: "Please enter a valid national ID or file number", onOkPressed: () {}); + return false; + } + + if (phoneNumber == null || phoneNumber.isEmpty) { + dialogService.showErrorDialog(message: "Please enter a valid phone number", onOkPressed: () {}); + return false; + } + return true; + } +} + + diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 187939e..cbfac11 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -235,10 +235,12 @@ extension EmailValidator on String { ); Widget toText36({Color? color, bool isBold = false, bool isCenter = false}) => Text( - this, - textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 47 / 36, color: color ?? AppColors.blackColor, fontSize: 36.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), - ); + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: 47 / 36, color: color ?? AppColors.blackColor, fontSize: 36.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); + + Widget toText44({Color? color, bool isBold = false}) => Text( this, @@ -381,53 +383,55 @@ class FontUtils { } } -extension CountryExtension on Country { + +extension CountryExtension on CountryEnum { String get displayName { switch (this) { - case Country.saudiArabia: + case CountryEnum.saudiArabia: return "Kingdom Of Saudi Arabia"; - case Country.unitedArabEmirates: + case CountryEnum.unitedArabEmirates: return "United Arab Emirates"; } } String get nameArabic { switch (this) { - case Country.saudiArabia: + case CountryEnum.saudiArabia: return "المملكة العربية السعودية"; - case Country.unitedArabEmirates: + case CountryEnum.unitedArabEmirates: return "الإمارات العربية المتحدة"; } } String get iconPath { switch (this) { - case Country.saudiArabia: + case CountryEnum.saudiArabia: return AppAssets.ksa; - case Country.unitedArabEmirates: + case CountryEnum.unitedArabEmirates: return AppAssets.uae; } } String get countryCode { switch (this) { - case Country.saudiArabia: + case CountryEnum.saudiArabia: return "966"; - case Country.unitedArabEmirates: + case CountryEnum.unitedArabEmirates: return "971"; } } - static Country fromDisplayName(String name) { + static CountryEnum fromDisplayName(String name) { switch (name) { case "Kingdom Of Saudi Arabia": case "المملكة العربية السعودية": - return Country.saudiArabia; + return CountryEnum.saudiArabia; case "United Arab Emirates": case "الإمارات العربية المتحدة": - return Country.unitedArabEmirates; + return CountryEnum.unitedArabEmirates; default: throw Exception("Invalid country name"); } } } + diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index 7307013..0be6eaf 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -20,8 +20,7 @@ abstract class AuthenticationRepo { required CheckPatientAuthenticationReq checkPatientAuthenticationReq, }); - Future>> sendActivationCodeRegister( - {required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}); + Future>> sendActivationCodeRegister({required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}); } class AuthenticationRepoImp implements AuthenticationRepo { @@ -114,8 +113,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { } @override - Future>> sendActivationCodeRegister( - {required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}) async { + Future>> sendActivationCodeRegister({required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}) async { int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1; //TODO : We will use all these from AppState directly in the ApiClient diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index ed77aca..f192db2 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_patient_authentication_request_model.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; @@ -23,9 +24,15 @@ class AuthenticationViewModel extends ChangeNotifier { final TextEditingController nationalIdController = TextEditingController(); final TextEditingController phoneNumberController = TextEditingController(); + void login() { + if (ValidationUtils.isValidatePhoneAndId(nationalId: nationalIdController.text, phoneNumber: phoneNumberController.text)) { + } else { + + } + } + Future selectDeviceImei({Function(dynamic)? onSuccess, Function(String)? onError}) async { - String firebaseToken = - "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc"; + String firebaseToken = "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc"; final result = await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken); result.fold( @@ -40,96 +47,96 @@ class AuthenticationViewModel extends ChangeNotifier { ); } - // Future checkUserAuthentication({Function(dynamic)? onSuccess, Function(String)? onError}) async { - // CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome( - // phoneNumber: '0567184134', - // otpTypeEnum: OTPTypeEnum.sms, - // deviceToken: 'dummyDeviceToken123', - // patientOutSA: true, - // loginTokenID: 'dummyLoginToken456', - // registeredData: null, - // patientId: 12345, - // nationIdText: '1234567890', - // countryCode: 'SA', - // ); - // - // final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); - // result.fold( - // (failure) async => await errorHandlerService.handleError(failure: failure), - // (apiResponse) { - // if (apiResponse.data['isSMSSent']) { - // // TODO: set this in AppState - // // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']); - // // loginTokenID = value['LogInTokenID'], - // // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), - // sendActivationCode(type); - // } else { - // if (apiResponse.data['IsAuthenticated']) { - // checkActivationCode(onWrongActivationCode: (String? message) {}); - // } - // } - // }, - // ); - // } +// Future checkUserAuthentication({Function(dynamic)? onSuccess, Function(String)? onError}) async { +// CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome( +// phoneNumber: '0567184134', +// otpTypeEnum: OTPTypeEnum.sms, +// deviceToken: 'dummyDeviceToken123', +// patientOutSA: true, +// loginTokenID: 'dummyLoginToken456', +// registeredData: null, +// patientId: 12345, +// nationIdText: '1234567890', +// countryCode: 'SA', +// ); +// +// final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq); +// result.fold( +// (failure) async => await errorHandlerService.handleError(failure: failure), +// (apiResponse) { +// if (apiResponse.data['isSMSSent']) { +// // TODO: set this in AppState +// // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']); +// // loginTokenID = value['LogInTokenID'], +// // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), +// sendActivationCode(type); +// } else { +// if (apiResponse.data['IsAuthenticated']) { +// checkActivationCode(onWrongActivationCode: (String? message) {}); +// } +// } +// }, +// ); +// } - // Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async { - // var request = RequestUtils.getCommonRequestAuthProvider( - // otpTypeEnum: otpTypeEnum, - // registeredData: null, - // deviceToken: "dummyLoginToken456", - // mobileNumber: "0567184134", - // zipCode: "SA", - // patientOutSA: true, - // loginTokenID: "dummyLoginToken456", - // selectedOption: selectedOption, - // patientId: 12345, - // ); - // - // request.sMSSignature = await SMSOTP.getSignature(); - // selectedOption = type; - // // GifLoaderDialogUtils.showMyDialog(context); - // if (healthId != null || isDubai) { - // if (!isDubai) { - // request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob)); - // } - // request.healthId = healthId; - // request.isHijri = isHijri; - // await this.apiClient.sendActivationCodeRegister(request).then((result) { - // // GifLoaderDialogUtils.hideDialog(context); - // if (result != null && result['isSMSSent'] == true) { - // this.startSMSService(type); - // } - // }).catchError((r) { - // GifLoaderDialogUtils.hideDialog(context); - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: r.toString(), - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // )); - // // AppToast.showErrorToast(message: r); - // }); - // } else { - // request.dob = ""; - // request.healthId = ""; - // request.isHijri = 0; - // await this.authService.sendActivationCode(request).then((result) { - // GifLoaderDialogUtils.hideDialog(context); - // if (result != null && result['isSMSSent'] == true) { - // this.startSMSService(type); - // } - // }).catchError((r) { - // GifLoaderDialogUtils.hideDialog(context); - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: r.toString(), - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // )); - // // AppToast.showErrorToast(message: r.toString()); - // }); - // } - // } +// Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async { +// var request = RequestUtils.getCommonRequestAuthProvider( +// otpTypeEnum: otpTypeEnum, +// registeredData: null, +// deviceToken: "dummyLoginToken456", +// mobileNumber: "0567184134", +// zipCode: "SA", +// patientOutSA: true, +// loginTokenID: "dummyLoginToken456", +// selectedOption: selectedOption, +// patientId: 12345, +// ); +// +// request.sMSSignature = await SMSOTP.getSignature(); +// selectedOption = type; +// // GifLoaderDialogUtils.showMyDialog(context); +// if (healthId != null || isDubai) { +// if (!isDubai) { +// request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob)); +// } +// request.healthId = healthId; +// request.isHijri = isHijri; +// await this.apiClient.sendActivationCodeRegister(request).then((result) { +// // GifLoaderDialogUtils.hideDialog(context); +// if (result != null && result['isSMSSent'] == true) { +// this.startSMSService(type); +// } +// }).catchError((r) { +// GifLoaderDialogUtils.hideDialog(context); +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: r.toString(), +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// )); +// // AppToast.showErrorToast(message: r); +// }); +// } else { +// request.dob = ""; +// request.healthId = ""; +// request.isHijri = 0; +// await this.authService.sendActivationCode(request).then((result) { +// GifLoaderDialogUtils.hideDialog(context); +// if (result != null && result['isSMSSent'] == true) { +// this.startSMSService(type); +// } +// }).catchError((r) { +// GifLoaderDialogUtils.hideDialog(context); +// context.showBottomSheet( +// child: ExceptionBottomSheet( +// message: r.toString(), +// onOkPressed: () { +// Navigator.of(context).pop(); +// }, +// )); +// // AppToast.showErrorToast(message: r.toString()); +// }); +// } +// } } diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index f74348f..2771b36 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/context_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/authentication/register.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -14,6 +15,7 @@ import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:provider/provider.dart'; class LoginScreen extends StatefulWidget { @override @@ -33,6 +35,7 @@ class _LoginScreen extends State { @override Widget build(BuildContext context) { + AuthenticationViewModel authVm = context.read(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, appBar: CustomAppBar( @@ -61,7 +64,7 @@ class _LoginScreen extends State { TextInputWidget( labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", hintText: "xxxxxxxxx", - controller: TextEditingController(), + controller: authVm.nationalIdController, keyboardType: TextInputType.number, isEnable: true, prefix: null, @@ -80,7 +83,7 @@ class _LoginScreen extends State { icon: AppAssets.login1, iconColor: Colors.white, onPressed: () { - showLoginModel(context: context); + showLoginModel(context: context, textController: authVm.phoneNumberController); // if (nationIdController.text.isNotEmpty) { // } else { @@ -150,7 +153,7 @@ class _LoginScreen extends State { child: GenericBottomSheet( countryCode: "966", initialPhoneNumber: "", - textController: TextEditingController(), + textController: textController, isEnableCountryDropdown: true, onCountryChange: (value) {}, onChange: (String? value) {}, diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 0fcad5c..b445631 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -85,8 +85,8 @@ class _RegisterNew extends State { child: Column( children: [ CustomCountryDropdown( - countryList: Country.values, - onCountryChange: (Country? value) {}, + countryList: CountryEnum.values, + onCountryChange: (CountryEnum? value) {}, isRtl: Directionality.of(context) == TextDirection.LTR, ).withVerticalPadding(8.h), Divider(height: 1.h), diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 55abb00..f2017b2 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -51,7 +51,9 @@ class _LandingPageState extends State { CustomButton( text: LocaleKeys.loginOrRegister.tr(context: context), onPressed: () async { - await authenticationViewModel.selectDeviceImei(); + + // await authenticationViewModel.selectDeviceImei(); + Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => LoginScreen())); }, backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 55ca84b..ba274bf 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -40,7 +40,6 @@ class MedicalFilePage extends StatelessWidget { isAllowLeadingIcon: true, padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h), leadingIcon: AppAssets.student_card, - hasError: true, ), SizedBox(height: 16.h), Container( diff --git a/lib/widgets/bottomsheet/generic_bottom_sheet.dart b/lib/widgets/bottomsheet/generic_bottom_sheet.dart index eefb187..1d07099 100644 --- a/lib/widgets/bottomsheet/generic_bottom_sheet.dart +++ b/lib/widgets/bottomsheet/generic_bottom_sheet.dart @@ -17,7 +17,7 @@ class GenericBottomSheet extends StatefulWidget { final List buttons; TextEditingController? textController; final bool isForEmail; - Function(Country)? onCountryChange; + Function(CountryEnum)? onCountryChange; final bool isEnableCountryDropdown; final bool isFromSavedLogin; Function(String?)? onChange; diff --git a/lib/widgets/dropdown/country_dropdown_widget.dart b/lib/widgets/dropdown/country_dropdown_widget.dart index 81ac60a..5e4bddf 100644 --- a/lib/widgets/dropdown/country_dropdown_widget.dart +++ b/lib/widgets/dropdown/country_dropdown_widget.dart @@ -7,8 +7,8 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; class CustomCountryDropdown extends StatefulWidget { - final List countryList; - final Function(Country)? onCountryChange; + final List countryList; + final Function(CountryEnum)? onCountryChange; final bool isRtl; const CustomCountryDropdown({ @@ -23,14 +23,14 @@ class CustomCountryDropdown extends StatefulWidget { } class _CustomCountryDropdownState extends State { - Country? selectedCountry; + CountryEnum? selectedCountry; late OverlayEntry _overlayEntry; bool _isDropdownOpen = false; @override void initState() { super.initState(); - selectedCountry = Country.saudiArabia; + selectedCountry = CountryEnum.saudiArabia; } @override From 7321da67e370c7161214a67e66b69e9ac8d939d8 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 3 Sep 2025 10:15:07 +0300 Subject: [PATCH 07/16] register step 2 & fixes --- assets/json/countriesList.json | 1212 +++++++++++++++++ assets/langs/ar-SA.json | 7 +- assets/langs/en-US.json | 7 +- .../nationality_country_model.dart | 29 + lib/extensions/string_extensions.dart | 103 ++ lib/generated/locale_keys.g.dart | 4 + lib/presentation/authentication/register.dart | 47 +- .../authentication/register_step2.dart | 311 +++++ lib/presentation/home/landing_page.dart | 3 +- lib/theme/colors.dart | 4 + lib/widgets/otp/otp.dart | 3 + pubspec.yaml | 1 + 12 files changed, 1703 insertions(+), 28 deletions(-) create mode 100644 assets/json/countriesList.json create mode 100644 lib/core/common_models/nationality_country_model.dart create mode 100644 lib/presentation/authentication/register_step2.dart diff --git a/assets/json/countriesList.json b/assets/json/countriesList.json new file mode 100644 index 0000000..25a65b3 --- /dev/null +++ b/assets/json/countriesList.json @@ -0,0 +1,1212 @@ +[ + { + "ID": "AFG", + "Name": "Afghan", + "NameN": "أفغاني" + }, + { + "ID": "CAF", + "Name": "African", + "NameN": "أفريقي" + }, + { + "ID": "ALB", + "Name": "Albanian", + "NameN": "ألباني" + }, + { + "ID": "DZA", + "Name": "Algerian", + "NameN": "جزائري" + }, + { + "ID": "USA", + "Name": "American", + "NameN": "أمريكي" + }, + { + "ID": "UMI", + "Name": "American (Minor Outlying Islands)", + "NameN": "أمريكي (الجزر الصغرى النائية)" + }, + { + "ID": "ASM", + "Name": "American Samoa", + "NameN": "ساموا الأمريكية" + }, + { + "ID": "AND", + "Name": "Andorrian", + "NameN": "أندوري" + }, + { + "ID": "AGO", + "Name": "Angolan", + "NameN": "أنغولي" + }, + { + "ID": "AIA", + "Name": "Anguillan", + "NameN": "أنغيلي" + }, + { + "ID": "ATA", + "Name": "Antarctica", + "NameN": "أنتاركتيكا" + }, + { + "ID": "ATG", + "Name": "Antiguans, Barbudans", + "NameN": "أنتيغوي، باربودي" + }, + { + "ID": "ARG", + "Name": "Argentine", + "NameN": "أرجنتيني" + }, + { + "ID": "ARM", + "Name": "Armenian", + "NameN": "أرمني" + }, + { + "ID": "ABW", + "Name": "Arubian", + "NameN": "أروبي" + }, + { + "ID": "AUS", + "Name": "Australian", + "NameN": "أسترالي" + }, + { + "ID": "AUT", + "Name": "Austrian", + "NameN": "نمساوي" + }, + { + "ID": "AZE", + "Name": "Azeri", + "NameN": "أذربيجاني" + }, + { + "ID": "BHS", + "Name": "Bahamian", + "NameN": "باهامي" + }, + { + "ID": "BHR", + "Name": "Bahraini", + "NameN": "بحريني" + }, + { + "ID": "BRB", + "Name": "Barbadian or Bajuns", + "NameN": "باربادوسي" + }, + { + "ID": "BLR", + "Name": "Belarusian", + "NameN": "بيلاروسي" + }, + { + "ID": "BEL", + "Name": "Belgian", + "NameN": "بلجيكي" + }, + { + "ID": "BLZ", + "Name": "Belizean", + "NameN": "بليزي" + }, + { + "ID": "BGD", + "Name": "Bengali", + "NameN": "بنغلاديشي" + }, + { + "ID": "BEN", + "Name": "Beninese", + "NameN": "بنيني" + }, + { + "ID": "BMU", + "Name": "Bermudian", + "NameN": "برمودي" + }, + { + "ID": "BTN", + "Name": "Bhutanese", + "NameN": "بوتاني" + }, + { + "ID": "BOL", + "Name": "Bolivian", + "NameN": "بوليفي" + }, + { + "ID": "BIH", + "Name": "Bosnian And Herzegovinian", + "NameN": "بوسني وهرسكي" + }, + { + "ID": "BWA", + "Name": "Botswanian", + "NameN": "بوتسواني" + }, + { + "ID": "BVT", + "Name": "Bouvet Island (Bouvetoya)", + "NameN": "جزيرة بوفيه" + }, + { + "ID": "BRA", + "Name": "Brazilian", + "NameN": "برازيلي" + }, + { + "ID": "GBR", + "Name": "British", + "NameN": "بريطاني" + }, + { + "ID": "IOT", + "Name": "British (Chagos Archipelago)", + "NameN": "بريطاني (أرخبيل شاغوس)" + }, + { + "ID": "VGB", + "Name": "British (Virgin Islands)", + "NameN": "بريطاني (الجزر العذراء)" + }, + { + "ID": "BRN", + "Name": "Bruneian", + "NameN": "بروني" + }, + { + "ID": "BGR", + "Name": "Bulgarian", + "NameN": "بلغاري" + }, + { + "ID": "MMR", + "Name": "Burmese or Myanmarese", + "NameN": "بورمي أو ميانماري" + }, + { + "ID": "BFA", + "Name": "Burnikabe", + "NameN": "بوركيني" + }, + { + "ID": "BDI", + "Name": "Burundi", + "NameN": "بوروندي" + }, + { + "ID": "KHM", + "Name": "Cambodian", + "NameN": "كمبودي" + }, + { + "ID": "CMR", + "Name": "Cameroonian", + "NameN": "كاميروني" + }, + { + "ID": "CAN", + "Name": "Canadian", + "NameN": "كندي" + }, + { + "ID": "CPV", + "Name": "Cape Verdian or Cape Verdean", + "NameN": "رأس أخضري" + }, + { + "ID": "CYM", + "Name": "Cayman Islander", + "NameN": "جزر كايماني" + }, + { + "ID": "TCD", + "Name": "Chadian", + "NameN": "تشادي" + }, + { + "ID": "CHL", + "Name": "Chilean", + "NameN": "تشيلي" + }, + { + "ID": "HKG", + "Name": "Chinese (Hong Kong)", + "NameN": "صيني (هونغ كونغ)" + }, + { + "ID": "MAC", + "Name": "Chinese (Macao)", + "NameN": "صيني (ماكاو)" + }, + { + "ID": "CHN", + "Name": "Chinese", + "NameN": "صيني" + }, + { + "ID": "CXR", + "Name": "Christmas Island", + "NameN": "جزيرة الكريسماس" + }, + { + "ID": "CCK", + "Name": "Cocos (Keeling) Islands", + "NameN": "جزر كوكوس (كيلنغ)" + }, + { + "ID": "COL", + "Name": "Colombian", + "NameN": "كولومبي" + }, + { + "ID": "COM", + "Name": "Comoran", + "NameN": "قمري" + }, + { + "ID": "COG", + "Name": "Congolese", + "NameN": "كونغولي" + }, + { + "ID": "COD", + "Name": "Congolese (Zaire)", + "NameN": "كونغولي (زائير)" + }, + { + "ID": "COK", + "Name": "Cook Islander", + "NameN": "جزر كوك" + }, + { + "ID": "CRI", + "Name": "Costa Rican", + "NameN": "كوستاريكي" + }, + { + "ID": "HRV", + "Name": "Croat", + "NameN": "كرواتي" + }, + { + "ID": "CUB", + "Name": "Cuban", + "NameN": "كوبي" + }, + { + "ID": "CYP", + "Name": "Cypriot, Greek", + "NameN": "قبرصي، يوناني" + }, + { + "ID": "CZE", + "Name": "Czech", + "NameN": "تشيكي" + }, + { + "ID": "DNK", + "Name": "Danish", + "NameN": "دنماركي" + }, + { + "ID": "DJI", + "Name": "Djibouti", + "NameN": "جيبوتي" + }, + { + "ID": "DOM", + "Name": "Dominican", + "NameN": "دومينيكاني" + }, + { + "ID": "DMA", + "Name": "Dominican (Commonwealth)", + "NameN": "دومينيكي (الكومنولث)" + }, + { + "ID": "ANT", + "Name": "Dutch (Antilles)", + "NameN": "هولندي (الأنتيل)" + }, + { + "ID": "NLD", + "Name": "Dutch (Netherlands)", + "NameN": "هولندي (هولندا)" + }, + { + "ID": "ECU", + "Name": "Ecuadorean", + "NameN": "إكوادوري" + }, + { + "ID": "EGY", + "Name": "Egyptians", + "NameN": "مصري" + }, + { + "ID": "ARE", + "Name": "Emirati", + "NameN": "إماراتي" + }, + { + "ID": "GNQ", + "Name": "Equatorial Guinean or Equatoguinean", + "NameN": "غيني استوائي" + }, + { + "ID": "ERI", + "Name": "Eritrean", + "NameN": "إريتري" + }, + { + "ID": "EST", + "Name": "Estonian", + "NameN": "إستوني" + }, + { + "ID": "ETH", + "Name": "Ethiopian", + "NameN": "إثيوبي" + }, + { + "ID": "FRO", + "Name": "Faeroe Islander", + "NameN": "جزر فارو" + }, + { + "ID": "FLK", + "Name": "Falkland Islander", + "NameN": "جزر فوكلاند" + }, + { + "ID": "FJI", + "Name": "Fijian", + "NameN": "فيجي" + }, + { + "ID": "PHL", + "Name": "Filipino", + "NameN": "فلبيني" + }, + { + "ID": "FIN", + "Name": "Finnish", + "NameN": "فنلندي" + }, + { + "ID": "FRA", + "Name": "French", + "NameN": "فرنسي" + }, + { + "ID": "GUF", + "Name": "French Guianian", + "NameN": "غويانا الفرنسية" + }, + { + "ID": "PYF", + "Name": "French Polynesian", + "NameN": "بولينيزيا الفرنسية" + }, + { + "ID": "ATF", + "Name": "French Southern Territories", + "NameN": "الأقاليم الجنوبية الفرنسية" + }, + { + "ID": "GAB", + "Name": "Gabonese", + "NameN": "غابوني" + }, + { + "ID": "GMB", + "Name": "Gambian", + "NameN": "غامبي" + }, + { + "ID": "GEO", + "Name": "Georgian", + "NameN": "جورجي" + }, + { + "ID": "SGS", + "Name": "Georgian (South)", + "NameN": "جورجي (جنوبي)" + }, + { + "ID": "DEU", + "Name": "German", + "NameN": "ألماني" + }, + { + "ID": "GHA", + "Name": "Ghanaian", + "NameN": "غاني" + }, + { + "ID": "GIB", + "Name": "Gibraltarian", + "NameN": "جبل طارقي" + }, + { + "ID": "GRC", + "Name": "Greek", + "NameN": "يوناني" + }, + { + "ID": "GRL", + "Name": "Greenlander", + "NameN": "غرينلاندي" + }, + { + "ID": "GRD", + "Name": "Grenadian or Grenadan", + "NameN": "غرينادي" + }, + { + "ID": "GLP", + "Name": "Guadeloupian", + "NameN": "غوادلوبي" + }, + { + "ID": "GUM", + "Name": "Guamanian", + "NameN": "غوامي" + }, + { + "ID": "GTM", + "Name": "Guatemalan", + "NameN": "غواتيمالي" + }, + { + "ID": "GNB", + "Name": "Guinea-Bissauan", + "NameN": "غيني بيساوي" + }, + { + "ID": "GIN", + "Name": "Guinean", + "NameN": "غيني" + }, + { + "ID": "GUY", + "Name": "Guyanese", + "NameN": "غيانيزي" + }, + { + "ID": "HTI", + "Name": "Haitian", + "NameN": "هايتيني" + }, + { + "ID": "HMD", + "Name": "Heard and McDonald Islands", + "NameN": "جزر هيرد وماكدونالد" + }, + { + "ID": "HND", + "Name": "Honduran", + "NameN": "هندوراسي" + }, + { + "ID": "HUN", + "Name": "Hungarian", + "NameN": "مجري" + }, + { + "ID": "ISL", + "Name": "Icelander", + "NameN": "آيسلندي" + }, + { + "ID": "KIR", + "Name": "I-Kiribati", + "NameN": "كيريباتي" + }, + { + "ID": "IND", + "Name": "Indian", + "NameN": "هندي" + }, + { + "ID": "IDN", + "Name": "Indonesian", + "NameN": "إندونيسي" + }, + { + "ID": "IRN", + "Name": "Iranian", + "NameN": "إيراني" + }, + { + "ID": "IRQ", + "Name": "Iraqi", + "NameN": "عراقي" + }, + { + "ID": "IRL", + "Name": "Irish", + "NameN": "إيرلندي" + }, + { + "ID": "ITA", + "Name": "Italian", + "NameN": "إيطالي" + }, + { + "ID": "CIV", + "Name": "Ivoirian", + "NameN": "إيفواري" + }, + { + "ID": "JAM", + "Name": "Jamaican", + "NameN": "جامايكي" + }, + { + "ID": "JPN", + "Name": "Japanese", + "NameN": "ياباني" + }, + { + "ID": "JOR", + "Name": "Jordanian", + "NameN": "أردني" + }, + { + "ID": "KAZ", + "Name": "Kazakhstani", + "NameN": "كازاخستاني" + }, + { + "ID": "KEN", + "Name": "Kenyan", + "NameN": "كيني" + }, + { + "ID": "KGZ", + "Name": "Kirghiz", + "NameN": "قيرغيزي" + }, + { + "ID": "KOR", + "Name": "Korean", + "NameN": "كوري" + }, + { + "ID": "PRK", + "Name": "Korean", + "NameN": "كوري" + }, + { + "ID": "KWT", + "Name": "Kuwaiti", + "NameN": "كويتي" + }, + { + "ID": "LAO", + "Name": "Laotian", + "NameN": "لاوسي" + }, + { + "ID": "LVA", + "Name": "Latvian", + "NameN": "لاتفي" + }, + { + "ID": "LBN", + "Name": "Lebanese", + "NameN": "لبناني" + }, + { + "ID": "LBR", + "Name": "Liberian", + "NameN": "ليبيري" + }, + { + "ID": "LBY", + "Name": "Libyan", + "NameN": "ليبي" + }, + { + "ID": "LIE", + "Name": "Liechtensteiner", + "NameN": "ليختنشتايني" + }, + { + "ID": "LTU", + "Name": "Lithuanian", + "NameN": "ليتواني" + }, + { + "ID": "LUX", + "Name": "Luxembourger", + "NameN": "لوكسمبورغي" + }, + { + "ID": "MKD", + "Name": "Macedonia, the former Yugoslav Republic of", + "NameN": "مقدوني، جمهورية يوغوسلافيا السابقة" + }, + { + "ID": "MYT", + "Name": "Mahorais", + "NameN": "مايوتي" + }, + { + "ID": "MDG", + "Name": "Malagasy", + "NameN": "مدغشقري" + }, + { + "ID": "MWI", + "Name": "Malawian", + "NameN": "مالاوي" + }, + { + "ID": "MYS", + "Name": "Malaysian", + "NameN": "ماليزي" + }, + { + "ID": "MDV", + "Name": "Maldivan", + "NameN": "مالديفي" + }, + { + "ID": "MLI", + "Name": "Malian", + "NameN": "مالي" + }, + { + "ID": "MLT", + "Name": "Maltese", + "NameN": "مالطي" + }, + { + "ID": "MHL", + "Name": "Marshallese", + "NameN": "مارشالي" + }, + { + "ID": "MTQ", + "Name": "Martiniquais", + "NameN": "مارتينيكي" + }, + { + "ID": "MRT", + "Name": "Mauritanian", + "NameN": "موريتاني" + }, + { + "ID": "MUS", + "Name": "Mauritian", + "NameN": "موريشيوسي" + }, + { + "ID": "MEX", + "Name": "Mexican", + "NameN": "مكسيكي" + }, + { + "ID": "FSM", + "Name": "Micronesian", + "NameN": "ميكرونيزي" + }, + { + "ID": "MDA", + "Name": "Moldovian", + "NameN": "مولدوفي" + }, + { + "ID": "MCO", + "Name": "Monegasque or Monacan", + "NameN": "موناكي" + }, + { + "ID": "MNG", + "Name": "Mongolian", + "NameN": "منغولي" + }, + { + "ID": "MNE", + "Name": "Montenegrin", + "NameN": "جبل أسودي" + }, + { + "ID": "MSR", + "Name": "Montserratian", + "NameN": "مونتسيراتي" + }, + { + "ID": "MAR", + "Name": "Moroccan", + "NameN": "مغربي" + }, + { + "ID": "LSO", + "Name": "Mosotho", + "NameN": "ليسوتي" + }, + { + "ID": "MOZ", + "Name": "Mozambican", + "NameN": "موزمبيقي" + }, + { + "ID": "NAM", + "Name": "Namibian", + "NameN": "ناميبي" + }, + { + "ID": "NRU", + "Name": "Nauruan", + "NameN": "ناوروي" + }, + { + "ID": "NPL", + "Name": "Nepalese", + "NameN": "نيبالي" + }, + { + "ID": "NCL", + "Name": "New Caledonian", + "NameN": "كاليدوني جديد" + }, + { + "ID": "NZL", + "Name": "New Zealand", + "NameN": "نيوزيلندي" + }, + { + "ID": "VUT", + "Name": "Ni- Vanuatu", + "NameN": "فانواتي" + }, + { + "ID": "NIC", + "Name": "Nicaraguan", + "NameN": "نيكاراغوي" + }, + { + "ID": "NGA", + "Name": "Nigerian", + "NameN": "نيجيري" + }, + { + "ID": "NER", + "Name": "Nigerien", + "NameN": "نيجري" + }, + { + "ID": "NIU", + "Name": "Niuean", + "NameN": "نيوي" + }, + { + "ID": "VAT", + "Name": "None (Vatican City)", + "NameN": "لا شيء (مدينة الفاتيكان)" + }, + { + "ID": "NFK", + "Name": "Norfolk Islander", + "NameN": "جزيرة نورفولك" + }, + { + "ID": "MNP", + "Name": "Northern Mariana Islander", + "NameN": "جزر ماريانا الشمالية" + }, + { + "ID": "NOR", + "Name": "Norwegean", + "NameN": "نرويجي" + }, + { + "ID": "OMN", + "Name": "Omani", + "NameN": "عماني" + }, + { + "ID": "OTH", + "Name": "OTHERS", + "NameN": "آخرون" + }, + { + "ID": "PAK", + "Name": "Pakistani", + "NameN": "باكستاني" + }, + { + "ID": "PLW", + "Name": "Palauan", + "NameN": "بالاوي" + }, + { + "ID": "PSE", + "Name": "Palestinian", + "NameN": "فلسطيني" + }, + { + "ID": "PAN", + "Name": "Panamanian", + "NameN": "بنمي" + }, + { + "ID": "PNG", + "Name": "Papua New Guinean", + "NameN": "بابوا غيني جديد" + }, + { + "ID": "PRY", + "Name": "Paraguayan", + "NameN": "باراغوايي" + }, + { + "ID": "PER", + "Name": "Peruvian", + "NameN": "بيروفي" + }, + { + "ID": "PCN", + "Name": "Pitcairn Islander", + "NameN": "جزر بيتكايرن" + }, + { + "ID": "POL", + "Name": "Polish", + "NameN": "بولندي" + }, + { + "ID": "PRT", + "Name": "Portuguese", + "NameN": "برتغالي" + }, + { + "ID": "PRI", + "Name": "Puerto Rican", + "NameN": "بورتوريكي" + }, + { + "ID": "QAT", + "Name": "Qatari", + "NameN": "قطري" + }, + { + "ID": "RKS", + "Name": "Republic of Kosovo", + "NameN": "جمهورية كوسوفو" + }, + { + "ID": "REU", + "Name": "Reunionese", + "NameN": "ريونيوني" + }, + { + "ID": "ROU", + "Name": "Romanian", + "NameN": "روماني" + }, + { + "ID": "RUS", + "Name": "Russian", + "NameN": "روسي" + }, + { + "ID": "RWA", + "Name": "Rwandan", + "NameN": "رواندي" + }, + { + "ID": "ESH", + "Name": "Sahrawi", + "NameN": "صحراوي" + }, + { + "ID": "SLV", + "Name": "Salvadoran", + "NameN": "سلفادوري" + }, + { + "ID": "SMR", + "Name": "Sammarinese", + "NameN": "سان مارينو" + }, + { + "ID": "WSM", + "Name": "Samoan", + "NameN": "ساموي" + }, + { + "ID": "STP", + "Name": "Sao Tomean", + "NameN": "ساو تومي" + }, + { + "ID": "SAU", + "Name": "Saudi", + "NameN": "سعودي" + }, + { + "ID": "SCT", + "Name": "Scottish", + "NameN": "اسكتلندي" + }, + { + "ID": "SEN", + "Name": "Senegalese", + "NameN": "سنغالي" + }, + { + "ID": "SRB", + "Name": "Serbian", + "NameN": "صربي" + }, + { + "ID": "SYC", + "Name": "Seychellois", + "NameN": "سيشيلي" + }, + { + "ID": "SLE", + "Name": "Sierra Leonean", + "NameN": "سيراليوني" + }, + { + "ID": "SGP", + "Name": "Singaporian", + "NameN": "سنغافوري" + }, + { + "ID": "SVK", + "Name": "Slovak", + "NameN": "سلوفاكي" + }, + { + "ID": "SVN", + "Name": "Slovene", + "NameN": "سلوفيني" + }, + { + "ID": "SLB", + "Name": "Solomon Islander", + "NameN": "جزر سليمان" + }, + { + "ID": "SOM", + "Name": "Somalian", + "NameN": "صومالي" + }, + { + "ID": "ZAF", + "Name": "South African", + "NameN": "جنوب أفريقي" + }, + { + "ID": "ESP", + "Name": "Spanish", + "NameN": "إسباني" + }, + { + "ID": "LKA", + "Name": "Sri Lankan", + "NameN": "سريلانكي" + }, + { + "ID": "SHN", + "Name": "St. Helena", + "NameN": "سانت هيلينا" + }, + { + "ID": "KNA", + "Name": "St. Kitts and Nevis", + "NameN": "سانت كيتس ونيفيس" + }, + { + "ID": "LCA", + "Name": "St. Lucia", + "NameN": "سانت لوسيا" + }, + { + "ID": "SPM", + "Name": "St. Pierre and Miquelon", + "NameN": "سانت بيير وميكيلون" + }, + { + "ID": "VCT", + "Name": "St. Vincent and the Grenadines", + "NameN": "سانت فنسنت والغرينادين" + }, + { + "ID": "SDN", + "Name": "Sudanese", + "NameN": "سوداني" + }, + { + "ID": "SUR", + "Name": "Surinamer", + "NameN": "سورينامي" + }, + { + "ID": "SJM", + "Name": "Svalbard and Jan Mayen islander", + "NameN": "سفالبارد وجان ماين" + }, + { + "ID": "SWZ", + "Name": "Swazi", + "NameN": "سوازي" + }, + { + "ID": "SWE", + "Name": "Swedish", + "NameN": "سويدي" + }, + { + "ID": "CHE", + "Name": "Swiss", + "NameN": "سويسري" + }, + { + "ID": "SYR", + "Name": "Syrian", + "NameN": "سوري" + }, + { + "ID": "TWN", + "Name": "Taiwan, Province of China", + "NameN": "تايوان، مقاطعة الصين" + }, + { + "ID": "TJK", + "Name": "Tajik", + "NameN": "طاجيكي" + }, + { + "ID": "TZA", + "Name": "Tanzania, United Republic o", + "NameN": "تنزاني، الجمهورية المتحدة" + }, + { + "ID": "THA", + "Name": "Thai", + "NameN": "تايلندي" + }, + { + "ID": "TLS", + "Name": "Timorese", + "NameN": "تيموري" + }, + { + "ID": "TGO", + "Name": "Togolese", + "NameN": "توغولي" + }, + { + "ID": "TKL", + "Name": "Tokelauan", + "NameN": "توكيلاوي" + }, + { + "ID": "TON", + "Name": "Tongan", + "NameN": "تونغي" + }, + { + "ID": "TTO", + "Name": "Trinidadian", + "NameN": "ترينيدادي" + }, + { + "ID": "TUN", + "Name": "Tunisian", + "NameN": "تونسي" + }, + { + "ID": "TCA", + "Name": "Turk", + "NameN": "تركي" + }, + { + "ID": "TUR", + "Name": "Turkish", + "NameN": "تركي" + }, + { + "ID": "TKM", + "Name": "Turkmen", + "NameN": "تركماني" + }, + { + "ID": "TUV", + "Name": "Tuvaluan", + "NameN": "توفالوي" + }, + { + "ID": "UGA", + "Name": "Ugandan", + "NameN": "أوغندي" + }, + { + "ID": "UKR", + "Name": "Ukrainian", + "NameN": "أوكراني" + }, + { + "ID": "URY", + "Name": "Uruguayan", + "NameN": "أوروغوايي" + }, + { + "ID": "UZB", + "Name": "Uzbek", + "NameN": "أوزبكي" + }, + { + "ID": "VEN", + "Name": "Venenzuelan", + "NameN": "فنزويلي" + }, + { + "ID": "VNM", + "Name": "Vietnamese", + "NameN": "فيتنامي" + }, + { + "ID": "VIR", + "Name": "Virgin Islander", + "NameN": "جزر عذراء" + }, + { + "ID": "WLF", + "Name": "Wallis and Futuna Islander", + "NameN": "واليس وفوتونا" + }, + { + "ID": "YEM", + "Name": "Yemeni", + "NameN": "يمني" + }, + { + "ID": "ZMB", + "Name": "Zambian", + "NameN": "زامبي" + }, + { + "ID": "ZWE", + "Name": "Zimbabwean", + "NameN": "زيمبابوي" + } +] \ No newline at end of file diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 64477a3..df18bfd 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -186,7 +186,6 @@ "middleName": "اسم الأب", "lastName": "اسم العائلة", "female": "أنثى", - "male": "ذكر", "preferredLanguage": "اللغة المفضلة *", "locationsRegister": "أين ترغب في إنشاء هذا الملف؟", "ksa": "السعودية", @@ -790,5 +789,9 @@ "notice": "إشعار", "oR": "أو", "sendOTPWHATSAPP": "أرسل لي OTP عبر واتساب", - "sendOTPSMS": "أرسل لي OTP عبر الرسائل القصيرة" + "sendOTPSMS": "أرسل لي OTP عبر الرسائل القصيرة", + "fullName": "الاسم الكامل", + "married": "متزوج", + "uae": "الإمارات العربية المتحدة", + "malE": "ذكر" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index cd15a5a..c9493db 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -186,7 +186,6 @@ "middleName": "Middle Name", "lastName": "Last Name", "female": "Female", - "male": "Male", "preferredLanguage": "Preferred Language *", "locationsRegister": "Where do you want to create this file?", "ksa": "KSA", @@ -786,5 +785,9 @@ "notice": "Notice", "oR": "OR", "sendOTPWHATSAPP": "Send me OTP on Whatsapp", - "sendOTPSMS": "Send me OTP on SMS" + "sendOTPSMS": "Send me OTP on SMS", + "fullName": "Full Name", + "married": "Married", + "uae" : "United Arab Emirates", + "malE": "Male" } \ No newline at end of file diff --git a/lib/core/common_models/nationality_country_model.dart b/lib/core/common_models/nationality_country_model.dart new file mode 100644 index 0000000..fb3961b --- /dev/null +++ b/lib/core/common_models/nationality_country_model.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; + +class NationalityCountries { + String? id; + String? name; + String? nameN; + + NationalityCountries({ + this.id, + this.name, + this.nameN, + }); + + factory NationalityCountries.fromRawJson(String str) => NationalityCountries.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory NationalityCountries.fromJson(Map json) => NationalityCountries( + id: json["ID"], + name: json["Name"], + nameN: json["NameN"], + ); + + Map toJson() => { + "ID": id, + "Name": name, + "NameN": nameN, + }; +} diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index cbfac11..c9d6e39 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -435,3 +435,106 @@ extension CountryExtension on CountryEnum { } } + +extension GenderTypeExtension on GenderTypeEnum { + String get value => this == GenderTypeEnum.male ? "M" : "F"; + + String get type => this == GenderTypeEnum.male ? "Male" : "Female"; + + String get typeAr => this == GenderTypeEnum.male ? "ذكر" : "أنثى"; + + static GenderTypeEnum? fromValue(String? value) { + switch (value) { + case "M": + return GenderTypeEnum.male; + case "F": + return GenderTypeEnum.female; + default: + return null; + } + } + + static GenderTypeEnum? fromType(String? type) { + switch (type) { + case "Male": + return GenderTypeEnum.male; + case "Female": + return GenderTypeEnum.female; + default: + return null; + } + } +} + +extension MaritalStatusTypeExtension on MaritalStatusTypeEnum { + String get value { + switch (this) { + case MaritalStatusTypeEnum.single: + return "U"; + case MaritalStatusTypeEnum.married: + return "M"; + case MaritalStatusTypeEnum.divorced: + return "D"; + case MaritalStatusTypeEnum.widowed: + return "W"; + } + } + + String get type { + switch (this) { + case MaritalStatusTypeEnum.single: + return "Single"; + case MaritalStatusTypeEnum.married: + return "Married"; + case MaritalStatusTypeEnum.divorced: + return "Divorced"; + case MaritalStatusTypeEnum.widowed: + return "Widowed"; + } + } + + String get typeAr { + switch (this) { + case MaritalStatusTypeEnum.single: + return "أعزب"; + case MaritalStatusTypeEnum.married: + return "متزوج"; + case MaritalStatusTypeEnum.divorced: + return "مطلق"; + case MaritalStatusTypeEnum.widowed: + return "أرمل"; + } + } + + static MaritalStatusTypeEnum? fromValue(String? value) { + switch (value) { + case "U": + return MaritalStatusTypeEnum.single; + case "M": + return MaritalStatusTypeEnum.married; + case "D": + return MaritalStatusTypeEnum.divorced; + case "W": + return MaritalStatusTypeEnum.widowed; + default: + return null; + } + } + + static MaritalStatusTypeEnum? fromType(String? type) { + switch (type) { + case "Single": + return MaritalStatusTypeEnum.single; + case "Married": + return MaritalStatusTypeEnum.married; + case "Divorced": + return MaritalStatusTypeEnum.divorced; + case "Widowed": + return MaritalStatusTypeEnum.widowed; + default: + return null; + } + } +} + + diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c5f2592..881e730 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -789,5 +789,9 @@ abstract class LocaleKeys { static const oR = 'oR'; static const sendOTPWHATSAPP = 'sendOTPWHATSAPP'; static const sendOTPSMS = 'sendOTPSMS'; + static const fullName = 'fullName'; + static const married = 'married'; + static const uae = 'uae'; + static const malE = 'malE'; } diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index b445631..20c83f5 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -229,29 +229,30 @@ class _RegisterNew extends State { MaterialPageRoute(builder: (BuildContext context) => OTPVerificationPage(phoneNumber: '12234567',))); - // if (mobileNo.isEmpty) { - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: TranslationBase.of(context).pleaseEnterMobile, - // showCancel: false, - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ); - // } else if (!Utils.validateMobileNumber(mobileNo)) { - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: TranslationBase.of(context).pleaseEnterValidMobile, - // showCancel: false, - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ); - // } else { - // registerUser(1); - // } + // if (mobileNo.isEmpty) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else if (!Utils.validateMobileNumber(mobileNo)) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterValidMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else { + // registerUser(1); + // } + Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => OTPVerificationPage(phoneNumber: '12234567'))); }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedBorderColor, diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart new file mode 100644 index 0000000..dbffe91 --- /dev/null +++ b/lib/presentation/authentication/register_step2.dart @@ -0,0 +1,311 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/context_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; + +class RegisterNewStep2 extends StatefulWidget { + var nHICData; + var payload; + + RegisterNewStep2(this.nHICData, this.payload, {Key? key}) : super(key: key); + + @override + _RegisterNew createState() => _RegisterNew(); +} + +class _RegisterNew extends State { + bool isFromDubai = true; + List countriesList = []; + AppState? appState; + GenderTypeEnum? selectedGenderType; + MaritalStatusTypeEnum? selectedMaritalStatusType; + CountryEnum? selectedCountry; + + @override + void initState() { + super.initState(); + // isFromDubai = widget.payload.zipCode!.contains("971") || widget.payload.zipCode!.contains("+971"); + loadCountriesList(); + } + + loadCountriesList() async { + appState = getIt.get(); + final String response = await DefaultAssetBundle.of(context).loadString('assets/json/countriesList.json'); + final List data = json.decode(response); + countriesList = data.map((e) => NationalityCountries.fromJson(e)).toList(); + } + + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar(onBackPressed: () {}, onLanguageChanged: (lang) {}, hideLogoAndLang: true,), + body: SingleChildScrollView( + reverse: false, + padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Directionality( + textDirection: Directionality.of(context), + child: Container( + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)), + padding: EdgeInsets.only(left: 16.h, right: 16.h), + child: Column( + children: [ + TextInputWidget( + labelText: isFromDubai ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(), + hintText: isFromDubai ? "name" ?? "" : (widget.nHICData!.firstNameEn!.toUpperCase() + " " + widget.nHICData!.lastNameEn!.toUpperCase()), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + keyboardType: TextInputType.text, + isAllowLeadingIcon: true, + isReadOnly: isFromDubai ? false : true, + leadingIcon: AppAssets.user_circle), + Divider(height: 1), + TextInputWidget( + labelText: LocaleKeys.nationalIdNumber.tr(), + hintText: isFromDubai ? "widget.payload.nationalID!" : (widget.nHICData!.idNumber ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.student_card), + Divider(height: 1), + isFromDubai + ? DropdownWidget( + labelText: LocaleKeys.gender.tr(), + hintText: LocaleKeys.malE.tr(), + isEnable: true, + dropdownItems: GenderTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: appState!.isArabic() ? selectedGenderType!.typeAr : selectedGenderType?.type, + // selectionType: SelectionType.dropdown, + onChange: (val) { + if (val != null) {} + }, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + // leadingIcon: 'assets/images/svg/user-full.svg', + ).withVerticalPadding(8) + : TextInputWidget( + labelText: LocaleKeys.gender.tr(), + hintText: (widget.nHICData!.gender ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: isFromDubai ? false : true, + leadingIcon: AppAssets.user_full, + onChange: (value) {}), + Divider(height: 1), + isFromDubai + ? DropdownWidget( + labelText: LocaleKeys.maritalStatus.tr(), + hintText: LocaleKeys.married.tr(), + isEnable: true, + dropdownItems: MaritalStatusTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: appState!.isArabic() ? selectedMaritalStatusType!.typeAr : selectedMaritalStatusType?.type, + onChange: (val) {}, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + // leadingIcon: 'assets/images/svg/smart-phone.svg', + ).withVerticalPadding(8) + : TextInputWidget( + labelText: LocaleKeys.maritalStatus.tr(), + hintText: appState!.isArabic() + ? (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.typeAr) + : (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.type), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.smart_phone, + onChange: (value) {}), + Divider(height: 1), + isFromDubai + ? DropdownWidget( + labelText: LocaleKeys.country.tr(), + hintText: LocaleKeys.uae.tr(), + isEnable: true, + dropdownItems: countriesList.map((e) => appState!.isArabic() ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: appState!.isArabic() ? selectedCountry!.nameArabic ?? "" : selectedCountry?.name ?? "", + onChange: (val) {}, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + // leadingIcon: 'assets/images/svg/globe.svg', + ).withVerticalPadding(8) + : TextInputWidget( + labelText: LocaleKeys.nationality.tr(), + hintText: appState!.isArabic() + ? (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") + : (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.globe, + onChange: (value) {}), + Divider(height: 1), + TextInputWidget( + labelText: LocaleKeys.mobileNumber.tr(), + hintText: ("widget.payload.mobileNo" ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.call, + onChange: (value) {}), + Divider(height: 1), + TextInputWidget( + labelText: LocaleKeys.dob.tr(), + hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""), + controller: null, + isEnable: true, + prefix: null, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + // selectionType: SelectionType.calendar, + // selectedValue: widget.payload.dob != null ? Utils.formatDateToDisplay(widget.payload.dob.toString()) : null, + // selectionCustomIcon: AppAssets.calendar, + // leadingIcon: AppAssets.birthday_cake, + onChange: (value) {}), + ], + ), + ), + ), + SizedBox(height: 50.h), + Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.cancel, + icon: AppAssets.cancel, + onPressed: () { + Navigator.of(context).pop(); + }, + // fontFamily: context.fontFamily, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + iconColor: AppColors.primaryRedColor, + ), + ), + SizedBox( + width: 16, + ), + Expanded( + child: CustomButton( + backgroundColor: AppColors.lightGreenColor, + borderColor: AppColors.lightGreenColor, + textColor: AppColors.textGreenColor, + text: LocaleKeys.confirm.tr(), + icon: AppAssets.confirm, + iconColor: AppColors.textGreenColor, + onPressed: () { + // if (isFromDubai) { + // if (name == null) { + // AppToast.showErrorToast(message: LocaleKeys.enterFullName); + // return; + // } + // if (!name!.contains(" ")) if (selectedGenderType == null) { + // AppToast.showErrorToast(message: TranslationBase.of(context).enterFullName); + // return; + // } + // if (selectedMaritalStatusType == null) { + // AppToast.showErrorToast(message: TranslationBase.of(context).chooseMaritalStatus); + // return; + // } + // if (selectedCountry == null) { + // AppToast.showErrorToast(message: TranslationBase.of(context).chooseCountry); + // return; + // } + // } + + showModel(context: context); + }, + ), + ) + ], + ), + ], + ), + ), + ); + } + + void showModel({required BuildContext context}) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + backgroundColor: Colors.transparent, + builder: (bottomSheetContext) => Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + textController: TextEditingController(), + isForEmail: true, + buttons: [ + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: CustomButton( + text: LocaleKeys.submit, + onPressed: () { + // if (emailAddress.text.isEmpty) { + // Utils.showErrorToast(TranslationBase.of(context).enterEmailAddress); + // return; + // } else { + // Navigator.of(context).pop(); + // registerNow(); + // } + }, + backgroundColor: AppColors.bgGreenColor, + borderColor: AppColors.bgGreenColor, + textColor: AppColors.whiteColor), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index c305110..60161f0 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -49,7 +49,8 @@ class _LandingPageState extends State { CustomButton( text: LocaleKeys.loginOrRegister.tr(context: context), onPressed: () async { - await authenticationViewModel.selectDeviceImei(); + // await authenticationViewModel.selectDeviceImei(); + Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => LoginScreen())); }, backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index a395ba3..4b2d69b 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -35,6 +35,10 @@ class AppColors { static const blackColor = textColor; static const inputLabelTextColor = Color(0xff898A8D); + + static const lightGreenColor = Color(0xFF0ccedde); + static const textGreenColor = Color(0xFF18C273); + //Chips static const Color successColor = Color(0xff18C273); static const Color errorColor = Color(0xFFED1C2B); diff --git a/lib/widgets/otp/otp.dart b/lib/widgets/otp/otp.dart index 5cfd3e9..81c375b 100644 --- a/lib/widgets/otp/otp.dart +++ b/lib/widgets/otp/otp.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; @@ -208,6 +209,8 @@ class _OTPVerificationPageState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Verifying OTP: $otp')), ); + + Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => RegisterNewStep2(null, {"nationalID": "12345678654321"}))); } /// Auto fill OTP into text fields diff --git a/pubspec.yaml b/pubspec.yaml index 4c2b81f..22a33d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -81,6 +81,7 @@ flutter: - assets/ - assets/fonts/ - assets/langs/ + - assets/json/ - assets/images/ - assets/images/svg/ - assets/images/png/ From 85fac13314e145f0713a564c0fb8df18bd3cee03 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 4 Sep 2025 08:35:41 +0300 Subject: [PATCH 08/16] changes --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/core/enums.dart | 51 +++- lib/extensions/string_extensions.dart | 30 +- .../authentication_view_model.dart | 3 +- lib/generated/locale_keys.g.dart | 4 + .../authentication/saved_login_screen.dart | 289 ++++++++++++++++++ lib/presentation/home/landing_page.dart | 5 +- lib/theme/colors.dart | 1 + .../dropdown/country_dropdown_widget.dart | 89 +++--- lib/widgets/input_widget.dart | 49 +-- pubspec.lock | 22 +- 12 files changed, 452 insertions(+), 103 deletions(-) create mode 100644 lib/presentation/authentication/saved_login_screen.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index df18bfd..0ad2cd6 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -793,5 +793,9 @@ "fullName": "الاسم الكامل", "married": "متزوج", "uae": "الإمارات العربية المتحدة", - "malE": "ذكر" + "malE": "ذكر", + "loginBy": "تسجيل الدخول بواسطة", + "loginByOTP": "تسجيل الدخول بواسطة OTP", + "guest": "زائر", + "switchAccount": "تبديل الحساب" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index c9493db..bbe9b0c 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -789,5 +789,9 @@ "fullName": "Full Name", "married": "Married", "uae" : "United Arab Emirates", - "malE": "Male" + "malE": "Male", + "loginBy": "Login By", + "loginByOTP": "Login By OTP", + "guest": "Guest", + "switchAccount": "Switch Account" } \ No newline at end of file diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 2fd5867..34aa911 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -22,12 +22,6 @@ enum ViewStateEnum { errorLocal, } -enum LoginTypeEnum { - fromLogin, - silentLogin, - silentWithOTP, -} - enum OTPTypeEnum { sms, whatsapp } enum CountryEnum { saudiArabia, unitedArabEmirates } @@ -40,6 +34,51 @@ enum MaritalStatusTypeEnum { single, married, divorced, widowed } enum ChipTypeEnum { success, error, alert, info, warning } +enum LoginTypeEnum { sms, whatsapp, face, fingerprint } + +extension LoginTypeExtension on LoginTypeEnum { + int get toInt { + switch (this) { + case LoginTypeEnum.sms: + return 1; + case LoginTypeEnum.whatsapp: + return 4; + case LoginTypeEnum.face: + return 3; + case LoginTypeEnum.fingerprint: + return 2; + } + } + + String get displayName { + switch (this) { + case LoginTypeEnum.sms: + return 'SMS'; + case LoginTypeEnum.whatsapp: + return 'WhatsApp'; + case LoginTypeEnum.face: + return 'Face Recognition'; + case LoginTypeEnum.fingerprint: + return 'Fingerprint'; + } + } + + static LoginTypeEnum? fromValue(int value) { + switch (value) { + case 1: + return LoginTypeEnum.sms; + case 2: + return LoginTypeEnum.fingerprint; + case 3: + return LoginTypeEnum.face; + case 4: + return LoginTypeEnum.whatsapp; + default: + return null; + } + } +} + extension OTPTypeEnumExtension on OTPTypeEnum { /// Convert enum to int int toInt() { diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index c9d6e39..4d813f0 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -206,19 +206,25 @@ extension EmailValidator on String { Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( this, maxLines: maxlines, - style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.fSize, letterSpacing: -0.4, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)), + style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.fSize, letterSpacing: -0.4, fontWeight: weight ?? (isBold ? FontWeight.w600 : FontWeight.normal)), ); Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toText24({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); + + Widget toText26({Color? color, bool isBold = false, bool isCenter = false, double height = 23/26}) => Text( + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: height, color: color ?? AppColors.blackColor, fontSize: 26.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toText28({Color? color, bool isBold = false, bool isCenter = false, TextScaler? textScaler}) => Text( @@ -231,20 +237,18 @@ extension EmailValidator on String { Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toText36({Color? color, bool isBold = false, bool isCenter = false}) => Text( - this, - textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: 47 / 36, color: color ?? AppColors.blackColor, fontSize: 36.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), - ); - - + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: 47 / 36, color: color ?? AppColors.blackColor, fontSize: 36.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), + ); Widget toText44({Color? color, bool isBold = false}) => Text( this, - style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.w600 : FontWeight.normal), ); Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) { @@ -383,7 +387,6 @@ class FontUtils { } } - extension CountryExtension on CountryEnum { String get displayName { switch (this) { @@ -435,7 +438,6 @@ extension CountryExtension on CountryEnum { } } - extension GenderTypeExtension on GenderTypeEnum { String get value => this == GenderTypeEnum.male ? "M" : "F"; @@ -536,5 +538,3 @@ extension MaritalStatusTypeExtension on MaritalStatusTypeEnum { } } } - - diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index f192db2..baa6f7e 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -26,9 +26,8 @@ class AuthenticationViewModel extends ChangeNotifier { void login() { if (ValidationUtils.isValidatePhoneAndId(nationalId: nationalIdController.text, phoneNumber: phoneNumberController.text)) { - } else { - } + } else {} } Future selectDeviceImei({Function(dynamic)? onSuccess, Function(String)? onError}) async { diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 881e730..0877462 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -793,5 +793,9 @@ abstract class LocaleKeys { static const married = 'married'; static const uae = 'uae'; static const malE = 'malE'; + static const loginBy = 'loginBy'; + static const loginByOTP = 'loginByOTP'; + static const guest = 'guest'; + static const switchAccount = 'switchAccount'; } diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart new file mode 100644 index 0000000..da04c40 --- /dev/null +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -0,0 +1,289 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; +import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class SavedLogin extends StatefulWidget { + // final SelectDeviceIMEIRES savedLoginData; + + // const SavedLogin(this.savedLoginData, {Key? key}) : super(key: key); + const SavedLogin(); + + @override + _SavedLogin createState() => _SavedLogin(); +} + +class _SavedLogin extends State { + LoginTypeEnum loginType = LoginTypeEnum.sms; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + onBackPressed: () {}, + onLanguageChanged: (lang) {}, + ), + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 24.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Spacer(flex: 2), + // Welcome back text + LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor), + SizedBox(height: 16.h), + ("widget.savedLoginData.name!.toLowerCase().capitalizeFirstofEach").toText26(isBold: true, height: 26 / 36, color: AppColors.textColor), + SizedBox(height: 24.h), + Container( + padding: EdgeInsets.all(16.h), + decoration: BoxDecoration( + color: AppColors.whiteColor, + border: Border.all(color: AppColors.whiteColor), + borderRadius: BorderRadius.circular(24.h), + boxShadow: [ + BoxShadow(color: Color(0x0D000000), blurRadius: 16, offset: Offset(0, 0), spreadRadius: 5), + ], + ), + child: Column( + children: [ + // Last login info + ('LocaleKeys.lastloginBy.tr()' + ' {getType(widget.savedLoginData.logInType!, context)}').toText14(isBold: true, color: AppColors.greyTextColor), + ('widget.savedLoginData.createdOn != null ? DateUtil.getFormattedDate(DateUtil.convertStringToDate(widget.savedLoginData.createdOn!), "d MMMM, y at HH:mm") : --') + .toText16(isBold: true, color: AppColors.textColor), + + Container(margin: EdgeInsets.all(16.h), child: Utils.buildSvgWithAssets(icon: getTypeIcons(loginType.toInt), iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)), + // Face ID login button + Container( + height: 45, + child: CustomButton( + text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}", + onPressed: () { + if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { + // loginWithFingerPrintFace(loginType.toInt, widget.savedLoginData.iMEI!); + } else { + int? val = loginType.toInt; + //checkUserAuthentication(val); + } + }, + backgroundColor: Color(0xffED1C2B), + borderColor: Color(0xffFEE9EA), + textColor: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(0, 10, 0, 10), + icon: AppAssets.apple_finder, + ), + ), + ], + ), + ), + const SizedBox(height: 24), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + LocaleKeys.oR.tr(), + style: context.dynamicTextStyle(fontSize: 16, fontWeight: FontWeight.w500), + ), + ), + const SizedBox(height: 24), + // OTP login button + loginType != null && loginType.toInt != 1 + ? Column( + children: [ + loginType.toInt != 1 + ? CustomButton( + text: LocaleKeys.loginByOTP.tr(), + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + enableDrag: false, + // Prevent dragging to avoid focus conflicts + builder: (bottomSheetContext) => StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + countryCode: "966", + initialPhoneNumber: "", + textController: TextEditingController(), + isFromSavedLogin: true, + isEnableCountryDropdown: true, + onCountryChange: (value) {}, + onChange: (String? value) {}, + buttons: [ + Padding( + padding: EdgeInsets.only(bottom: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () { + Navigator.of(context).pop(); + loginType = LoginTypeEnum.sms; + int? val = loginType.toInt; + // checkUserAuthentication(val); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding(padding: EdgeInsets.symmetric(horizontal: 8.h), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)), + ], + ), + Padding( + padding: const EdgeInsets.only(bottom: 10, top: 10), + child: CustomButton( + text: LocaleKeys.sendOTPWHATSAPP.tr(), + onPressed: () { + Navigator.of(context).pop(); + loginType = LoginTypeEnum.whatsapp; + int? val = loginType.toInt; + // checkUserAuthentication(val); + }, + backgroundColor: AppColors.transparent, + borderColor: AppColors.textColor, + textColor: AppColors.textColor, + icon: AppAssets.whatsapp, + ), + ), + ], + ), + ), + ); + }), + ); + }, + backgroundColor: AppColors.whiteColor, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.textColor, + borderWidth: 2, + padding: EdgeInsets.fromLTRB(0, 14, 0, 14), + icon: AppAssets.password_validation, + ) + : Container(), + SizedBox( + height: 20, + ), + ], + ) + : CustomButton( + text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}", + onPressed: () { + if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { + // loginWithFingerPrintFace(loginType.toInt, "iMEI"); + } else { + loginType = LoginTypeEnum.whatsapp; + int? val = loginType.toInt; + // checkUserAuthentication(val); + } + }, + backgroundColor: AppColors.whiteColor, + borderColor: Color(0xFF2E3039), + textColor: Color(0xFF2E3039), + borderWidth: 2, + padding: EdgeInsets.fromLTRB(0, 14, 0, 14), + icon: AppAssets.whatsapp, + ), + const Spacer(flex: 2), + // OR divider + + const SizedBox(height: 24), + // Guest and Switch account + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + height: 56, + child: CustomButton( + text: LocaleKeys.guest.tr(), + onPressed: () { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(0, 10, 0, 10), + // icon: "assets/images/svg/apple-finder.svg", + ), + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.05, + ), + Expanded( + child: Container( + height: 56, + child: CustomButton( + text: LocaleKeys.switchAccount.tr(), + onPressed: () { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 15, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(0, 10, 0, 10), + // icon: "assets/images/svg/apple-finder.svg", + ), + ), + ), + ], + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } + + String getTypeIcons(int type) { + final types = { + 1: AppAssets.sms, + 2: AppAssets.fingerprint, + 3: AppAssets.fingerprint, + 4: AppAssets.whatsapp, + }; + + if (types.containsKey(type)) { + return types[type]!; + } else { + throw Exception('Invalid login type: $type'); + } + } +} diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 60161f0..5e755d1 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -179,10 +179,7 @@ class _LandingPageState extends State { ) : Container( height: 127.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24), child: Padding( padding: EdgeInsets.all(16.h), child: Column( diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 4b2d69b..c1b9541 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -34,6 +34,7 @@ class AppColors { static const Color blackBgColor = Color(0xFF2E3039); static const blackColor = textColor; static const inputLabelTextColor = Color(0xff898A8D); + static const greyTextColor = Color(0xFF8F9AA3); static const lightGreenColor = Color(0xFF0ccedde); diff --git a/lib/widgets/dropdown/country_dropdown_widget.dart b/lib/widgets/dropdown/country_dropdown_widget.dart index 5e4bddf..603cfed 100644 --- a/lib/widgets/dropdown/country_dropdown_widget.dart +++ b/lib/widgets/dropdown/country_dropdown_widget.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; @@ -5,17 +6,24 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; class CustomCountryDropdown extends StatefulWidget { final List countryList; final Function(CountryEnum)? onCountryChange; final bool isRtl; + final bool isFromBottomSheet; + final bool isEnableTextField; + Widget? textField; - const CustomCountryDropdown({ + CustomCountryDropdown({ Key? key, required this.countryList, this.onCountryChange, required this.isRtl, + this.isFromBottomSheet = false, + this.isEnableTextField = false, + this.textField, }) : super(key: key); @override @@ -52,15 +60,36 @@ class _CustomCountryDropdownState extends State { SizedBox(width: 8.h), Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), SizedBox(width: 4.h), - Text( - selectedCountry != null ? selectedCountry!.displayName : "Select Country", - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - letterSpacing: -0.2, + if (widget.isFromBottomSheet) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + LocaleKeys.phoneNumber.tr(), + style: TextStyle(fontSize: 12.fSize, height: 21 / 12, fontWeight: FontWeight.w500, letterSpacing: -1), + ), + ], + ), + Row( + children: [ + Text( + selectedCountry!.countryCode, + style: TextStyle(fontSize: 12.fSize, height: 21 / 18, fontWeight: FontWeight.w600, letterSpacing: -0.2), + ), + SizedBox(width: 4.h), + if (widget.isEnableTextField) widget.textField!, + ], + ) + ], + ), + if (!widget.isFromBottomSheet) + Text( + selectedCountry != null ? selectedCountry!.displayName : "Select Country", + style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2), ), - ), ], ), ), @@ -84,27 +113,11 @@ class _CustomCountryDropdownState extends State { ), Positioned( top: offset.dy + renderBox.size.height, - left: widget.isRtl ? offset.dx + 15.h : offset.dx - 15.h, + left: widget.isRtl ? offset.dx + 6.h : offset.dx - 6.h, width: renderBox.size.width, child: Material( child: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: Colors.white, - borderRadius: 12, - ), - // decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(12), - // boxShadow: [ - // BoxShadow( - // color: Color(0xFFF8F8FA), - // blurRadius: 8.h, - // offset: Offset( - // 0, - // 2, - // ), - // ), - // ], - // ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12), child: Column( children: widget.countryList .map( @@ -117,25 +130,13 @@ class _CustomCountryDropdownState extends State { _closeDropdown(); }, child: Container( - padding: EdgeInsets.symmetric(vertical: 12.h, horizontal: 16.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - borderRadius: 16.h, - ), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 16.h), child: Row( children: [ - Utils.buildSvgWithAssets( - icon: country.iconPath, - width: 38.h, - height: 38.h, - ), - SizedBox(width: 12.h), - Text(country.displayName, - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - letterSpacing: -0.2, - )), + Utils.buildSvgWithAssets(icon: country.iconPath, width: 38.h, height: 38.h), + if (!widget.isFromBottomSheet) SizedBox(width: 12.h), + if (!widget.isFromBottomSheet) Text(country.displayName, style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)), ], ), ), diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index ca72fe8..6cefbbf 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; // TODO: Import AppColors if bgRedColor is defined there // import 'package:hmg_patient_app_new/core/ui_utils/app_colors.dart'; @@ -72,17 +74,26 @@ class TextInputWidget extends StatelessWidget { child: Row( textDirection: Directionality.of(context), children: [ - if (isAllowLeadingIcon && leadingIcon != null) _buildLeadingIcon(context), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildLabelText(), - _buildTextField(context), - ], + if (isAllowLeadingIcon && leadingIcon != null) + isCountryDropDown + ? CustomCountryDropdown( + countryList: CountryEnum.values, + onCountryChange: (CountryEnum? value) {}, + isRtl: Directionality.of(context) == TextDirection.rtl, + isFromBottomSheet: isCountryDropDown, + isEnableTextField:true, + textField: _buildTextField(context), + ) + : Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildLabelText(), + _buildTextField(context), + ], + ), ), - ), ], ), ), @@ -161,15 +172,15 @@ class TextInputWidget extends StatelessWidget { prefixIcon: prefix == null ? null : Text( - "+" + prefix!, - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - color: Color(0xff2E303A), - letterSpacing: -0.2, - ), - ), + "+" + prefix!, + style: TextStyle( + fontSize: 14.fSize, + height: 21 / 14, + fontWeight: FontWeight.w500, + color: Color(0xff2E303A), + letterSpacing: -0.2, + ), + ), contentPadding: EdgeInsets.zero, border: InputBorder.none, focusedBorder: InputBorder.none, diff --git a/pubspec.lock b/pubspec.lock index 679a57e..5665d11 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -874,26 +874,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.1" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -1455,10 +1455,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" time: dependency: transitive description: @@ -1583,10 +1583,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -1636,5 +1636,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.7.0 <4.0.0" + dart: ">=3.8.0-0 <4.0.0" flutter: ">=3.29.0" From 0309299f44b84aa1a0360832be876fa05ad4dafc Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 4 Sep 2025 10:02:07 +0300 Subject: [PATCH 09/16] changes --- assets/langs/ar-SA.json | 2 +- assets/langs/en-US.json | 3 +- lib/generated/locale_keys.g.dart | 2 +- lib/presentation/authentication/register.dart | 1 + .../authentication/register_step2.dart | 38 +++---- .../dropdown/country_dropdown_widget.dart | 98 ++++++++++++++----- lib/widgets/dropdown/dropdown_widget.dart | 34 +++++-- lib/widgets/input_widget.dart | 58 ++++++----- 8 files changed, 155 insertions(+), 81 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 46362bb..2c7ac9b 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -782,7 +782,7 @@ "resultsPending": "النتائج معلقة", "resultsAvailable": "النتائج متاحة", "viewReport": "عرض التقرير", - "prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم." + "prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم.", "receiveOtpToast": "أين تود تلقي رمز التحقق OTP؟", "enterPhoneNumber": "أدخل رقم الهاتف", "enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 22509d8..d01d9f6 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -783,8 +783,7 @@ "resultsPending": "Results Pending", "resultsAvailable": "Results Available", "viewReport": "View Report", - "prescriptionDeliveryError": "This clinic doesn't support refill" - "loginOrRegister": "Login or Register", + "prescriptionDeliveryError": "This clinic doesn't support refill", "prepareToElevate": "Prepared to elevate your health and well-being?", "iAcceptTermsConditions": "I Accept the Terms and Conditions", "alreadyHaveAccount": "Already have an account?", diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index e6e3090..0210e4c 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -774,13 +774,13 @@ abstract class LocaleKeys { static const validPassportNumber = 'validPassportNumber'; static const continuePlan = 'continuePlan'; static const aboutApp = 'aboutApp'; + static const dontHaveAccount = 'dontHaveAccount'; static const loginOrRegister = 'loginOrRegister'; static const myFiles = 'myFiles'; static const resultsPending = 'resultsPending'; static const resultsAvailable = 'resultsAvailable'; static const viewReport = 'viewReport'; static const prescriptionDeliveryError = 'prescriptionDeliveryError'; - static const dontHaveAccount = 'dontHaveAccount'; static const receiveOtpToast = 'receiveOtpToast'; static const enterPhoneNumber = 'enterPhoneNumber'; static const enterEmailDesc = 'enterEmailDesc'; diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 20c83f5..1e8a0c7 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -218,6 +218,7 @@ class _RegisterNew extends State { countryCode: "966", initialPhoneNumber: "", textController: TextEditingController(), + isEnableCountryDropdown: true, onChange: (String? value) {}, buttons: [ Padding( diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart index dbffe91..295dd8a 100644 --- a/lib/presentation/authentication/register_step2.dart +++ b/lib/presentation/authentication/register_step2.dart @@ -53,6 +53,7 @@ class _RegisterNew extends State { Widget build(BuildContext context) { return Scaffold( + appBar: CustomAppBar(onBackPressed: () {}, onLanguageChanged: (lang) {}, hideLogoAndLang: true,), body: SingleChildScrollView( reverse: false, @@ -78,8 +79,8 @@ class _RegisterNew extends State { keyboardType: TextInputType.text, isAllowLeadingIcon: true, isReadOnly: isFromDubai ? false : true, - leadingIcon: AppAssets.user_circle), - Divider(height: 1), + leadingIcon: AppAssets.user_circle).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), TextInputWidget( labelText: LocaleKeys.nationalIdNumber.tr(), hintText: isFromDubai ? "widget.payload.nationalID!" : (widget.nHICData!.idNumber ?? ""), @@ -90,8 +91,8 @@ class _RegisterNew extends State { isBorderAllowed: false, isAllowLeadingIcon: true, isReadOnly: true, - leadingIcon: AppAssets.student_card), - Divider(height: 1), + leadingIcon: AppAssets.student_card).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), isFromDubai ? DropdownWidget( labelText: LocaleKeys.gender.tr(), @@ -108,7 +109,7 @@ class _RegisterNew extends State { isAllowRadius: false, padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), selectionCustomIcon: AppAssets.arrow_down, - // leadingIcon: 'assets/images/svg/user-full.svg', + leadingIcon: AppAssets.user_full, ).withVerticalPadding(8) : TextInputWidget( labelText: LocaleKeys.gender.tr(), @@ -121,8 +122,9 @@ class _RegisterNew extends State { isAllowLeadingIcon: true, isReadOnly: isFromDubai ? false : true, leadingIcon: AppAssets.user_full, - onChange: (value) {}), - Divider(height: 1), + + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), isFromDubai ? DropdownWidget( labelText: LocaleKeys.maritalStatus.tr(), @@ -136,7 +138,7 @@ class _RegisterNew extends State { isAllowRadius: false, padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), selectionCustomIcon: AppAssets.arrow_down, - // leadingIcon: 'assets/images/svg/smart-phone.svg', + leadingIcon: AppAssets.smart_phone, ).withVerticalPadding(8) : TextInputWidget( labelText: LocaleKeys.maritalStatus.tr(), @@ -150,8 +152,8 @@ class _RegisterNew extends State { isAllowLeadingIcon: true, isReadOnly: true, leadingIcon: AppAssets.smart_phone, - onChange: (value) {}), - Divider(height: 1), + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), isFromDubai ? DropdownWidget( labelText: LocaleKeys.country.tr(), @@ -165,7 +167,7 @@ class _RegisterNew extends State { isAllowRadius: false, padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), selectionCustomIcon: AppAssets.arrow_down, - // leadingIcon: 'assets/images/svg/globe.svg', + leadingIcon: AppAssets.globe, ).withVerticalPadding(8) : TextInputWidget( labelText: LocaleKeys.nationality.tr(), @@ -179,8 +181,8 @@ class _RegisterNew extends State { isAllowLeadingIcon: true, isReadOnly: true, leadingIcon: AppAssets.globe, - onChange: (value) {}), - Divider(height: 1), + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), TextInputWidget( labelText: LocaleKeys.mobileNumber.tr(), hintText: ("widget.payload.mobileNo" ?? ""), @@ -192,8 +194,8 @@ class _RegisterNew extends State { isAllowLeadingIcon: true, isReadOnly: true, leadingIcon: AppAssets.call, - onChange: (value) {}), - Divider(height: 1), + onChange: (value) {}).paddingSymmetrical(0.h,16.h), + Divider(height: 1, color: AppColors.greyColor,), TextInputWidget( labelText: LocaleKeys.dob.tr(), hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""), @@ -203,11 +205,11 @@ class _RegisterNew extends State { isBorderAllowed: false, isAllowLeadingIcon: true, isReadOnly: true, - // selectionType: SelectionType.calendar, + // : SelectionType.calendar, // selectedValue: widget.payload.dob != null ? Utils.formatDateToDisplay(widget.payload.dob.toString()) : null, // selectionCustomIcon: AppAssets.calendar, - // leadingIcon: AppAssets.birthday_cake, - onChange: (value) {}), + leadingIcon: AppAssets.birthday_cake, + onChange: (value) {}).paddingSymmetrical(0.h,16.h), ], ), ), diff --git a/lib/widgets/dropdown/country_dropdown_widget.dart b/lib/widgets/dropdown/country_dropdown_widget.dart index 603cfed..3d28e23 100644 --- a/lib/widgets/dropdown/country_dropdown_widget.dart +++ b/lib/widgets/dropdown/country_dropdown_widget.dart @@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; class CustomCountryDropdown extends StatefulWidget { final List countryList; final Function(CountryEnum)? onCountryChange; + final Function(String)? onPhoneNumberChanged; final bool isRtl; final bool isFromBottomSheet; final bool isEnableTextField; @@ -20,6 +21,7 @@ class CustomCountryDropdown extends StatefulWidget { Key? key, required this.countryList, this.onCountryChange, + this.onPhoneNumberChanged, required this.isRtl, this.isFromBottomSheet = false, this.isEnableTextField = false, @@ -34,34 +36,55 @@ class _CustomCountryDropdownState extends State { CountryEnum? selectedCountry; late OverlayEntry _overlayEntry; bool _isDropdownOpen = false; + FocusNode textFocusNode = new FocusNode(); @override void initState() { super.initState(); selectedCountry = CountryEnum.saudiArabia; + + if (widget.isEnableTextField && widget.isFromBottomSheet) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && textFocusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(textFocusNode); + } + }); + } + } + + @override + void dispose() { + textFocusNode.dispose(); + super.dispose(); } @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - if (_isDropdownOpen) { - _closeDropdown(); - } else { - _openDropdown(); - } - }, - child: Container( - height: 40.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h), - child: Row( - children: [ - Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h), - SizedBox(width: 8.h), - Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), - SizedBox(width: 4.h), - if (widget.isFromBottomSheet) - Column( + return Container( + height: 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h), + child: Row( + children: [ + GestureDetector( + onTap: () { + if (_isDropdownOpen) { + _closeDropdown(); + } else { + _openDropdown(); + } + }, + child: Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h)), + SizedBox(width: 8.h), + Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), + SizedBox(width: 4.h), + if (widget.isFromBottomSheet) + GestureDetector( + onTap: () { + if (widget.isEnableTextField && textFocusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(textFocusNode); + } + }, + child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -80,23 +103,36 @@ class _CustomCountryDropdownState extends State { style: TextStyle(fontSize: 12.fSize, height: 21 / 18, fontWeight: FontWeight.w600, letterSpacing: -0.2), ), SizedBox(width: 4.h), - if (widget.isEnableTextField) widget.textField!, + if (widget.isEnableTextField) + SizedBox( + height: 20, + width: 200, + child: TextField( + focusNode: textFocusNode, + decoration: InputDecoration(hintText: "", isDense: true, border: InputBorder.none), + keyboardType: TextInputType.phone, + onChanged: widget.onPhoneNumberChanged), + ), ], ) ], ), - if (!widget.isFromBottomSheet) - Text( - selectedCountry != null ? selectedCountry!.displayName : "Select Country", - style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2), - ), - ], - ), + ), + if (!widget.isFromBottomSheet) + Text( + selectedCountry != null ? selectedCountry!.displayName : "Select Country", + style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2), + ), + ], ), ); } void _openDropdown() { + if (textFocusNode.hasFocus) { + textFocusNode.unfocus(); + } + RenderBox renderBox = context.findRenderObject() as RenderBox; Offset offset = renderBox.localToGlobal(Offset.zero); @@ -162,5 +198,13 @@ class _CustomCountryDropdownState extends State { setState(() { _isDropdownOpen = false; }); + + if (widget.isEnableTextField && widget.isFromBottomSheet) { + Future.delayed(Duration(milliseconds: 100), () { + if (mounted && textFocusNode.canRequestFocus) { + FocusScope.of(context).requestFocus(textFocusNode); + } + }); + } } } diff --git a/lib/widgets/dropdown/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart index 268e44a..2a0cad3 100644 --- a/lib/widgets/dropdown/dropdown_widget.dart +++ b/lib/widgets/dropdown/dropdown_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart' show Icons, PopupMenuItem, showMenu, Colo import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; class DropdownWidget extends StatelessWidget { final String labelText; @@ -16,6 +17,7 @@ class DropdownWidget extends StatelessWidget { final EdgeInsetsGeometry? padding; final bool hasSelectionCustomIcon; final String? selectionCustomIcon; + final String? leadingIcon; const DropdownWidget({ Key? key, @@ -30,29 +32,49 @@ class DropdownWidget extends StatelessWidget { this.padding, this.hasSelectionCustomIcon = false, this.selectionCustomIcon, + this.leadingIcon, }) : super(key: key); @override Widget build(BuildContext context) { + Widget content = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [_buildLabelText(), _buildDropdown(context)], + ); + return Container( padding: padding, - alignment: Alignment.center, + alignment: Alignment.center, // This might need adjustment based on layout decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: Colors.white, borderRadius: isAllowRadius ? 15.h : null, side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null, ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( + // Wrap with a Row + crossAxisAlignment: CrossAxisAlignment.center, // Align items vertically in the center children: [ - _buildLabelText(), - _buildDropdown(context), + if (leadingIcon != null) ...[ + _buildLeadingIcon(), + SizedBox(width: 3.h), + ], + Expanded(child: content), ], ), ); } + Widget _buildLeadingIcon() { + return Container( + height: 40.h, + width: 40.h, + margin: EdgeInsets.only(right: 10.h), + padding: EdgeInsets.all(8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.greyColor), + child: Utils.buildSvgWithAssets(icon: leadingIcon!)); + } + Widget _buildLabelText() { return Text( labelText, diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index 6cefbbf..b8d36e7 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -74,26 +74,32 @@ class TextInputWidget extends StatelessWidget { child: Row( textDirection: Directionality.of(context), children: [ - if (isAllowLeadingIcon && leadingIcon != null) + if (isAllowLeadingIcon && leadingIcon != null && !isCountryDropDown) + _buildLeadingIcon(context), isCountryDropDown ? CustomCountryDropdown( - countryList: CountryEnum.values, - onCountryChange: (CountryEnum? value) {}, - isRtl: Directionality.of(context) == TextDirection.rtl, - isFromBottomSheet: isCountryDropDown, - isEnableTextField:true, - textField: _buildTextField(context), - ) + countryList: CountryEnum.values, + onCountryChange: (CountryEnum? value) { + print(value); + }, + isRtl: Directionality.of(context) == TextDirection.rtl, + isFromBottomSheet: isCountryDropDown, + isEnableTextField: true, + onPhoneNumberChanged: (value) { + print(value); + }, + textField: _buildTextField(context), + ) : Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildLabelText(), - _buildTextField(context), - ], - ), - ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildLabelText(), + _buildTextField(context), + ], + ), + ), ], ), ), @@ -172,15 +178,15 @@ class TextInputWidget extends StatelessWidget { prefixIcon: prefix == null ? null : Text( - "+" + prefix!, - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - color: Color(0xff2E303A), - letterSpacing: -0.2, - ), - ), + "+" + prefix!, + style: TextStyle( + fontSize: 14.fSize, + height: 21 / 14, + fontWeight: FontWeight.w500, + color: Color(0xff2E303A), + letterSpacing: -0.2, + ), + ), contentPadding: EdgeInsets.zero, border: InputBorder.none, focusedBorder: InputBorder.none, From e41ffd583d938bc10e0186d6c883759878f6a4c5 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 4 Sep 2025 11:25:16 +0300 Subject: [PATCH 10/16] changes --- .../authentication_view_model.dart | 17 +- lib/presentation/authentication/login.dart | 12 +- lib/presentation/authentication/register.dart | 244 +++++++++--------- .../authentication/register_step2.dart | 199 ++++++++------ .../bottomsheet/generic_bottom_sheet.dart | 11 +- lib/widgets/input_widget.dart | 49 ++-- 6 files changed, 286 insertions(+), 246 deletions(-) diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index baa6f7e..9abd673 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -23,13 +23,28 @@ class AuthenticationViewModel extends ChangeNotifier { final TextEditingController nationalIdController = TextEditingController(); final TextEditingController phoneNumberController = TextEditingController(); + CountryEnum selectedCountry = CountryEnum.saudiArabia; + bool isTermsAccepted = false; void login() { if (ValidationUtils.isValidatePhoneAndId(nationalId: nationalIdController.text, phoneNumber: phoneNumberController.text)) { - } else {} } + void onCountryChange(CountryEnum country) { + selectedCountry = country; + notifyListeners(); + } + + void onPhoneNumberChange(String? phoneNumber) { + phoneNumberController.text = phoneNumber!; + } + + void onTermAccepted() { + isTermsAccepted = !isTermsAccepted; + notifyListeners(); + } + Future selectDeviceImei({Function(dynamic)? onSuccess, Function(String)? onError}) async { String firebaseToken = "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc"; final result = await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken); diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index 2771b36..f171443 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -83,7 +83,7 @@ class _LoginScreen extends State { icon: AppAssets.login1, iconColor: Colors.white, onPressed: () { - showLoginModel(context: context, textController: authVm.phoneNumberController); + showLoginModel(context: context, authVM: authVm); // if (nationIdController.text.isNotEmpty) { // } else { @@ -140,7 +140,7 @@ class _LoginScreen extends State { ); } - void showLoginModel({required BuildContext context, TextEditingController? textController}) { + void showLoginModel({required BuildContext context, required AuthenticationViewModel authVM}) { context.showBottomSheet( isScrollControlled: true, isDismissible: false, @@ -151,12 +151,12 @@ class _LoginScreen extends State { padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), child: SingleChildScrollView( child: GenericBottomSheet( - countryCode: "966", + countryCode: authVM.selectedCountry.countryCode, initialPhoneNumber: "", - textController: textController, + textController: authVM.phoneNumberController, isEnableCountryDropdown: true, - onCountryChange: (value) {}, - onChange: (String? value) {}, + onCountryChange: authVM.onCountryChange, + onChange: authVM.onPhoneNumberChange, buttons: [ Padding( padding: EdgeInsets.only(bottom: 10.h), diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 1e8a0c7..85ff402 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; @@ -18,6 +19,7 @@ import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dar import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/otp/otp.dart'; +import 'package:provider/provider.dart'; class RegisterNew extends StatefulWidget { @override @@ -25,8 +27,6 @@ class RegisterNew extends StatefulWidget { } class _RegisterNew extends State { - bool isTermsAccepted = true; - @override void initState() { super.initState(); @@ -40,6 +40,8 @@ class _RegisterNew extends State { @override Widget build(BuildContext context) { AppState appState = getIt.get(); + AuthenticationViewModel authVm = context.read(); + return Scaffold( backgroundColor: AppColors.bgScaffoldColor, appBar: CustomAppBar( @@ -68,12 +70,7 @@ class _RegisterNew extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.showLottie(context: context, - assetPath: 'assets/animations/lottie/register.json', - width: 200.h, - height: 200.h, - fit: BoxFit.cover, - repeat: true), + Utils.showLottie(context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.h, height: 200.h, fit: BoxFit.cover, repeat: true), SizedBox(height: 16.h), LocaleKeys.prepareToElevate.tr().toText32(isBold: true), SizedBox(height: 24.h), @@ -125,22 +122,25 @@ class _RegisterNew extends State { ), SizedBox(height: 25.h), GestureDetector( - onTap: () {}, + onTap: authVm.onTermAccepted, child: Row( children: [ - AnimatedContainer( - duration: const Duration(milliseconds: 200), - height: 24.h, - width: 24.h, - decoration: BoxDecoration( - color: isTermsAccepted ? const Color(0xFFE92227) : Colors.transparent, - borderRadius: BorderRadius.circular(6), - border: Border.all( - color: isTermsAccepted ? const Color(0xFFE92227) : Colors.grey, - width: 2.h, - ), - ), - child: isTermsAccepted ? Icon(Icons.check, size: 16.fSize, color: Colors.white) : null, + Selector( + selector: (_, viewModel) => viewModel.isTermsAccepted, + shouldRebuild: (previous, next) => previous != next, + builder: (context, isTermsAccepted, child) { + return AnimatedContainer( + duration: const Duration(milliseconds: 200), + height: 24.h, + width: 24.h, + decoration: BoxDecoration( + color: isTermsAccepted ? AppColors.primaryRedColor : Colors.transparent, + borderRadius: BorderRadius.circular(6), + border: Border.all(color: isTermsAccepted ? AppColors.primaryRedBorderColor : AppColors.greyColor, width: 2.h), + ), + child: isTermsAccepted ? Icon(Icons.check, size: 16.fSize, color: Colors.white) : null, + ); + }, ), SizedBox(width: 12.h), Expanded( @@ -157,7 +157,7 @@ class _RegisterNew extends State { text: "Register", icon: AppAssets.note_edit, onPressed: () { - showRegisterModel(context: context); + showRegisterModel(context: context, authVM: authVm); }, ), SizedBox(height: 14), @@ -200,118 +200,116 @@ class _RegisterNew extends State { )); } - void showRegisterModel({required BuildContext context, TextEditingController? textController}) { + void showRegisterModel({required BuildContext context, required AuthenticationViewModel authVM}) { showModalBottomSheet( context: context, isScrollControlled: true, isDismissible: false, useSafeArea: true, backgroundColor: Colors.transparent, - builder: (bottomSheetContext) => - Padding( - padding: EdgeInsets.only(bottom: MediaQuery - .of(bottomSheetContext) - .viewInsets - .bottom), - child: SingleChildScrollView( - child: GenericBottomSheet( - countryCode: "966", - initialPhoneNumber: "", - textController: TextEditingController(), - isEnableCountryDropdown: true, - onChange: (String? value) {}, - buttons: [ - Padding( - padding: const EdgeInsets.only(bottom: 10), - child: CustomButton( - text: LocaleKeys.sendOTPSMS.tr(), - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (BuildContext context) => OTPVerificationPage(phoneNumber: '12234567',))); - + builder: (bottomSheetContext) => Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + countryCode: authVM.selectedCountry.countryCode, + initialPhoneNumber: "", + textController: authVM.phoneNumberController, + isEnableCountryDropdown: true, + onCountryChange: authVM.onCountryChange, + onChange: authVM.onPhoneNumberChange, + buttons: [ + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (BuildContext context) => OTPVerificationPage( + phoneNumber: '12234567', + ))); - // if (mobileNo.isEmpty) { - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: TranslationBase.of(context).pleaseEnterMobile, - // showCancel: false, - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ); - // } else if (!Utils.validateMobileNumber(mobileNo)) { - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: TranslationBase.of(context).pleaseEnterValidMobile, - // showCancel: false, - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ); - // } else { - // registerUser(1); - // } - Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => OTPVerificationPage(phoneNumber: '12234567'))); - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedBorderColor, - textColor: AppColors.whiteColor, - icon: AppAssets.message, - ), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: 8.h), - child: LocaleKeys.oR.tr().toText16(color: AppColors.textColor), - ), - ], - ), + // if (mobileNo.isEmpty) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else if (!Utils.validateMobileNumber(mobileNo)) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterValidMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else { + // registerUser(1); + // } + Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => OTPVerificationPage(phoneNumber: '12234567'))); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedBorderColor, + textColor: AppColors.whiteColor, + icon: AppAssets.message, + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ Padding( - padding: EdgeInsets.only(bottom: 10.h, top: 10.h), - child: CustomButton( - text: LocaleKeys.sendOTPWHATSAPP.tr(), - onPressed: () { - // if (mobileNo.isEmpty) { - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: TranslationBase.of(context).pleaseEnterMobile, - // showCancel: false, - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ); - // } else if (!Utils.validateMobileNumber(mobileNo)) { - // context.showBottomSheet( - // child: ExceptionBottomSheet( - // message: TranslationBase.of(context).pleaseEnterValidMobile, - // showCancel: false, - // onOkPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ); - // } else { - // registerUser(4); - // } - // int? val = Utils.onOtpBtnPressed(OTPType.whatsapp, mobileNo, context); - // registerUser(val); - }, - backgroundColor: AppColors.whiteColor, - borderColor: AppColors.borderOnlyColor, - textColor: AppColors.textColor, - icon: AppAssets.whatsapp, - ), + padding: EdgeInsets.symmetric(horizontal: 8.h), + child: LocaleKeys.oR.tr().toText16(color: AppColors.textColor), ), ], ), - ), + Padding( + padding: EdgeInsets.only(bottom: 10.h, top: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPWHATSAPP.tr(), + onPressed: () { + // if (mobileNo.isEmpty) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else if (!Utils.validateMobileNumber(mobileNo)) { + // context.showBottomSheet( + // child: ExceptionBottomSheet( + // message: TranslationBase.of(context).pleaseEnterValidMobile, + // showCancel: false, + // onOkPressed: () { + // Navigator.of(context).pop(); + // }, + // ), + // ); + // } else { + // registerUser(4); + // } + // int? val = Utils.onOtpBtnPressed(OTPType.whatsapp, mobileNo, context); + // registerUser(val); + }, + backgroundColor: AppColors.whiteColor, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.textColor, + icon: AppAssets.whatsapp, + ), + ), + ], ), + ), + ), ); } } diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart index 295dd8a..660ba69 100644 --- a/lib/presentation/authentication/register_step2.dart +++ b/lib/presentation/authentication/register_step2.dart @@ -53,8 +53,11 @@ class _RegisterNew extends State { Widget build(BuildContext context) { return Scaffold( - - appBar: CustomAppBar(onBackPressed: () {}, onLanguageChanged: (lang) {}, hideLogoAndLang: true,), + appBar: CustomAppBar( + onBackPressed: () {}, + onLanguageChanged: (lang) {}, + hideLogoAndLang: true, + ), body: SingleChildScrollView( reverse: false, padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 24.h), @@ -69,30 +72,38 @@ class _RegisterNew extends State { child: Column( children: [ TextInputWidget( - labelText: isFromDubai ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(), - hintText: isFromDubai ? "name" ?? "" : (widget.nHICData!.firstNameEn!.toUpperCase() + " " + widget.nHICData!.lastNameEn!.toUpperCase()), - controller: null, - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - keyboardType: TextInputType.text, - isAllowLeadingIcon: true, - isReadOnly: isFromDubai ? false : true, - leadingIcon: AppAssets.user_circle).paddingSymmetrical(0.h,16.h), - Divider(height: 1, color: AppColors.greyColor,), + labelText: isFromDubai ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(), + hintText: isFromDubai ? "name" ?? "" : (widget.nHICData!.firstNameEn!.toUpperCase() + " " + widget.nHICData!.lastNameEn!.toUpperCase()), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + keyboardType: TextInputType.text, + isAllowLeadingIcon: true, + isReadOnly: isFromDubai ? false : true, + leadingIcon: AppAssets.user_circle) + .paddingSymmetrical(0.h, 16.h), + Divider( + height: 1, + color: AppColors.greyColor, + ), TextInputWidget( - labelText: LocaleKeys.nationalIdNumber.tr(), - hintText: isFromDubai ? "widget.payload.nationalID!" : (widget.nHICData!.idNumber ?? ""), - controller: null, - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: true, - leadingIcon: AppAssets.student_card).paddingSymmetrical(0.h,16.h), - Divider(height: 1, color: AppColors.greyColor,), + labelText: LocaleKeys.nationalIdNumber.tr(), + hintText: isFromDubai ? "widget.payload.nationalID!" : (widget.nHICData!.idNumber ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.student_card) + .paddingSymmetrical(0.h, 16.h), + Divider( + height: 1, + color: AppColors.greyColor, + ), isFromDubai ? DropdownWidget( labelText: LocaleKeys.gender.tr(), @@ -109,22 +120,25 @@ class _RegisterNew extends State { isAllowRadius: false, padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.user_full, + leadingIcon: AppAssets.user_full, ).withVerticalPadding(8) : TextInputWidget( - labelText: LocaleKeys.gender.tr(), - hintText: (widget.nHICData!.gender ?? ""), - controller: null, - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: isFromDubai ? false : true, - leadingIcon: AppAssets.user_full, - - onChange: (value) {}).paddingSymmetrical(0.h,16.h), - Divider(height: 1, color: AppColors.greyColor,), + labelText: LocaleKeys.gender.tr(), + hintText: (widget.nHICData!.gender ?? ""), + controller: null, + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: isFromDubai ? false : true, + leadingIcon: AppAssets.user_full, + onChange: (value) {}) + .paddingSymmetrical(0.h, 16.h), + Divider( + height: 1, + color: AppColors.greyColor, + ), isFromDubai ? DropdownWidget( labelText: LocaleKeys.maritalStatus.tr(), @@ -138,22 +152,26 @@ class _RegisterNew extends State { isAllowRadius: false, padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.smart_phone, + leadingIcon: AppAssets.smart_phone, ).withVerticalPadding(8) : TextInputWidget( - labelText: LocaleKeys.maritalStatus.tr(), - hintText: appState!.isArabic() - ? (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.typeAr) - : (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.type), - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: true, - leadingIcon: AppAssets.smart_phone, - onChange: (value) {}).paddingSymmetrical(0.h,16.h), - Divider(height: 1, color: AppColors.greyColor,), + labelText: LocaleKeys.maritalStatus.tr(), + hintText: appState!.isArabic() + ? (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.typeAr) + : (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.type), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.smart_phone, + onChange: (value) {}) + .paddingSymmetrical(0.h, 16.h), + Divider( + height: 1, + color: AppColors.greyColor, + ), isFromDubai ? DropdownWidget( labelText: LocaleKeys.country.tr(), @@ -170,46 +188,55 @@ class _RegisterNew extends State { leadingIcon: AppAssets.globe, ).withVerticalPadding(8) : TextInputWidget( - labelText: LocaleKeys.nationality.tr(), - hintText: appState!.isArabic() - ? (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") - : (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), + labelText: LocaleKeys.nationality.tr(), + hintText: appState!.isArabic() + ? (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") + : (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), + isEnable: true, + prefix: null, + isAllowRadius: false, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.globe, + onChange: (value) {}) + .paddingSymmetrical(0.h, 16.h), + Divider( + height: 1, + color: AppColors.greyColor, + ), + TextInputWidget( + labelText: LocaleKeys.mobileNumber.tr(), + hintText: ("widget.payload.mobileNo" ?? ""), + controller: null, isEnable: true, prefix: null, isAllowRadius: false, isBorderAllowed: false, isAllowLeadingIcon: true, isReadOnly: true, - leadingIcon: AppAssets.globe, - onChange: (value) {}).paddingSymmetrical(0.h,16.h), - Divider(height: 1, color: AppColors.greyColor,), - TextInputWidget( - labelText: LocaleKeys.mobileNumber.tr(), - hintText: ("widget.payload.mobileNo" ?? ""), - controller: null, - isEnable: true, - prefix: null, - isAllowRadius: false, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: true, - leadingIcon: AppAssets.call, - onChange: (value) {}).paddingSymmetrical(0.h,16.h), - Divider(height: 1, color: AppColors.greyColor,), + leadingIcon: AppAssets.call, + onChange: (value) {}) + .paddingSymmetrical(0.h, 16.h), + Divider( + height: 1, + color: AppColors.greyColor, + ), TextInputWidget( - labelText: LocaleKeys.dob.tr(), - hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""), - controller: null, - isEnable: true, - prefix: null, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: true, - // : SelectionType.calendar, - // selectedValue: widget.payload.dob != null ? Utils.formatDateToDisplay(widget.payload.dob.toString()) : null, - // selectionCustomIcon: AppAssets.calendar, - leadingIcon: AppAssets.birthday_cake, - onChange: (value) {}).paddingSymmetrical(0.h,16.h), + labelText: LocaleKeys.dob.tr(), + hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""), + controller: null, + isEnable: true, + prefix: null, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + // : SelectionType.calendar, + // selectedValue: widget.payload.dob != null ? Utils.formatDateToDisplay(widget.payload.dob.toString()) : null, + // selectionCustomIcon: AppAssets.calendar, + leadingIcon: AppAssets.birthday_cake, + onChange: (value) {}) + .paddingSymmetrical(0.h, 16.h), ], ), ), diff --git a/lib/widgets/bottomsheet/generic_bottom_sheet.dart b/lib/widgets/bottomsheet/generic_bottom_sheet.dart index 1d07099..08bf849 100644 --- a/lib/widgets/bottomsheet/generic_bottom_sheet.dart +++ b/lib/widgets/bottomsheet/generic_bottom_sheet.dart @@ -46,8 +46,8 @@ class _GenericBottomSheetState extends State { void initState() { super.initState(); - if (!widget.isForEmail) { - widget.textController = TextEditingController(text: widget.initialPhoneNumber); + if (!widget.isForEmail && widget.textController != null) { + widget.textController!.text = widget.initialPhoneNumber!; } } @@ -116,13 +116,16 @@ class _GenericBottomSheetState extends State { padding: EdgeInsets.all(8.h), keyboardType: widget.isForEmail ? TextInputType.emailAddress : TextInputType.number, onChange: (value) { - widget.textController!.text = value!; if (widget.onChange != null) { widget.onChange!(value); } }, + onCountryChange: (value) { + if (widget.onCountryChange != null) { + widget.onCountryChange!(value); + } + }, isEnable: true, - // focusNode: widget.myFocusNode, isReadOnly: widget.isFromSavedLogin, prefix: widget.isForEmail ? null : widget.countryCode, isBorderAllowed: false, diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index b8d36e7..ca19ee6 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -28,11 +28,12 @@ class TextInputWidget extends StatelessWidget { final bool isCountryDropDown; final bool hasError; final String? errorMessage; + Function(CountryEnum)? onCountryChange; // final List countryList; // final Function(Country)? onCountryChange; - const TextInputWidget({ + TextInputWidget({ Key? key, required this.labelText, required this.hintText, @@ -52,6 +53,7 @@ class TextInputWidget extends StatelessWidget { this.isCountryDropDown = false, this.hasError = false, this.errorMessage, + this.onCountryChange, // this.countryList = const [], // this.onCountryChange, }) : super(key: key); @@ -74,32 +76,27 @@ class TextInputWidget extends StatelessWidget { child: Row( textDirection: Directionality.of(context), children: [ - if (isAllowLeadingIcon && leadingIcon != null && !isCountryDropDown) - _buildLeadingIcon(context), - isCountryDropDown - ? CustomCountryDropdown( - countryList: CountryEnum.values, - onCountryChange: (CountryEnum? value) { - print(value); - }, - isRtl: Directionality.of(context) == TextDirection.rtl, - isFromBottomSheet: isCountryDropDown, - isEnableTextField: true, - onPhoneNumberChanged: (value) { - print(value); - }, - textField: _buildTextField(context), - ) - : Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildLabelText(), - _buildTextField(context), - ], - ), + if (isAllowLeadingIcon && leadingIcon != null && !isCountryDropDown) _buildLeadingIcon(context), + isCountryDropDown + ? CustomCountryDropdown( + countryList: CountryEnum.values, + onCountryChange: onCountryChange, + isRtl: Directionality.of(context) == TextDirection.rtl, + isFromBottomSheet: isCountryDropDown, + isEnableTextField: true, + onPhoneNumberChanged: onChange, + // textField: _buildTextField(context), + ) + : Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildLabelText(), + _buildTextField(context), + ], ), + ), ], ), ), From 2f2a8f3f8fd52fe154b71b3523ec4f25d95bf5b7 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Thu, 4 Sep 2025 14:55:59 +0300 Subject: [PATCH 11/16] search lab result screen added --- assets/langs/ar-SA.json | 3 +- assets/langs/en-US.json | 3 +- lib/core/app_assets.dart | 1 + lib/core/utils/size_utils.dart | 4 + lib/features/lab/lab_view_model.dart | 8 ++ lib/generated/locale_keys.g.dart | 1 + lib/presentation/lab/lab_orders_page.dart | 28 ++++- lib/presentation/lab/search_lab_report.dart | 116 ++++++++++++++++++++ lib/widgets/common_bottom_sheet.dart | 82 ++++++++++++++ 9 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 lib/presentation/lab/search_lab_report.dart create mode 100644 lib/widgets/common_bottom_sheet.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 718bf83..e0b2fd3 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -782,5 +782,6 @@ "resultsPending": "النتائج معلقة", "resultsAvailable": "النتائج متاحة", "viewReport": "عرض التقرير", - "prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم." + "prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم.", + "searchLabReport" : "ابحث عن تقرير المختبر" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 6743993..55338dc 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -778,5 +778,6 @@ "resultsPending": "Results Pending", "resultsAvailable": "Results Available", "viewReport": "View Report", - "prescriptionDeliveryError": "This clinic doesn't support refill" + "prescriptionDeliveryError": "This clinic doesn't support refill", + "searchLabReport" : "Search Lab Report" } \ No newline at end of file diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index bd3e541..02e7026 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -75,6 +75,7 @@ class AppAssets { static const String myFilesBottom = '$svgBasePath/my_files_bottom.svg'; static const String toDoBottom = '$svgBasePath/todo_bottom.svg'; static const String servicesBottom = '$svgBasePath/services_bottom.svg'; + static const String closeBottomNav = '$svgBasePath/close_bottom_nav.svg'; // PNGS // static const String hmg_logo = '$pngBasePath/hmg_logo.png'; diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart index 0b37082..6ad1835 100644 --- a/lib/core/utils/size_utils.dart +++ b/lib/core/utils/size_utils.dart @@ -11,6 +11,10 @@ extension ResponsiveExtension on num { double get h => ((this * _width) / FIGMA_DESIGN_WIDTH); double get fSize => ((this * _width) / FIGMA_DESIGN_WIDTH); + static double get screenHeight => SizeUtils.height; + + /// Full screen width + static double get screenWidth => SizeUtils.width; } extension FormatExtension on double { diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index acd87eb..115b28d 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -12,6 +12,10 @@ class LabViewModel extends ChangeNotifier { List patientLabOrders = []; + List labSuggestionsList =[]; + + get labSuggestions => labSuggestionsList; + LabViewModel({required this.labRepo, required this.errorHandlerService}); initLabProvider() { @@ -42,4 +46,8 @@ class LabViewModel extends ChangeNotifier { }, ); } + + filterSuggestions(){ + + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 54a1136..f7566f3 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -781,5 +781,6 @@ abstract class LocaleKeys { static const resultsAvailable = 'resultsAvailable'; static const viewReport = 'viewReport'; static const prescriptionDeliveryError = 'prescriptionDeliveryError'; + static const searchLabReport = 'searchLabReport'; } diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 8a0ce26..97e2216 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -5,14 +5,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_config.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -25,7 +30,7 @@ class LabOrdersPage extends StatefulWidget { class _LabOrdersPageState extends State { late LabViewModel labProvider; - + List?> labSuggestions = []; int? expandedIndex; @override @@ -57,7 +62,17 @@ class _LabOrdersPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.labResults.tr(context: context).toText24(isBold: true), - Utils.buildSvgWithAssets(icon: AppAssets.search_icon), + Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(() { + if(model.isLabOrdersLoading){ + return; + }else { + labSuggestions = getLabSuggestions(model); + showCommonBottomSheet(context, child: SearchLabResultsContent(), + callBackFunc: () {}, + title: LocaleKeys.searchLabReport.tr(), + height: ResponsiveExtension.screenHeight, + isCloseButtonVisible: true); + } }), ], ), SizedBox(height: 16.h), @@ -262,4 +277,13 @@ class _LabOrdersPageState extends State { return ""; } } + getLabSuggestions(LabViewModel model) { + if(model.patientLabOrders.isEmpty){ + return []; + } + return model.patientLabOrders.map((m) => m.testDetails).toList(); + + + } + } diff --git a/lib/presentation/lab/search_lab_report.dart b/lib/presentation/lab/search_lab_report.dart new file mode 100644 index 0000000..1c05174 --- /dev/null +++ b/lib/presentation/lab/search_lab_report.dart @@ -0,0 +1,116 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:sizer/sizer.dart'; + +class SearchLabResultsContent extends StatelessWidget { + const SearchLabResultsContent({super.key}); + + final List _chipLabels = const [ + "Blood Test", + "X-Ray", + "MRI Scan", + "CT Scan", + "Ultrasound", + "Urine Test", + "Allergy Test", + "Cholesterol Test", + "Diabetes Test", + "Thyroid Test", + ]; + + @override + Widget build(BuildContext context) { + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextInputWidget( + labelText:"Search lab results", + hintText: "Type test name", + controller: TextEditingController(), + isEnable: true, + prefix: null, + autoFocus: true, + isBorderAllowed: false, + padding: EdgeInsets.symmetric(vertical:ResponsiveExtension(10).h, horizontal: ResponsiveExtension(15).h), + + ), + SizedBox(height: ResponsiveExtension(20).h), + "Suggestions".toText16(isBold: true), + const SizedBox(height: 12), + ], + ), + ), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Wrap( + alignment: WrapAlignment.start, + spacing: 10, + runSpacing: 10, + children: _chipLabels + .map((label) => SuggestionChip( + label: label, + onTap: () {}, + )) + .toList(), + ), + ), + ), + Container( + color: Colors.white, + padding: EdgeInsets.all(ResponsiveExtension(20).h), + child: CustomButton( + text: LocaleKeys.search.tr(), + icon: AppAssets.search_icon, + iconColor: Colors.white, + onPressed: () => Navigator.pop(context), + ), + ), + ], + ); + } +} + +class SuggestionChip extends StatelessWidget { + final String label; + final bool isSelected; + final VoidCallback? onTap; + + const SuggestionChip({ + super.key, + required this.label, + this.isSelected = false, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, // optional tap callback + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + decoration: BoxDecoration( + color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor, + borderRadius: BorderRadius.circular(8), + ), + child: label.toText12( + color: isSelected ? Colors.white : Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ); + } +} diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart new file mode 100644 index 0000000..3d72864 --- /dev/null +++ b/lib/widgets/common_bottom_sheet.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; + +void showCommonBottomSheet(BuildContext context, {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true}) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: AppColors.scaffoldBgColor, + builder: (BuildContext context) { + return Container( + height: height, + decoration: const BoxDecoration( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + child: ButtonSheetContent( + title: title!, + isCloseButtonVisible: isCloseButtonVisible, + child: child, + ), + ); + }).then((value) { + callBackFunc(); + }); +} + +class ButtonSheetContent extends StatelessWidget { + final Widget child; + final String title; + final bool isCloseButtonVisible; + + const ButtonSheetContent({super.key, required this.child, required this.isCloseButtonVisible, required this.title}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 20.h,), + Center( + child: Container( + margin: const EdgeInsets.only(top: 18, bottom: 12), + height: 4, + width: 40.h, + decoration: BoxDecoration( + color: Colors.grey[400], + borderRadius: BorderRadius.circular(2), + ), + ), + ), + + // Close button + isCloseButtonVisible + ? Padding( + padding: EdgeInsets.symmetric(horizontal: 16), child: Utils.buildSvgWithAssets( icon: AppAssets.closeBottomNav, + width: 32, + height: 32).onPress((){ + Navigator.of(context).pop(); + }), + ) + : SizedBox(), + + SizedBox(height: 20,), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text( + title, + style: TextStyle(fontSize: 27.h, fontWeight: FontWeight.bold), + ), + ), + const SizedBox(height: 16), + + Expanded(child: child) + ], + ); + + } +} From e8cc6407b62ff3f6965378074b476e51bf6ad246 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 4 Sep 2025 14:58:00 +0300 Subject: [PATCH 12/16] Insurance update implementation contd. --- assets/images/svg/cancel_circle.svg | 3 + assets/images/svg/insurance_history_icon.svg | 4 + assets/images/svg/update_insurance_card.svg | 4 + lib/core/app_assets.dart | 3 + lib/core/dependencies.dart | 10 ++ lib/features/insurance/insurance_repo.dart | 77 +++++++++++ .../insurance/insurance_view_model.dart | 43 ++++++ ...ient_insurance_details_response_model.dart | 96 +++++++++++++ lib/main.dart | 7 + lib/presentation/home/navigation_screen.dart | 15 +- .../home/widgets/small_service_card.dart | 6 + .../insurance/insurance_home_page.dart | 82 +++++++++++ .../widgets/patient_insurance_card.dart | 128 ++++++++++++++++++ .../medical_file/medical_file_page.dart | 112 +++------------ 14 files changed, 485 insertions(+), 105 deletions(-) create mode 100644 assets/images/svg/cancel_circle.svg create mode 100644 assets/images/svg/insurance_history_icon.svg create mode 100644 assets/images/svg/update_insurance_card.svg create mode 100644 lib/features/insurance/insurance_repo.dart create mode 100644 lib/features/insurance/insurance_view_model.dart create mode 100644 lib/features/insurance/models/resp_models/patient_insurance_details_response_model.dart create mode 100644 lib/presentation/insurance/insurance_home_page.dart create mode 100644 lib/presentation/insurance/widgets/patient_insurance_card.dart diff --git a/assets/images/svg/cancel_circle.svg b/assets/images/svg/cancel_circle.svg new file mode 100644 index 0000000..3ceda6f --- /dev/null +++ b/assets/images/svg/cancel_circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/insurance_history_icon.svg b/assets/images/svg/insurance_history_icon.svg new file mode 100644 index 0000000..4ae2a1c --- /dev/null +++ b/assets/images/svg/insurance_history_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/update_insurance_card.svg b/assets/images/svg/update_insurance_card.svg new file mode 100644 index 0000000..9b21aab --- /dev/null +++ b/assets/images/svg/update_insurance_card.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 7d32262..530064e 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -68,6 +68,9 @@ class AppAssets { static const String doctor_calendar_icon = '$svgBasePath/doctor_calendar_icon.svg'; static const String prescription_remarks_icon = '$svgBasePath/prescription_remarks_icon.svg'; static const String prescription_reminder_icon = '$svgBasePath/prescription_reminder_icon.svg'; + static const String insurance_history_icon = '$svgBasePath/insurance_history_icon.svg'; + static const String cancel_circle_icon = '$svgBasePath/cancel_circle.svg'; + static const String update_insurance_card_icon = '$svgBasePath/update_insurance_card.svg'; //bottom navigation// diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 65fd2c6..aa32fa6 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -6,6 +6,8 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_repo. import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; @@ -65,6 +67,7 @@ class AppDependencies { getIt.registerLazySingleton(() => LabRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => RadiologyRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => PrescriptionsRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => InsuranceRepoImp(loggerService: getIt(), apiClient: getIt())); // ViewModels // Global/shared VMs → LazySingleton @@ -90,6 +93,13 @@ class AppDependencies { ), ); + getIt.registerLazySingleton( + () => InsuranceViewModel( + insuranceRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + getIt.registerLazySingleton( () => AuthenticationViewModel( authenticationRepo: getIt(), diff --git a/lib/features/insurance/insurance_repo.dart b/lib/features/insurance/insurance_repo.dart new file mode 100644 index 0000000..a5c624f --- /dev/null +++ b/lib/features/insurance/insurance_repo.dart @@ -0,0 +1,77 @@ +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class InsuranceRepo { + Future>>> getPatientInsuranceDetails({required String patientId}); +} + +class InsuranceRepoImp implements InsuranceRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + InsuranceRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>>> getPatientInsuranceDetails({required String patientId}) async { + final mapDevice = { + "isDentalAllowedBackend": false, + "VersionID": 50.0, + "Channel": 3, + "LanguageID": 2, + "IPAdress": "10.20.10.20", + "generalid": "Cs2020@2016\$2958", + "Latitude": 0.0, + "Longitude": 0.0, + "DeviceTypeID": 1, + "PatientType": 1, + "PatientTypeID": 1, + "TokenID": "@dm!n", + "PatientID": "3628599", + "PatientOutSA": "0", + "SessionID": "03478TYC02N80874CTYN04883475!?" + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_PAtIENTS_INSURANCE, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus}) { + try { + final list = response['List_PatientInsuranceCard']; + if (list == null || list.isEmpty) { + throw Exception("insurance list is empty"); + } + + final labOrders = list.map((item) => PatientInsuranceDetailsResponseModel.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: labOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + + throw UnimplementedError(); + } +} diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart new file mode 100644 index 0000000..545a32a --- /dev/null +++ b/lib/features/insurance/insurance_view_model.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; +import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; + +class InsuranceViewModel extends ChangeNotifier { + bool isInsuranceLoading = false; + + InsuranceRepo insuranceRepo; + ErrorHandlerService errorHandlerService; + + List patientInsuranceList = []; + + InsuranceViewModel({required this.insuranceRepo, required this.errorHandlerService}); + + initInsuranceProvider() { + patientInsuranceList.clear(); + isInsuranceLoading = true; + getPatientInsuranceDetails(); + notifyListeners(); + } + + Future getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await insuranceRepo.getPatientInsuranceDetails(patientId: "1231755"); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + patientInsuranceList = apiResponse.data!; + isInsuranceLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } +} diff --git a/lib/features/insurance/models/resp_models/patient_insurance_details_response_model.dart b/lib/features/insurance/models/resp_models/patient_insurance_details_response_model.dart new file mode 100644 index 0000000..b734abc --- /dev/null +++ b/lib/features/insurance/models/resp_models/patient_insurance_details_response_model.dart @@ -0,0 +1,96 @@ +class PatientInsuranceDetailsResponseModel { + String? setupID; + int? projectID; + bool? isActive; + int? patientID; + int? companyID; + int? subCategoryID; + dynamic companyType; + String? patientCardID; + String? cardValidTo; + int? patientCreditLimit; + String? subPolicyNo; + String? companyName; + String? companyNameN; + String? subCategoryDesc; + dynamic subCategoryDescN; + bool? isElectronicClaim; + String? subCategoryValidTo; + dynamic groupID; + String? groupName; + dynamic groupNameN; + String? insurancePolicyNo; + + PatientInsuranceDetailsResponseModel( + {this.setupID, + this.projectID, + this.isActive, + this.patientID, + this.companyID, + this.subCategoryID, + this.companyType, + this.patientCardID, + this.cardValidTo, + this.patientCreditLimit, + this.subPolicyNo, + this.companyName, + this.companyNameN, + this.subCategoryDesc, + this.subCategoryDescN, + this.isElectronicClaim, + this.subCategoryValidTo, + this.groupID, + this.groupName, + this.groupNameN, + this.insurancePolicyNo}); + + PatientInsuranceDetailsResponseModel.fromJson(Map json) { + setupID = json['SetupID']; + projectID = json['ProjectID']; + isActive = json['IsActive']; + patientID = json['PatientID']; + companyID = json['CompanyID']; + subCategoryID = json['SubCategoryID']; + companyType = json['CompanyType']; + patientCardID = json['PatientCardID']; + cardValidTo = json['CardValidTo']; + patientCreditLimit = json['PatientCreditLimit']; + subPolicyNo = json['SubPolicyNo']; + companyName = json['CompanyName']; + companyNameN = json['CompanyNameN']; + subCategoryDesc = json['SubCategoryDesc']; + subCategoryDescN = json['SubCategoryDescN']; + isElectronicClaim = json['IsElectronicClaim']; + subCategoryValidTo = json['SubCategoryValidTo']; + groupID = json['GroupID']; + groupName = json['GroupName']; + groupNameN = json['GroupNameN']; + insurancePolicyNo = json['InsurancePolicyNo']; + } + + Map toJson() { + final Map data = new Map(); + data['SetupID'] = this.setupID; + data['ProjectID'] = this.projectID; + data['IsActive'] = this.isActive; + data['PatientID'] = this.patientID; + data['CompanyID'] = this.companyID; + data['SubCategoryID'] = this.subCategoryID; + data['CompanyType'] = this.companyType; + data['PatientCardID'] = this.patientCardID; + data['CardValidTo'] = this.cardValidTo; + data['PatientCreditLimit'] = this.patientCreditLimit; + data['SubPolicyNo'] = this.subPolicyNo; + data['CompanyName'] = this.companyName; + data['CompanyNameN'] = this.companyNameN; + data['SubCategoryDesc'] = this.subCategoryDesc; + data['SubCategoryDescN'] = this.subCategoryDescN; + data['IsElectronicClaim'] = this.isElectronicClaim; + data['SubCategoryValidTo'] = this.subCategoryValidTo; + data['GroupID'] = this.groupID; + data['GroupName'] = this.groupName; + data['GroupNameN'] = this.groupNameN; + data['InsurancePolicyNo'] = this.insurancePolicyNo; + return data; + } +} diff --git a/lib/main.dart b/lib/main.dart index 035c6bf..ce69f5d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; @@ -71,6 +72,12 @@ void main() async { errorHandlerService: getIt(), ), ), + ChangeNotifierProvider( + create: (_) => InsuranceViewModel( + insuranceRepo: getIt(), + errorHandlerService: getIt(), + ), + ), ChangeNotifierProvider( create: (_) => AuthenticationViewModel( authenticationRepo: getIt(), diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index f3e088b..152bbd2 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -20,22 +20,19 @@ class _LandingNavigationState extends State { body: PageView( controller: _pageController, physics: const NeverScrollableScrollPhysics(), - children: const [ - LandingPage(), + children: [ + const LandingPage(), MedicalFilePage(), - LandingPage(), - LandingPage(), - LandingPage(), + const LandingPage(), + const LandingPage(), + const LandingPage(), ], ), bottomNavigationBar: BottomNavigation( currentIndex: _currentIndex, onTap: (index) { setState(() => _currentIndex = index); - _pageController.animateToPage( - index, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut); + _pageController.animateToPage(index, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut); }, ), ); diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart index 297ed2b..f988e18 100644 --- a/lib/presentation/home/widgets/small_service_card.dart +++ b/lib/presentation/home/widgets/small_service_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; @@ -78,6 +79,11 @@ class SmallServiceCard extends StatelessWidget { ); break; case "insurance_update": + Navigator.of(context).push( + FadePage( + page: InsuranceHomePage(), + ), + ); break; default: // Handle unknown service diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart new file mode 100644 index 0000000..d348f8d --- /dev/null +++ b/lib/presentation/insurance/insurance_home_page.dart @@ -0,0 +1,82 @@ +import 'dart:async'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class InsuranceHomePage extends StatefulWidget { + const InsuranceHomePage({super.key}); + + @override + State createState() => _InsuranceHomePageState(); +} + +class _InsuranceHomePageState extends State { + late InsuranceViewModel insuranceViewModel; + + @override + void initState() { + scheduleMicrotask(() { + insuranceViewModel.initInsuranceProvider(); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + insuranceViewModel = Provider.of(context); + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + appBar: AppBar( + title: LocaleKeys.insurance.tr(context: context).toText18(), + backgroundColor: AppColors.bgScaffoldColor, + ), + body: SingleChildScrollView( + child: Consumer(builder: (context, insuranceVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.updateInsurance.tr(context: context)}".toText24(isBold: true), + CustomButton( + icon: AppAssets.insurance_history_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 21.h, + text: LocaleKeys.history.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), + borderColor: AppColors.primaryRedColor.withOpacity(0.0), + textColor: AppColors.primaryRedColor, + fontSize: 14, + fontWeight: FontWeight.w600, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), + ], + ).paddingSymmetrical(24.h, 24.h), + insuranceVM.isInsuranceLoading + ? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0) + : PatientInsuranceCard( + insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first, + isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))), + ], + ); + }), + ), + ); + } +} diff --git a/lib/presentation/insurance/widgets/patient_insurance_card.dart b/lib/presentation/insurance/widgets/patient_insurance_card.dart new file mode 100644 index 0000000..7726b94 --- /dev/null +++ b/lib/presentation/insurance/widgets/patient_insurance_card.dart @@ -0,0 +1,128 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class PatientInsuranceCard extends StatelessWidget { + PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired}); + + PatientInsuranceDetailsResponseModel insuranceCardDetailsModel; + bool isInsuranceExpired = false; + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Haroon Amjad".toText18(isBold: true), + "Policy: ${insuranceCardDetailsModel.insurancePolicyNo}".toText12(isBold: true, color: AppColors.lightGrayColor), + ], + ), + CustomButton( + icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon, + iconColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor, + iconSize: 13.h, + text: isInsuranceExpired ? "Insurance Expired" : "Insurance Active", + onPressed: () {}, + backgroundColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.15) : AppColors.successColor.withOpacity(0.15), + borderColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.01) : AppColors.successColor.withOpacity(0.01), + textColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + SizedBox(height: 12.h), + insuranceCardDetailsModel.groupName!.toText12(isBold: true), + insuranceCardDetailsModel.companyName!.toText12(isBold: true), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 6.h, + runSpacing: 6.h, + children: [ + Row( + children: [ + CustomButton( + icon: AppAssets.doctor_calendar_icon, + iconColor: AppColors.blackColor, + iconSize: 13.h, + text: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + Row( + children: [ + CustomButton( + text: "Patient Card ID: ${insuranceCardDetailsModel.patientCardID}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + ], + ), + ], + ), + SizedBox(height: 10.h), + isInsuranceExpired + ? CustomButton( + icon: AppAssets.update_insurance_card_icon, + iconColor: AppColors.successColor, + iconSize: 15.h, + text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}", + onPressed: () {}, + backgroundColor: AppColors.bgGreenColor.withOpacity(0.20), + borderColor: AppColors.bgGreenColor.withOpacity(0.0), + textColor: AppColors.bgGreenColor, + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ) + : Container(), + ], + ), + ), + ).paddingSymmetrical(24.h, 0.h); + } +} diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 7fa4fe9..da5227a 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -1,20 +1,28 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart'; import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:provider/provider.dart'; class MedicalFilePage extends StatelessWidget { - const MedicalFilePage({super.key}); + MedicalFilePage({super.key}); + + late InsuranceViewModel insuranceViewModel; @override Widget build(BuildContext context) { + insuranceViewModel = Provider.of(context); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, appBar: AppBar( @@ -162,101 +170,13 @@ class MedicalFilePage extends StatelessWidget { ), SizedBox(height: 16.h), //Insurance Tab Data - Container( - // height: 150.h, - width: double.infinity, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Haroon Amjad".toText18(isBold: true), - "Policy: 223123345".toText12(isBold: true, color: AppColors.lightGrayColor), - ], - ), - CustomButton( - icon: AppAssets.cross_circle, - iconColor: AppColors.primaryRedColor, - iconSize: 13.h, - text: "Insurance Expired", - onPressed: () {}, - backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), - borderColor: AppColors.primaryRedColor.withOpacity(0.0), - textColor: AppColors.primaryRedColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - ], - ), - SizedBox(height: 12.h), - "NCCI".toText12(isBold: true), - "NC_Dr Sulaiman Al Habib Medical Group".toText12(isBold: true), - SizedBox(height: 8.h), - Row( - children: [ - CustomButton( - icon: AppAssets.cross_circle, - iconColor: AppColors.primaryRedColor, - iconSize: 13.h, - text: "Expiry: 18 Mar, 2025", - onPressed: () {}, - backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), - borderColor: AppColors.primaryRedColor.withOpacity(0.0), - textColor: AppColors.primaryRedColor, - fontSize: 10, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - SizedBox(width: 5.h), - CustomButton( - text: "Patient Card ID: 3628599", - onPressed: () {}, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 10, - fontWeight: FontWeight.normal, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 30.h, - ), - ], - ), - SizedBox(height: 10.h), - CustomButton( - icon: AppAssets.cross_circle, - iconColor: AppColors.primaryRedColor, - iconSize: 13.h, - text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}", - onPressed: () {}, - backgroundColor: AppColors.bgGreenColor.withOpacity(0.20), - borderColor: AppColors.bgGreenColor.withOpacity(0.0), - textColor: AppColors.bgGreenColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - ], - ), - ), - ), + Consumer(builder: (context, insuranceVM, child) { + return insuranceVM.isInsuranceLoading + ? const MoviesShimmerWidget() + : PatientInsuranceCard( + insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first, + isInsuranceExpired: DateTime.now().isBefore(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))); + }), SizedBox(height: 10.h), GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 13, mainAxisSpacing: 13), From 333b813f907bfebd9eefb6aca09118093439e000 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 4 Sep 2025 15:22:54 +0300 Subject: [PATCH 13/16] bottom sheet fixes --- .../insurance/insurance_home_page.dart | 13 ++- .../widgets/patient_insurance_card.dart | 5 ++ lib/presentation/lab/lab_orders_page.dart | 2 +- lib/widgets/common_bottom_sheet.dart | 79 ++++++++++--------- 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart index d348f8d..eea50c5 100644 --- a/lib/presentation/insurance/insurance_home_page.dart +++ b/lib/presentation/insurance/insurance_home_page.dart @@ -9,9 +9,11 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart'; +import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -56,7 +58,16 @@ class _InsuranceHomePageState extends State { iconColor: AppColors.primaryRedColor, iconSize: 21.h, text: LocaleKeys.history.tr(context: context), - onPressed: () {}, + onPressed: () { + showCommonBottomSheet( + context, + child: Container(), + callBackFunc: () {}, + title: "", + height: ResponsiveExtension.screenHeight * 0.5, + isCloseButtonVisible: false, + ); + }, backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), borderColor: AppColors.primaryRedColor.withOpacity(0.0), textColor: AppColors.primaryRedColor, diff --git a/lib/presentation/insurance/widgets/patient_insurance_card.dart b/lib/presentation/insurance/widgets/patient_insurance_card.dart index 7726b94..2701233 100644 --- a/lib/presentation/insurance/widgets/patient_insurance_card.dart +++ b/lib/presentation/insurance/widgets/patient_insurance_card.dart @@ -5,10 +5,12 @@ import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:provider/provider.dart'; class PatientInsuranceCard extends StatelessWidget { PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired}); @@ -16,8 +18,11 @@ class PatientInsuranceCard extends StatelessWidget { PatientInsuranceDetailsResponseModel insuranceCardDetailsModel; bool isInsuranceExpired = false; + late InsuranceViewModel insuranceViewModel; + @override Widget build(BuildContext context) { + insuranceViewModel = Provider.of(context); return Container( width: double.infinity, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 97e2216..e829c93 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -65,7 +65,7 @@ class _LabOrdersPageState extends State { Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(() { if(model.isLabOrdersLoading){ return; - }else { + } else { labSuggestions = getLabSuggestions(model); showCommonBottomSheet(context, child: SearchLabResultsContent(), callBackFunc: () {}, diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index 3d72864..f3d7425 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -9,14 +9,13 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; void showCommonBottomSheet(BuildContext context, {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true}) { showModalBottomSheet( context: context, - isScrollControlled: true, + isScrollControlled: false, + showDragHandle: false, backgroundColor: AppColors.scaffoldBgColor, builder: (BuildContext context) { return Container( height: height, - decoration: const BoxDecoration( - borderRadius: BorderRadius.vertical(top: Radius.circular(20)), - ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.scaffoldBgColor, borderRadius: 24.h), child: ButtonSheetContent( title: title!, isCloseButtonVisible: isCloseButtonVisible, @@ -37,46 +36,48 @@ class ButtonSheetContent extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 20.h,), - Center( - child: Container( - margin: const EdgeInsets.only(top: 18, bottom: 12), - height: 4, - width: 40.h, - decoration: BoxDecoration( - color: Colors.grey[400], - borderRadius: BorderRadius.circular(2), - ), + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // SizedBox( + // height: 20.h, + // ), + Center( + child: Container( + margin: const EdgeInsets.only(top: 18, bottom: 12), + height: 4, + width: 40.h, + decoration: BoxDecoration( + color: Colors.grey[400], + borderRadius: BorderRadius.circular(2), ), ), + ), - // Close button - isCloseButtonVisible - ? Padding( - padding: EdgeInsets.symmetric(horizontal: 16), child: Utils.buildSvgWithAssets( icon: AppAssets.closeBottomNav, - width: 32, - height: 32).onPress((){ - Navigator.of(context).pop(); - }), - ) - : SizedBox(), + // Close button + isCloseButtonVisible + ? Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Utils.buildSvgWithAssets(icon: AppAssets.closeBottomNav, width: 32, height: 32).onPress(() { + Navigator.of(context).pop(); + }), + ) + : SizedBox(), - SizedBox(height: 20,), - Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: Text( - title, - style: TextStyle(fontSize: 27.h, fontWeight: FontWeight.bold), - ), + SizedBox( + height: 20, + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text( + title, + style: TextStyle(fontSize: 27.h, fontWeight: FontWeight.bold), ), - const SizedBox(height: 16), - - Expanded(child: child) - ], - ); + ), + const SizedBox(height: 16), + Expanded(child: child) + ], + ); } } From 3855ba276f5791d98c5701e148f0e6f3d1394fb2 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 4 Sep 2025 16:22:43 +0300 Subject: [PATCH 14/16] calender & signup fixes. --- lib/core/app_state.dart | 13 +- .../authentication_view_model.dart | 47 ++++- lib/presentation/authentication/login.dart | 2 +- lib/presentation/authentication/register.dart | 37 ++-- .../authentication/register_step2.dart | 199 ++++++++++-------- lib/widgets/input_widget.dart | 56 +++-- pubspec.lock | 10 +- pubspec.yaml | 1 + 8 files changed, 226 insertions(+), 139 deletions(-) diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 7c42042..eb9315c 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -22,13 +22,8 @@ class AppState { set setUserLong(v) => userLong = v; - final PostParamsModel _postParamsInitConfig = PostParamsModel( - channel: 3, - versionID: ApiConsts.VERSION_ID, - ipAddress: '10.20.10.20', - generalId: 'Cs2020@2016\$2958', - deviceTypeID: "2", - sessionID: 'TMRhVmkGhOsvamErw'); + final PostParamsModel _postParamsInitConfig = + PostParamsModel(channel: 3, versionID: ApiConsts.VERSION_ID, ipAddress: '10.20.10.20', generalId: 'Cs2020@2016\$2958', deviceTypeID: "2", sessionID: 'TMRhVmkGhOsvamErw'); void setPostParamsInitConfig() { isAuthenticated = false; @@ -50,7 +45,9 @@ class AppState { bool isArabic() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode == "ar"; - int getLanguageID(context) => EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2; + int getLanguageID() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode == "ar" ? 1 : 2; + + String? getLanguageCode() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode; AuthenticatedUser? _authenticatedUser; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 9abd673..c34c339 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -1,8 +1,12 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/request_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart'; import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_patient_authentication_request_model.dart'; import 'package:hmg_patient_app_new/services/dialog_service.dart'; @@ -21,18 +25,53 @@ class AuthenticationViewModel extends ChangeNotifier { required this.dialogService, }); - final TextEditingController nationalIdController = TextEditingController(); - final TextEditingController phoneNumberController = TextEditingController(); - CountryEnum selectedCountry = CountryEnum.saudiArabia; + final TextEditingController nationalIdController = TextEditingController(), phoneNumberController = TextEditingController(), dobController = TextEditingController(); + CountryEnum selectedCountrySignup = CountryEnum.saudiArabia; + MaritalStatusTypeEnum? maritalStatus; + GenderTypeEnum? genderType; bool isTermsAccepted = false; + List? countriesList; + NationalityCountries? pickedCountryByUAEUser; void login() { if (ValidationUtils.isValidatePhoneAndId(nationalId: nationalIdController.text, phoneNumber: phoneNumberController.text)) { } else {} } + void clearDefaults() { + nationalIdController.clear(); + phoneNumberController.clear(); + dobController.clear(); + maritalStatus = null; + genderType = null; + isTermsAccepted = false; + selectedCountrySignup = CountryEnum.saudiArabia; + pickedCountryByUAEUser = null; + } + void onCountryChange(CountryEnum country) { - selectedCountry = country; + selectedCountrySignup = country; + notifyListeners(); + } + + void loadCountriesData({required BuildContext context}) async { + final String response = await DefaultAssetBundle.of(context).loadString('assets/json/countriesList.json'); + final List data = json.decode(response); + countriesList = data.map((e) => NationalityCountries.fromJson(e)).toList(); + } + + void onMaritalStatusChange(String? status) { + maritalStatus = MaritalStatusTypeExtension.fromType(status)!; + notifyListeners(); + } + + void onGenderChange(String? status) { + genderType = GenderTypeExtension.fromType(status)!; + notifyListeners(); + } + + void onUAEUserCountrySelection(String? value) { + pickedCountryByUAEUser = countriesList!.firstWhere((element) => element.name == value); notifyListeners(); } diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index f171443..aef877f 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -151,7 +151,7 @@ class _LoginScreen extends State { padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), child: SingleChildScrollView( child: GenericBottomSheet( - countryCode: authVM.selectedCountry.countryCode, + countryCode: authVM.selectedCountrySignup.countryCode, initialPhoneNumber: "", textController: authVM.phoneNumberController, isEnableCountryDropdown: true, diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 85ff402..4756cba 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -83,30 +83,31 @@ class _RegisterNew extends State { children: [ CustomCountryDropdown( countryList: CountryEnum.values, - onCountryChange: (CountryEnum? value) {}, + onCountryChange: authVm.onCountryChange, isRtl: Directionality.of(context) == TextDirection.LTR, ).withVerticalPadding(8.h), Divider(height: 1.h), TextInputWidget( - labelText: LocaleKeys.nationalIdNumber.tr(), - hintText: "xxxxxxxxx", - controller: TextEditingController(), - isEnable: true, - prefix: null, - isAllowRadius: true, - isBorderAllowed: false, - isAllowLeadingIcon: true, - autoFocus: true, - padding: EdgeInsets.symmetric(vertical: 8.h), - leadingIcon: AppAssets.student_card, - onChange: (value) { - print(value); - }).withVerticalPadding(8), + labelText: LocaleKeys.nationalIdNumber.tr(), + hintText: "xxxxxxxxx", + controller: authVm.nationalIdController, + isEnable: true, + prefix: null, + isAllowRadius: true, + isBorderAllowed: false, + isAllowLeadingIcon: true, + autoFocus: true, + padding: EdgeInsets.symmetric(vertical: 8.h), + leadingIcon: AppAssets.student_card, + // onChange: (value) { + // print(value); + // } + ).withVerticalPadding(8), Divider(height: 1), TextInputWidget( labelText: LocaleKeys.dob.tr(), hintText: "11 July, 1994", - controller: TextEditingController(), + controller: authVm.dobController, isEnable: true, prefix: null, isAllowRadius: true, @@ -114,7 +115,7 @@ class _RegisterNew extends State { isAllowLeadingIcon: true, padding: EdgeInsets.symmetric(vertical: 8.h), leadingIcon: AppAssets.birthday_cake, - onChange: (value) {}, + selectionType: SelectionTypeEnum.calendar, ).withVerticalPadding(8), ], ), @@ -211,7 +212,7 @@ class _RegisterNew extends State { padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), child: SingleChildScrollView( child: GenericBottomSheet( - countryCode: authVM.selectedCountry.countryCode, + countryCode: authVM.selectedCountrySignup.countryCode, initialPhoneNumber: "", textController: authVM.phoneNumberController, isEnableCountryDropdown: true, diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart index 660ba69..483ce5c 100644 --- a/lib/presentation/authentication/register_step2.dart +++ b/lib/presentation/authentication/register_step2.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart'; @@ -11,6 +12,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/context_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; @@ -18,6 +20,7 @@ import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dar import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:provider/provider.dart'; class RegisterNewStep2 extends StatefulWidget { var nHICData; @@ -31,30 +34,31 @@ class RegisterNewStep2 extends StatefulWidget { class _RegisterNew extends State { bool isFromDubai = true; - List countriesList = []; - AppState? appState; - GenderTypeEnum? selectedGenderType; - MaritalStatusTypeEnum? selectedMaritalStatusType; - CountryEnum? selectedCountry; + AuthenticationViewModel? authVM; @override void initState() { super.initState(); + authVM = context.read(); + authVM!.loadCountriesData(context: context); // isFromDubai = widget.payload.zipCode!.contains("971") || widget.payload.zipCode!.contains("+971"); - loadCountriesList(); } - loadCountriesList() async { - appState = getIt.get(); - final String response = await DefaultAssetBundle.of(context).loadString('assets/json/countriesList.json'); - final List data = json.decode(response); - countriesList = data.map((e) => NationalityCountries.fromJson(e)).toList(); + @override + void dispose() { + super.dispose(); + authVM!.clearDefaults(); } + @override Widget build(BuildContext context) { + AppState appState = getIt.get(); return Scaffold( appBar: CustomAppBar( - onBackPressed: () {}, + onBackPressed: () { + Navigator.of(context).pop(); + authVM!.clearDefaults(); + }, onLanguageChanged: (lang) {}, hideLogoAndLang: true, ), @@ -84,10 +88,7 @@ class _RegisterNew extends State { isReadOnly: isFromDubai ? false : true, leadingIcon: AppAssets.user_circle) .paddingSymmetrical(0.h, 16.h), - Divider( - height: 1, - color: AppColors.greyColor, - ), + Divider(height: 1, color: AppColors.greyColor), TextInputWidget( labelText: LocaleKeys.nationalIdNumber.tr(), hintText: isFromDubai ? "widget.payload.nationalID!" : (widget.nHICData!.idNumber ?? ""), @@ -100,28 +101,28 @@ class _RegisterNew extends State { isReadOnly: true, leadingIcon: AppAssets.student_card) .paddingSymmetrical(0.h, 16.h), - Divider( - height: 1, - color: AppColors.greyColor, - ), + Divider(height: 1, color: AppColors.greyColor), isFromDubai - ? DropdownWidget( - labelText: LocaleKeys.gender.tr(), - hintText: LocaleKeys.malE.tr(), - isEnable: true, - dropdownItems: GenderTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), - selectedValue: appState!.isArabic() ? selectedGenderType!.typeAr : selectedGenderType?.type, - // selectionType: SelectionType.dropdown, - onChange: (val) { - if (val != null) {} - }, - isBorderAllowed: false, - hasSelectionCustomIcon: true, - isAllowRadius: false, - padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.user_full, - ).withVerticalPadding(8) + ? Selector( + selector: (_, authViewModel) => authViewModel.genderType, + shouldRebuild: (previous, next) => previous != next, + builder: (context, genderType, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.gender.tr(), + hintText: LocaleKeys.malE.tr(), + isEnable: true, + dropdownItems: GenderTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: genderType != null ? (appState!.isArabic() ? genderType!.typeAr : genderType!.type) : "", + onChange: authVM.onGenderChange, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.user_full, + ).withVerticalPadding(8); + }) : TextInputWidget( labelText: LocaleKeys.gender.tr(), hintText: (widget.nHICData!.gender ?? ""), @@ -135,25 +136,29 @@ class _RegisterNew extends State { leadingIcon: AppAssets.user_full, onChange: (value) {}) .paddingSymmetrical(0.h, 16.h), - Divider( - height: 1, - color: AppColors.greyColor, - ), + Divider(height: 1, color: AppColors.greyColor), isFromDubai - ? DropdownWidget( - labelText: LocaleKeys.maritalStatus.tr(), - hintText: LocaleKeys.married.tr(), - isEnable: true, - dropdownItems: MaritalStatusTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), - selectedValue: appState!.isArabic() ? selectedMaritalStatusType!.typeAr : selectedMaritalStatusType?.type, - onChange: (val) {}, - isBorderAllowed: false, - hasSelectionCustomIcon: true, - isAllowRadius: false, - padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.smart_phone, - ).withVerticalPadding(8) + ? Selector( + selector: (_, authViewModel) => authViewModel.maritalStatus, + shouldRebuild: (previous, next) => previous != next, + builder: (context, maritalStatus, child) { + final authVM = context.read(); // For onChange + return DropdownWidget( + labelText: LocaleKeys.maritalStatus.tr(), + hintText: LocaleKeys.married.tr(), + isEnable: true, + dropdownItems: MaritalStatusTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(), + selectedValue: maritalStatus != null ? (appState!.isArabic() ? maritalStatus.typeAr : maritalStatus.type) : "", + onChange: authVM.onMaritalStatusChange, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.smart_phone, + ).withVerticalPadding(8); + }, + ) : TextInputWidget( labelText: LocaleKeys.maritalStatus.tr(), hintText: appState!.isArabic() @@ -168,30 +173,41 @@ class _RegisterNew extends State { leadingIcon: AppAssets.smart_phone, onChange: (value) {}) .paddingSymmetrical(0.h, 16.h), - Divider( - height: 1, - color: AppColors.greyColor, - ), + Divider(height: 1, color: AppColors.greyColor), isFromDubai - ? DropdownWidget( - labelText: LocaleKeys.country.tr(), - hintText: LocaleKeys.uae.tr(), - isEnable: true, - dropdownItems: countriesList.map((e) => appState!.isArabic() ? e.nameN ?? "" : e.name ?? "").toList(), - selectedValue: appState!.isArabic() ? selectedCountry!.nameArabic ?? "" : selectedCountry?.name ?? "", - onChange: (val) {}, - isBorderAllowed: false, - hasSelectionCustomIcon: true, - isAllowRadius: false, - padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), - selectionCustomIcon: AppAssets.arrow_down, - leadingIcon: AppAssets.globe, - ).withVerticalPadding(8) + ? Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( + selector: (context, authViewModel) { + final appState = getIt.get(); + return (countriesList: authViewModel.countriesList, selectedCountry: authViewModel.pickedCountryByUAEUser, isArabic: appState.isArabic()); + }, + shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic, + builder: (context, data, child) { + final authVM = context.read(); + return DropdownWidget( + labelText: LocaleKeys.country.tr(), + hintText: LocaleKeys.uae.tr(), + isEnable: true, + dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(), + selectedValue: data.selectedCountry != null + ? data.isArabic + ? data.selectedCountry!.nameN ?? "" + : data.selectedCountry!.name ?? "" + : "", + onChange: authVM.onUAEUserCountrySelection, + isBorderAllowed: false, + hasSelectionCustomIcon: true, + isAllowRadius: false, + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0), + selectionCustomIcon: AppAssets.arrow_down, + leadingIcon: AppAssets.globe, + ).withVerticalPadding(8); + }, + ) : TextInputWidget( labelText: LocaleKeys.nationality.tr(), - hintText: appState!.isArabic() - ? (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") - : (countriesList.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), + hintText: appState.isArabic() + ? (authVM!.countriesList!.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "") + : (authVM!.countriesList!.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""), isEnable: true, prefix: null, isAllowRadius: false, @@ -208,7 +224,7 @@ class _RegisterNew extends State { TextInputWidget( labelText: LocaleKeys.mobileNumber.tr(), hintText: ("widget.payload.mobileNo" ?? ""), - controller: null, + controller: authVM!.phoneNumberController, isEnable: true, prefix: null, isAllowRadius: false, @@ -223,20 +239,17 @@ class _RegisterNew extends State { color: AppColors.greyColor, ), TextInputWidget( - labelText: LocaleKeys.dob.tr(), - hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""), - controller: null, - isEnable: true, - prefix: null, - isBorderAllowed: false, - isAllowLeadingIcon: true, - isReadOnly: true, - // : SelectionType.calendar, - // selectedValue: widget.payload.dob != null ? Utils.formatDateToDisplay(widget.payload.dob.toString()) : null, - // selectionCustomIcon: AppAssets.calendar, - leadingIcon: AppAssets.birthday_cake, - onChange: (value) {}) - .paddingSymmetrical(0.h, 16.h), + labelText: LocaleKeys.dob.tr(), + hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""), + controller: authVM!.dobController, + isEnable: true, + prefix: null, + isBorderAllowed: false, + isAllowLeadingIcon: true, + isReadOnly: true, + leadingIcon: AppAssets.birthday_cake, + selectionType: SelectionTypeEnum.calendar, + ).paddingSymmetrical(0.h, 16.h), ], ), ), @@ -246,12 +259,12 @@ class _RegisterNew extends State { children: [ Expanded( child: CustomButton( - text: LocaleKeys.cancel, + text: LocaleKeys.cancel.tr(), icon: AppAssets.cancel, onPressed: () { Navigator.of(context).pop(); + authVM!.clearDefaults(); }, - // fontFamily: context.fontFamily, backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index ca19ee6..0f520c6 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart'; +import '../core/dependencies.dart'; + // TODO: Import AppColors if bgRedColor is defined there // import 'package:hmg_patient_app_new/core/ui_utils/app_colors.dart'; @@ -29,6 +34,7 @@ class TextInputWidget extends StatelessWidget { final bool hasError; final String? errorMessage; Function(CountryEnum)? onCountryChange; + SelectionTypeEnum? selectionType; // final List countryList; // final Function(Country)? onCountryChange; @@ -54,6 +60,7 @@ class TextInputWidget extends StatelessWidget { this.hasError = false, this.errorMessage, this.onCountryChange, + this.selectionType, // this.countryList = const [], // this.onCountryChange, }) : super(key: key); @@ -97,6 +104,7 @@ class TextInputWidget extends StatelessWidget { ], ), ), + if (selectionType == SelectionTypeEnum.calendar) _buildTrailingIcon(context), ], ), ), @@ -128,6 +136,38 @@ class TextInputWidget extends StatelessWidget { child: Utils.buildSvgWithAssets(icon: leadingIcon!)); } + Widget _buildTrailingIcon(BuildContext context) { + final AppState appState = getIt.get(); + return Container( + height: 40.h, + width: 40.h, + margin: EdgeInsets.zero, + padding: EdgeInsets.all(8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.whiteColor), + child: GestureDetector( + onTap: () async { + bool isGregorian = true; + final picked = await showHijriGregBottomSheet(context, + switcherIcon: Utils.buildSvgWithAssets(icon: AppAssets.language, width: 24.h, height: 24.h), + language: appState.getLanguageCode()!, + initialDate: DateTime.now(), + okWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.confirm, width: 24.h, height: 24.h)), + cancelWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)), + onCalendarTypeChanged: (bool value) { + isGregorian = value; + }); + if (picked != null && onChange != null) { + // if (onCalendarTypeChanged != null) { + // onCalendarTypeChanged.call(isGregorian); + // } + onChange!(picked.toIso8601String()); + } + }, + child: Utils.buildSvgWithAssets(icon: AppAssets.calendar), + ), + ); + } + Widget _buildLabelText() { return Text( labelText, @@ -154,23 +194,11 @@ class TextInputWidget extends StatelessWidget { onChanged: onChange, focusNode: focusNode, autofocus: autoFocus, - style: TextStyle( - fontSize: 14.fSize, - height: 21 / 14, - fontWeight: FontWeight.w500, - color: AppColors.textColor, - letterSpacing: -0.2, - ), + style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2), decoration: InputDecoration( isDense: true, hintText: hintText, - hintStyle: TextStyle( - fontSize: 14.fSize, - height: 21 / 16, - fontWeight: FontWeight.w500, - color: Color(0xff898A8D), - letterSpacing: -0.2, - ), + hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2), prefixIconConstraints: BoxConstraints(minWidth: 45.h), prefixIcon: prefix == null ? null diff --git a/pubspec.lock b/pubspec.lock index 5665d11..e2ebb3b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -742,6 +742,14 @@ packages: url: "https://pub.dev" source: hosted version: "13.1.3" + hijri_gregorian_calendar: + dependency: "direct main" + description: + name: hijri_gregorian_calendar + sha256: "9d23b52192783c1ad134b1ac001be7977342cb579c6b380647b6494fbd464d29" + url: "https://pub.dev" + source: hosted + version: "0.0.4" html: dependency: transitive description: @@ -1636,5 +1644,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.8.1 <4.0.0" flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0ec8324..cfb94d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,7 @@ dependencies: web: any flutter_staggered_animations: ^1.1.1 smooth_corner: ^1.1.1 + hijri_gregorian_calendar: ^0.0.4 dev_dependencies: flutter_test: From 2f9fbaef7ab183d70ab490f8e995c39d2fbd1617 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Thu, 4 Sep 2025 16:25:49 +0300 Subject: [PATCH 15/16] insurance update implementation contd. --- assets/images/svg/close_bottom_sheet_icon.svg | 4 ++ lib/core/app_assets.dart | 2 +- .../insurance/insurance_view_model.dart | 7 +++ .../insurance/insurance_home_page.dart | 13 ++-- .../insurance/widgets/insurance_history.dart | 39 ++++++++++++ lib/widgets/common_bottom_sheet.dart | 59 ++++++++++--------- 6 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 assets/images/svg/close_bottom_sheet_icon.svg create mode 100644 lib/presentation/insurance/widgets/insurance_history.dart diff --git a/assets/images/svg/close_bottom_sheet_icon.svg b/assets/images/svg/close_bottom_sheet_icon.svg new file mode 100644 index 0000000..599ce85 --- /dev/null +++ b/assets/images/svg/close_bottom_sheet_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 2077440..7f1b6d1 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -71,9 +71,9 @@ class AppAssets { static const String insurance_history_icon = '$svgBasePath/insurance_history_icon.svg'; static const String cancel_circle_icon = '$svgBasePath/cancel_circle.svg'; static const String update_insurance_card_icon = '$svgBasePath/update_insurance_card.svg'; + static const String close_bottom_sheet_icon = '$svgBasePath/close_bottom_sheet_icon.svg'; //bottom navigation// - static const String homeBottom = '$svgBasePath/home_bottom.svg'; static const String bookAppoBottom = '$svgBasePath/book_appo_bottom.svg'; static const String myFilesBottom = '$svgBasePath/my_files_bottom.svg'; diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart index 545a32a..fac09da 100644 --- a/lib/features/insurance/insurance_view_model.dart +++ b/lib/features/insurance/insurance_view_model.dart @@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/services/error_handler_service.dart'; class InsuranceViewModel extends ChangeNotifier { bool isInsuranceLoading = false; + bool isInsuranceHistoryLoading = false; InsuranceRepo insuranceRepo; ErrorHandlerService errorHandlerService; @@ -17,10 +18,16 @@ class InsuranceViewModel extends ChangeNotifier { initInsuranceProvider() { patientInsuranceList.clear(); isInsuranceLoading = true; + isInsuranceHistoryLoading = true; getPatientInsuranceDetails(); notifyListeners(); } + setIsInsuranceHistoryLoading(bool val) { + isInsuranceHistoryLoading = val; + notifyListeners(); + } + Future getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await insuranceRepo.getPatientInsuranceDetails(patientId: "1231755"); diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart index eea50c5..9f9271a 100644 --- a/lib/presentation/insurance/insurance_home_page.dart +++ b/lib/presentation/insurance/insurance_home_page.dart @@ -17,6 +17,8 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; +import 'widgets/insurance_history.dart'; + class InsuranceHomePage extends StatefulWidget { const InsuranceHomePage({super.key}); @@ -59,14 +61,9 @@ class _InsuranceHomePageState extends State { iconSize: 21.h, text: LocaleKeys.history.tr(context: context), onPressed: () { - showCommonBottomSheet( - context, - child: Container(), - callBackFunc: () {}, - title: "", - height: ResponsiveExtension.screenHeight * 0.5, - isCloseButtonVisible: false, - ); + insuranceVM.setIsInsuranceHistoryLoading(true); + showCommonBottomSheet(context, + child: InsuranceHistory(), callBackFunc: () {}, title: "", height: ResponsiveExtension.screenHeight * 0.5, isCloseButtonVisible: false, isFullScreen: false); }, backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), borderColor: AppColors.primaryRedColor.withOpacity(0.0), diff --git a/lib/presentation/insurance/widgets/insurance_history.dart b/lib/presentation/insurance/widgets/insurance_history.dart new file mode 100644 index 0000000..0708c7c --- /dev/null +++ b/lib/presentation/insurance/widgets/insurance_history.dart @@ -0,0 +1,39 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class InsuranceHistory extends StatelessWidget { + InsuranceHistory({super.key}); + + late InsuranceViewModel insuranceViewModel; + + @override + Widget build(BuildContext context) { + insuranceViewModel = Provider.of(context); + return Consumer(builder: (context, insuranceVM, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.history.tr(context: context).toText24(isBold: true), + Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon).onPress(() { + Navigator.of(context).pop(); + }), + ], + ).paddingSymmetrical(24.h, 24.h), + insuranceVM.isInsuranceHistoryLoading ? const MoviesShimmerWidget().paddingSymmetrical(24.h, 24.h) : Container() + ], + ); + }); + } +} diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index f3d7425..f6039eb 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -2,14 +2,19 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; -import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; -void showCommonBottomSheet(BuildContext context, {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true}) { +void showCommonBottomSheet(BuildContext context, + {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true, bool isFullScreen = true}) { showModalBottomSheet( + sheetAnimationStyle: AnimationStyle( + duration: Duration(milliseconds: 500), // Custom animation duration + reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration + ), context: context, - isScrollControlled: false, + isScrollControlled: true, showDragHandle: false, backgroundColor: AppColors.scaffoldBgColor, builder: (BuildContext context) { @@ -19,6 +24,7 @@ void showCommonBottomSheet(BuildContext context, {required Widget child, require child: ButtonSheetContent( title: title!, isCloseButtonVisible: isCloseButtonVisible, + isFullScreen: isFullScreen, child: child, ), ); @@ -31,8 +37,9 @@ class ButtonSheetContent extends StatelessWidget { final Widget child; final String title; final bool isCloseButtonVisible; + final bool isFullScreen; - const ButtonSheetContent({super.key, required this.child, required this.isCloseButtonVisible, required this.title}); + const ButtonSheetContent({super.key, required this.child, required this.isCloseButtonVisible, required this.title, required this.isFullScreen}); @override Widget build(BuildContext context) { @@ -42,20 +49,20 @@ class ButtonSheetContent extends StatelessWidget { // SizedBox( // height: 20.h, // ), - Center( - child: Container( - margin: const EdgeInsets.only(top: 18, bottom: 12), - height: 4, - width: 40.h, - decoration: BoxDecoration( - color: Colors.grey[400], - borderRadius: BorderRadius.circular(2), - ), - ), - ), + // Center( + // child: Container( + // margin: const EdgeInsets.only(top: 18, bottom: 12), + // height: 4, + // width: 40.h, + // decoration: BoxDecoration( + // color: Colors.grey[400], + // borderRadius: BorderRadius.circular(2), + // ), + // ), + // ), // Close button - isCloseButtonVisible + isCloseButtonVisible && isFullScreen ? Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Utils.buildSvgWithAssets(icon: AppAssets.closeBottomNav, width: 32, height: 32).onPress(() { @@ -64,17 +71,15 @@ class ButtonSheetContent extends StatelessWidget { ) : SizedBox(), - SizedBox( - height: 20, - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: Text( - title, - style: TextStyle(fontSize: 27.h, fontWeight: FontWeight.bold), - ), - ), - const SizedBox(height: 16), + isFullScreen + ? Column( + children: [ + SizedBox(height: 20.h), + Padding(padding: EdgeInsets.symmetric(horizontal: 16.h), child: title.toText24(isBold: true)), + SizedBox(height: 16.h), + ], + ) + : SizedBox(), Expanded(child: child) ], From c27d7301d97ef445dcc4eced493135f9dd8ece5f Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 4 Sep 2025 16:28:18 +0300 Subject: [PATCH 16/16] calender & signup fixes. --- lib/extensions/string_extensions.dart | 123 +++++++------------------- 1 file changed, 31 insertions(+), 92 deletions(-) diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 8ecdc3a..352415b 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -13,16 +13,13 @@ extension CapExtension on String { String get allInCaps => this.toUpperCase(); - String get capitalizeFirstofEach => - this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : ""; + String get capitalizeFirstofEach => this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : ""; } extension EmailValidator on String { Widget get toWidget => Text(this); - Widget toText8( - {Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => - Text( + Widget toText8({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text( this, maxLines: maxlines, overflow: textOverflow, @@ -35,14 +32,7 @@ extension EmailValidator on String { ), ); - Widget toText10( - {Color? color, - bool isBold = false, - bool isUnderLine = false, - int? maxlines, - FontStyle? fontStyle, - TextOverflow? textOverflow}) => - Text( + Widget toText10({Color? color, bool isBold = false, bool isUnderLine = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text( this, maxLines: maxlines, overflow: textOverflow, @@ -56,15 +46,7 @@ extension EmailValidator on String { decorationColor: color ?? AppColors.blackColor), ); - Widget toText11( - {Color? color, - FontWeight? weight, - bool isUnderLine = false, - bool isCenter = false, - bool isBold = false, - int maxLine = 0, - double letterSpacing = 0.64}) => - Text( + Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0.64}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, @@ -78,20 +60,11 @@ extension EmailValidator on String { ), ); - Widget toText12( - {Color? color, - bool isUnderLine = false, - bool isBold = false, - FontWeight? fontWeight, - bool isCenter = false, - double? height, - int maxLine = 0}) => - Text( + Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? fontWeight, bool isCenter = false, double? height, int maxLine = 0}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, style: TextStyle( - fontSize: 12.fSize, fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, @@ -102,9 +75,7 @@ extension EmailValidator on String { ), ); - Widget toText12Auto( - {Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => - AutoSizeText( + Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => AutoSizeText( this, textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, @@ -165,14 +136,7 @@ extension EmailValidator on String { decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText14( - {Color? color, - bool isUnderLine = false, - bool isBold = false, - bool isCenter = false, - FontWeight? weight, - int? maxlines}) => - Text( + Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: maxlines, @@ -184,14 +148,7 @@ extension EmailValidator on String { decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText15( - {Color? color, - bool isUnderLine = false, - bool isBold = false, - bool isCenter = false, - FontWeight? weight, - int? maxlines}) => - Text( + Widget toText15({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: maxlines, @@ -227,40 +184,24 @@ extension EmailValidator on String { Widget toText17({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle( - color: color ?? AppColors.blackColor, - fontSize: 17.fSize, - letterSpacing: -0.4, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); Widget toText18({Color? color, bool isBold = false, bool isCenter = false, int? maxlines}) => Text( maxLines: maxlines, textAlign: isCenter ? TextAlign.center : null, this, - style: TextStyle( - fontSize: 18.fSize, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal, - color: color ?? AppColors.blackColor, - letterSpacing: -0.4), + style: TextStyle(fontSize: 18.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4), ); Widget toText19({Color? color, bool isBold = false}) => Text( this, - style: TextStyle( - fontSize: 19.fSize, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal, - color: color ?? AppColors.blackColor, - letterSpacing: -0.4), + style: TextStyle(fontSize: 19.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4), ); Widget toText20({Color? color, bool isBold = false}) => Text( this, - style: TextStyle( - fontSize: 20.fSize, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal, - color: color ?? AppColors.blackColor, - letterSpacing: -0.4), + style: TextStyle(fontSize: 20.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4), ); Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( @@ -272,34 +213,34 @@ extension EmailValidator on String { Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle( - height: 1, - color: color ?? AppColors.blackColor, - fontSize: 22.fSize, - letterSpacing: -0.4, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); Widget toText24({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle( - height: 23 / 24, - color: color ?? AppColors.blackColor, - fontSize: 24.fSize, - letterSpacing: -0.4, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + ); + + Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text( + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); + + Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text( + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + ); + + + Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle( - height: 32 / 32, - color: color ?? AppColors.blackColor, - fontSize: 32.fSize, - letterSpacing: -0.4, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); Widget toText44({Color? color, bool isBold = false}) => Text( @@ -340,9 +281,7 @@ extension EmailValidator on String { } bool isValidEmail() { - return RegExp( - r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$') - .hasMatch(this); + return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this); } String toFormattedDate() {