bottom sheet

pull/8/head
aamir-csol 2 months ago
parent 4d76b34608
commit ea15bd9152

1
.gitignore vendored

@ -43,3 +43,4 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
/android/

@ -776,5 +776,12 @@
"validPassportNumber": "يرجى إدخال رقم جواز سفر صالح", "validPassportNumber": "يرجى إدخال رقم جواز سفر صالح",
"continuePlan": "متابعة خطة العلاج؟", "continuePlan": "متابعة خطة العلاج؟",
"aboutApp": "حول التطبيق", "aboutApp": "حول التطبيق",
"dontHaveAccount": "ليس لديك حساب؟" "dontHaveAccount": "ليس لديك حساب؟",
"receiveOtpToast": "أين تود تلقي رمز التحقق OTP؟",
"enterPhoneNumber": "أدخل رقم الهاتف",
"enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي",
"enterPhoneDesc": "أدخل رقم هاتفك لتلقي رمز التحقق ",
"pleaseChooseOption": "الرجاء اختيار من الخيارات أدناه لتلقي رمز التحقق OTP"
} }

@ -772,5 +772,10 @@
"aboutApp": "About the app", "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", "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)...", "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"
} }

@ -198,31 +198,31 @@ extension EmailValidator on String {
Widget toText20({Color? color, bool isBold = false}) => Text( Widget toText20({Color? color, bool isBold = false}) => Text(
this, 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( Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
this, this,
maxLines: maxlines, 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( Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, 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( Widget toText24({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, 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( Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, 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( Widget toText44({Color? color, bool isBold = false}) => Text(

@ -775,5 +775,10 @@ abstract class LocaleKeys {
static const continuePlan = 'continuePlan'; static const continuePlan = 'continuePlan';
static const aboutApp = 'aboutApp'; static const aboutApp = 'aboutApp';
static const dontHaveAccount = 'dontHaveAccount'; static const dontHaveAccount = 'dontHaveAccount';
static const receiveOtpToast = 'receiveOtpToast';
static const enterPhoneNumber = 'enterPhoneNumber';
static const enterEmailDesc = 'enterEmailDesc';
static const enterPhoneDesc = 'enterPhoneDesc';
static const pleaseChooseOption = 'pleaseChooseOption';
} }

@ -3,16 +3,16 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.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 { class LoginScreen extends StatefulWidget {
@override @override
@ -32,98 +32,145 @@ class _LoginScreen extends State<LoginScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Sizer(// Wrap with Sizer return Scaffold(
builder: (context, orientation, deviceType) { appBar: CustomAppBar(
return Scaffold( onBackPressed: () {
appBar: CustomAppBar( // Navigator.of(context).pop();
onBackPressed: () { },
// Navigator.of(context).pop(); onLanguageChanged: (String value) {
}, print(value);
onLanguageChanged: (String value) { context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US'));
print(value); },
context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US')); ),
}, body: GestureDetector(
), onTap: () {
body: GestureDetector( FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside
onTap: () { },
FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside child: SingleChildScrollView(
}, child: Padding(
child: SingleChildScrollView( padding: EdgeInsets.only(left: 6.h, right: 6.h),
child: Padding( child: Column(
padding: EdgeInsets.only(left: 6.w, right: 6.w), mainAxisSize: MainAxisSize.min,
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, children: [
crossAxisAlignment: CrossAxisAlignment.start, Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.h, height: 22.h, repeat: true, fit: BoxFit.cover),
children: [ SizedBox(height: 19.h), // Adjusted to sizer unit
Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.w, height: 22.h, repeat: true, fit: BoxFit.cover), LocaleKeys.welcomeToDrSulaiman.tr().toText24(),
SizedBox(height: 19.h), // Adjusted to sizer unit SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px)
Text( TextInputWidget(
LocaleKeys.welcomeToDrSulaiman.tr(), labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}",
style: context.dynamicTextStyle( hintText: "xxxxxxxxx",
fontSize: 22.sp, controller: TextEditingController(),
fontWeight: FontWeight.w600, keyboardType: TextInputType.number,
color: AppColors.textColor, isEnable: true,
letterSpacing: -0.4, prefix: null,
height: 40 / 28, autoFocus: true,
), isBorderAllowed: false,
), isAllowLeadingIcon: true,
SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px) padding: EdgeInsets.symmetric(vertical: 1.h, horizontal: 2.h),
TextInputWidget( leadingIcon: AppAssets.student_card,
labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", errorMessage: "Please enter a valid national ID or file number",
hintText: "xxxxxxxxx", hasError: true,
controller: TextEditingController(), ),
keyboardType: TextInputType.number, SizedBox(height: 2.h), // Adjusted to sizer unit (approx 16px)
isEnable: true, CustomButton(
prefix: null, text: LocaleKeys.login.tr(),
autoFocus: true, icon: AppAssets.login1,
isBorderAllowed: false, iconColor: Colors.white,
isAllowLeadingIcon: true, onPressed: () {
padding: EdgeInsets.symmetric(vertical: 1.h, horizontal: 2.w), showModalBottomSheet(
leadingIcon: AppAssets.student_card, context: context,
errorMessage: "Please enter a valid national ID or file number", isScrollControlled: true,
hasError: true, isDismissible: false,
), useSafeArea: true,
SizedBox(height: 2.h), // Adjusted to sizer unit (approx 16px) backgroundColor: Colors.transparent,
CustomButton( enableDrag: false,
text: LocaleKeys.login.tr(), builder: (bottomSheetContext) => StatefulBuilder(
icon: AppAssets.login1, builder: (BuildContext context, StateSetter setModalState) {
iconColor: Colors.white, return Padding(
onPressed: () {}, padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
), child: SingleChildScrollView(
SizedBox(height: 1.8.h), // Adjusted to sizer unit (approx 14px) child: GenericBottomSheet(
Center( countryCode: "selectedCountry.countryCode",
child: RichText( initialPhoneNumber: "",
textAlign: TextAlign.center, textController: TextEditingController(),
text: TextSpan( isEnableCountryDropdown: true,
style: context.dynamicTextStyle( onCountryChange: (value) {},
color: Colors.black, onChange: (String? value) {},
fontSize: 14.sp, // Adjusted to sizer unit buttons: [
height: 26 / 16, // This height is a ratio, may need re-evaluation Padding(
fontWeight: FontWeight.w500, padding: const EdgeInsets.only(bottom: 10),
), child: CustomButton(
children: <TextSpan>[ text: "TranslationBase.of(context).sendOTPSMS",
TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()), onPressed: () {},
TextSpan(text: " "), backgroundColor: AppColors.primaryRedColor,
TextSpan( borderColor: AppColors.primaryRedBorderColor,
text: LocaleKeys.registernow.tr(), textColor: AppColors.whiteColor,
style: context.dynamicTextStyle( icon: AppAssets.message,
color: AppColors.primaryRedColor, ),
fontSize: 14.sp, // Adjusted to sizer unit ),
height: 26 / 16, // Ratio Row(
fontWeight: FontWeight.w500, crossAxisAlignment: CrossAxisAlignment.center,
), mainAxisAlignment: MainAxisAlignment.center,
recognizer: TapGestureRecognizer()..onTap = () {}, 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>[
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
),
],
), ),
), ),
), ),
); ),
}); );
} }
} }

@ -8,7 +8,6 @@ class AppColors {
static const buttonColor = Color(0xFF6A46F5); static const buttonColor = Color(0xFF6A46F5);
static const splashBgColor = Color(0xFF3C355D); static const splashBgColor = Color(0xFF3C355D);
static const blackColor = Color(0xFF000000);
static const lightGray = Color(0xFFF4F5F7); static const lightGray = Color(0xFFF4F5F7);
static const lightPurple = Color(0xFFB7A3E6); static const lightPurple = Color(0xFFB7A3E6);
static const scaffoldBgColor = Color(0xFFF8F8F8); static const scaffoldBgColor = Color(0xFFF8F8F8);
@ -28,19 +27,20 @@ class AppColors {
static const Color bgGreenColor = Color(0xFF18C273); static const Color bgGreenColor = Color(0xFF18C273);
static const Color textColor = Color(0xFF2E3039); static const Color textColor = Color(0xFF2E3039);
static const Color borderOnlyColor = Color(0xFF2E3039); static const Color borderOnlyColor = Color(0xFF2E3039);
static const blackColor = textColor;
//Chips //Chips
static const Color successColor = Color(0xff18C273); static const Color successColor = Color(0xff18C273);
static const Color errorColor = Color(0xFFED1C2B); static const Color errorColor = Color(0xFFED1C2B);
static const Color alertColor = Color(0xFFD48D05); static const Color alertColor = Color(0xFFD48D05);
static const Color infoColor = Color(0xFF0B85F7); static const Color infoColor = Color(0xFF0B85F7);
static const Color warningColor = Color(0xFFFFCC00); static const Color warningColor = Color(0xFFFFCC00);
static const Color greyColor = Color(0xFFEFEFF0); static const Color greyColor = Color(0xFFEFEFF0);
static const Color successLightColor = Color(0xFF18C27326); static const Color successLightColor = Color(0xFF18C27326);
static const Color errorLightColor = Color(0xFFED1C2B1A); static const Color errorLightColor = Color(0xFFED1C2B1A);
static const Color alertLightColor = Color(0xFFD48D0526); static const Color alertLightColor = Color(0xFFD48D0526);
static const Color infoLightColor = Color(0xFF0B85F726); static const Color infoLightColor = Color(0xFF0B85F726);
static const Color warningLightColor = Color(0xFFFFCC0026); static const Color warningLightColor = Color(0xFFFFCC0026);
static const Color greyLightColor = Color(0xFFEFEFF026); static const Color greyLightColor = Color(0xFFEFEFF026);
} }

@ -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<Widget> 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<GenericBottomSheet> {
@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,
],
),
),
),
),
);
}
}
Loading…
Cancel
Save