diff --git a/lib/core/enums.dart b/lib/core/enums.dart index a034f3d..95efb6c 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -37,3 +37,5 @@ enum SelectionType { dropdown, calendar } enum GenderType { male, female } enum MaritalStatusType { single, married, divorced, widowed } + +enum ChipType { success, error, alert, info, warning } diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index afec9cf..8b8c796 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -1,3 +1,4 @@ +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -189,3 +190,37 @@ Widget widthSpacer3per() => SizedBox(height: 3.w); Widget widthSpacer4per() => SizedBox(height: 4.w); Widget widthSpacer5per() => SizedBox(height: 5.w); + + + +extension ChipTypeExtension on ChipType { + Color get color { + switch (this) { + case ChipType.success: + return AppColors.successColor; // Replace with your actual color + case ChipType.error: + return AppColors.errorColor; // Replace with your actual color + case ChipType.alert: + return AppColors.alertColor; // Replace with your actual color + case ChipType.info: + return AppColors.infoColor; // Replace with your actual color + case ChipType.warning: + return AppColors.warningColor; // Replace with your actual color + } + } + + Color get backgroundColor { + switch (this) { + case ChipType.success: + return AppColors.successLightColor; // Replace with your actual color + case ChipType.error: + return AppColors.errorLightColor; // Replace with your actual color + case ChipType.alert: + return AppColors.alertLightColor; // Replace with your actual color + case ChipType.info: + return AppColors.infoLightColor; // Replace with your actual color + case ChipType.warning: + return AppColors.warningLightColor; // Replace with your actual color + } + } +} \ No newline at end of file diff --git a/lib/presentation/authantication/login.dart b/lib/presentation/authantication/login.dart index 5473f0f..150d446 100644 --- a/lib/presentation/authantication/login.dart +++ b/lib/presentation/authantication/login.dart @@ -12,6 +12,7 @@ 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:hmg_patient_app_new/widgets/language_switcher.dart'; +import 'package:sizer/sizer.dart'; // Import sizer class LoginScreen extends StatefulWidget { @override @@ -31,59 +32,42 @@ class _LoginScreen extends State { @override Widget build(BuildContext context) { - 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: ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith(overscroll: false, physics: const ClampingScrollPhysics()), - child: NotificationListener( - onNotification: (notification) { - notification.disallowIndicator(); - return true; - }, - child: SingleChildScrollView( - physics: ClampingScrollPhysics(), // Remove NeverScrollableScrollPhysics() - padding: EdgeInsets.only( - left: 24, - right: 24, - ), + 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: MediaQuery.of(context).size.width * 0.45, - height: MediaQuery.of(context).size.height * 0.22, - repeat: true, - fit: BoxFit.cover), - Flexible( - fit: FlexFit.loose, - child: Container(height: MediaQuery.of(context).size.height * 0.18), - ), + 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: 28, + fontSize: 22.sp, fontWeight: FontWeight.w600, - color: Color(0xff2B353E), + color: AppColors.textColor, letterSpacing: -0.4, height: 40 / 28, ), ), - SizedBox(height: 32), + SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px) TextInputWidget( labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}", hintText: "xxxxxxxxx", @@ -94,25 +78,27 @@ class _LoginScreen extends State { autoFocus: true, isBorderAllowed: false, isAllowLeadingIcon: true, - padding: EdgeInsets.only(top: 8, bottom: 8, left: 8, right: 8), + 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: 16), + SizedBox(height: 2.h), // Adjusted to sizer unit (approx 16px) CustomButton( text: LocaleKeys.login.tr(), icon: AppAssets.login1, iconColor: Colors.white, onPressed: () {}, ), - SizedBox(height: 14), + 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: 16, - height: 26 / 16, + fontSize: 14.sp, // Adjusted to sizer unit + height: 26 / 16, // This height is a ratio, may need re-evaluation fontWeight: FontWeight.w500, ), children: [ @@ -121,23 +107,23 @@ class _LoginScreen extends State { TextSpan( text: LocaleKeys.registernow.tr(), style: context.dynamicTextStyle( - color: AppColors.bgRedColor, - fontSize: 16, - height: 26 / 16, + color: AppColors.primaryRedColor, + fontSize: 14.sp, // Adjusted to sizer unit + height: 26 / 16, // Ratio fontWeight: FontWeight.w500, ), recognizer: TapGestureRecognizer()..onTap = () {}, ), ], ), - ).withVerticalPadding(16), + ).withVerticalPadding(2.h), // Adjusted to sizer unit ), ], ), ), ), ), - ), - ); + ); + }); } } diff --git a/lib/splashPage.dart b/lib/splashPage.dart index cec91d3..72a9213 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -74,7 +74,7 @@ class _SplashScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Color(0xffF8F8F8), + backgroundColor: AppColors.whiteColor, body: Stack( alignment: Alignment.center, children: [ @@ -89,7 +89,7 @@ class _SplashScreenState extends State { children: [ Text( "Powered by", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.powerdBy, letterSpacing: -0.56, height: 16 / 14), + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: AppColors.textColor, letterSpacing: -0.56, height: 16 / 14), ), SizedBox( height: 5, diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index aa0a748..4987ffe 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -1,30 +1,46 @@ import 'package:flutter/material.dart'; class AppColors { -static const mainPurple = Color(0xFF7954F7); -static const purpleBg = Color(0xFFAEA4FC); -static const deepPurple = Color(0xFF7C65E7); -static const logoColor = Color(0xFF7C65E7); -static const buttonColor = Color(0xFF6A46F5); -static const splashBgColor = Color(0xFF3C355D); -static const whiteColor = Color(0xFFffffff); -static const blackColor = Color(0xFF000000); -static const lightGray = Color(0xFFF4F5F7); -static const lightPurple = Color(0xFFB7A3E6); -static const scaffoldBgColor = Color(0xFFF8F8F8); -static const lightGreyEFColor = Color(0xffeaeaff); -static const greyF7Color = Color(0xffF7F7F7); -static const lightGrayColor = Color(0xff808080); -static const buttonGrayColor = Color(0xffF1F1F1); -static const lightPurpleAlpha = Color(0x5AB7A3E6); + static const mainPurple = Color(0xFF7954F7); + static const purpleBg = Color(0xFFAEA4FC); + static const deepPurple = Color(0xFF7C65E7); + static const logoColor = Color(0xFF7C65E7); + 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); + static const lightGreyEFColor = Color(0xffeaeaff); + static const greyF7Color = Color(0xffF7F7F7); + static const lightGrayColor = Color(0xff808080); + static const buttonGrayColor = Color(0xffF1F1F1); + static const lightPurpleAlpha = Color(0x5AB7A3E6); // New UI Colors - static const Color bgRedColor = Color(0xFFED1C2B); - static const Color bgRedBorderColor = Color(0xFFED1C2B); + static const whiteColor = Color(0xFFffffff); + static const Color primaryRedColor = Color(0xFFED1C2B); + static const Color primaryRedBorderColor = Color(0xFFED1C2B); + static const Color secondaryLightRedColor = Color(0xFFFEE9EA); + static const Color secondaryLightRedBorderColor = Color(0xFFFEE9EA); static const Color bgRedLightColor = Color(0xFFFEE9EA); static const Color bgGreenColor = Color(0xFF18C273); - static const Color powerdBy = Color(0xFF333C45); -} - + static const Color textColor = Color(0xFF2E3039); + static const Color borderOnlyColor = Color(0xFF2E3039); + //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 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/appbar/app_bar_widget.dart b/lib/widgets/appbar/app_bar_widget.dart index af26653..dde02ed 100644 --- a/lib/widgets/appbar/app_bar_widget.dart +++ b/lib/widgets/appbar/app_bar_widget.dart @@ -28,12 +28,17 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // Arrow Back with click handler - GestureDetector( - onTap: onBackPressed, - child: Utils.buildSvgWithAssets( - icon: AppAssets.arrow_back, - width: 32, - height: 32, + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: GestureDetector( + onTap: onBackPressed, + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_back, + width: 32, + height: 32, + ), + ), ), ), @@ -43,14 +48,19 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ), // Language Selector - LanguageSelector( - currentLanguage: context.locale.languageCode, - showOnlyIcon: false, - onLanguageChanged: onLanguageChanged, - languages: [ - {'code': 'ar', 'name': LocaleKeys.arabic.tr()}, - {'code': 'en', 'name': LocaleKeys.english.tr()} - ], + 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/chip/custom_chip_widget.dart b/lib/widgets/chip/custom_chip_widget.dart new file mode 100644 index 0000000..9afb986 --- /dev/null +++ b/lib/widgets/chip/custom_chip_widget.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.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/extensions/widget_extensions.dart'; + +class CustomChipWidget extends StatelessWidget { + final ChipType chipType; + final String chipText; + final String? iconAsset; + final VoidCallback? onTap; + final bool isSelected; + final double borderRadius; + final EdgeInsetsGeometry padding; + + const CustomChipWidget({ + Key? key, + required this.chipType, + required this.chipText, + this.iconAsset, + this.onTap, + this.isSelected = false, + this.borderRadius = 12, + this.padding = const EdgeInsets.all(8), + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final hasIcon = iconAsset != null; + final hasOnTap = onTap != null || hasIcon; + + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(borderRadius), + color: isSelected ? chipType.color : chipType.backgroundColor, + border: Border.all( + color: chipType.color, + width: isSelected ? 0 : 1, + ), + ), + child: InkWell( + onTap: hasOnTap ? onTap : null, + borderRadius: BorderRadius.circular(borderRadius), + child: Container( + padding: padding, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(borderRadius), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (iconAsset != null) ...[ + Utils.buildSvgWithAssets(icon: iconAsset!), + const SizedBox(width: 6), + ], + Text( + chipText.toUpperCase(), + style: context.dynamicTextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: isSelected ? Colors.white : chipType.color, + letterSpacing: 0.1, + isLanguageSwitcher: true, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index d902314..f2446a7 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -2,6 +2,9 @@ import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +// TODO: Import AppColors if bgRedColor is defined there +// import 'package:hmg_patient_app_new/core/ui_utils/app_colors.dart'; + class TextInputWidget extends StatelessWidget { final String labelText; final String hintText; @@ -19,6 +22,8 @@ class TextInputWidget extends StatelessWidget { final bool isAllowLeadingIcon; final String? leadingIcon; final bool isCountryDropDown; + final bool hasError; + final String? errorMessage; // final List countryList; // final Function(Country)? onCountryChange; @@ -41,36 +46,58 @@ class TextInputWidget extends StatelessWidget { this.isAllowLeadingIcon = false, this.leadingIcon, this.isCountryDropDown = false, + this.hasError = false, + this.errorMessage, // this.countryList = const [], // this.onCountryChange, }) : super(key: key); @override Widget build(BuildContext context) { - return Container( - padding: padding, - alignment: Alignment.center, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: Colors.white, - borderRadius: isAllowRadius ? 15 : null, - side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null, - ), - 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), - ], + // Assuming AppColors.bgRedColor exists, otherwise using Colors.red + final errorColor = Colors.red; // Replace with AppColors.bgRedColor if available + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: padding, + alignment: Alignment.center, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: Colors.white, + borderRadius: isAllowRadius ? 15 : null, + side: isBorderAllowed ? BorderSide(color: hasError ? errorColor : const Color(0xffefefef), width: 1) : null, + ), + 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 (hasError && errorMessage != null) + Padding( + padding: const EdgeInsets.only(top: 4.0, left: 12.0), // Adjust padding as needed + child: Text( + errorMessage!, + style: TextStyle( + color: errorColor, + fontSize: 12, + ), ), ), - ], - ), + ], ); } diff --git a/lib/widgets/language_switcher.dart b/lib/widgets/language_switcher.dart index 38f33d3..4c621b7 100644 --- a/lib/widgets/language_switcher.dart +++ b/lib/widgets/language_switcher.dart @@ -43,19 +43,16 @@ class _LanguageSelectorState extends State { Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), - color: Color(0xFFFEE9EA), + color: AppColors.secondaryLightRedColor, ), child: InkWell( onTap: () { final newLanguage = widget.currentLanguage == 'ar' ? 'en' : 'ar'; - print(newLanguage); widget.onLanguageChanged(newLanguage); }, child: Container( padding: EdgeInsets.all(8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - ), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)), child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -66,7 +63,7 @@ class _LanguageSelectorState extends State { style: context.dynamicTextStyle( fontWeight: FontWeight.w500, fontSize: 14, - color: AppColors.bgRedColor, + color: AppColors.primaryRedColor, letterSpacing: 0.1, isLanguageSwitcher: true, ),