Merge pull request 'Design Fixes for Responsiveness (faiz_dev)' (#88) from faiz_dev into master

Reviewed-on: #88
master
Haroon6138 1 week ago
commit 622851a1a0

@ -1,16 +1,166 @@
# hmg_patient_app_new # HMG Patient App
New HMG Patient App A comprehensive Flutter-based mobile application for HMG (Hospital Management Group) patients,
providing seamless healthcare services and patient management features.
## Getting Started ## 📱 Features
This project is a starting point for a Flutter application. - **Patient Registration & Authentication**: Secure login and registration system
- **Appointment Management**: Book, reschedule, and manage medical appointments
- **Medical Records**: Access to personal health records and medical history
- **Doctor Consultation**: Video consultations and chat with healthcare providers
- **Health Monitoring**: Track vital signs and health metrics
- **Prescription Management**: View and manage prescriptions
- **Payment Integration**: Secure payment processing for medical services
- **Multi-language Support**: Available in English and Arabic
- **Push Notifications**: Real-time updates for appointments and health reminders
- **Calendar Integration**: Sync appointments with device calendar
- **Location Services**: Find nearby hospitals and clinics
A few resources to get you started if this is your first Flutter project: ## 🚀 Getting Started
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) ### Prerequisites
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the - Flutter SDK (>=3.0.0)
[online documentation](https://docs.flutter.dev/), which offers tutorials, - Dart SDK (>=3.0.0)
samples, guidance on mobile development, and a full API reference. - Android Studio / VS Code
- Android SDK (for Android development)
- Xcode (for iOS development, macOS only)
### Installation
1. **Clone the repository:**
```bash
git clone <repository-url>
cd HMG_Patient_App_New
```
2. **Install dependencies:**
```bash
flutter pub get
```
3. **Configure Firebase:**
- Add your `google-services.json` file to `android/app/`
- Add your `GoogleService-Info.plist` file to `ios/Runner/`
4. **Run the application:**
```bash
flutter run
```
## 🏗️ Project Structure
```
lib/
├── core/ # Core utilities and constants
├── extensions/ # Dart extensions
├── features/ # Feature-based modules
├── generated/ # Generated files (localization, etc.)
├── presentation/ # UI screens and widgets
├── routes/ # App navigation and routing
├── services/ # Business logic and API services
├── theme/ # App theming and styling
├── widgets/ # Reusable UI components
├── main.dart # App entry point
└── splashPage.dart # Splash screen
assets/
├── animations/ # Lottie animations
├── fonts/ # Custom fonts (Poppins, Gess Two)
├── images/ # PNG and SVG images
├── json/ # JSON data files
└── langs/ # Localization files
```
## 🛠️ Technologies Used
- **Framework**: Flutter
- **Language**: Dart
- **State Management**: [Your state management solution]
- **Backend Services**: Firebase
- **Authentication**: Firebase Auth
- **Database**: Cloud Firestore
- **Push Notifications**: Firebase Cloud Messaging
- **Maps**: Google Maps
- **Payment**: Amazon PayFort
- **Video Calling**: Flutter Zoom Video SDK
- **Local Storage**: SQLite, Shared Preferences
## 📱 Supported Platforms
- ✅ Android (API level 21+)
- ✅ iOS (iOS 12.0+)
- ✅ Foldable devices support
- ✅ Tablet optimization
## 🌐 Localization
The app supports multiple languages:
- English (en-US)
- Arabic (ar-SA)
## 🔧 Configuration
### Environment Setup
1. **API Keys**: Configure your API keys in the appropriate configuration files
2. **Firebase**: Set up Firebase project and add configuration files
3. **Maps**: Add Google Maps API key
4. **Payment**: Configure PayFort credentials
### Build Variants
- **Debug**: Development build with debugging enabled
- **Release**: Production-ready optimized build
## 📝 Development Guidelines
### Code Style
- Follow Dart/Flutter best practices
- Use consistent naming conventions
- Implement proper error handling
- Write comprehensive documentation
### Testing
```bash
# Run unit tests
flutter test
# Run integration tests
flutter test integration_test/
```
### Building for Production
**Android:**
```bash
flutter build apk --release
# or
flutter build appbundle --release
```
**iOS:**
```bash
flutter build ios --release
```
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/new-feature`)
3. Commit your changes (`git commit -am 'Add new feature'`)
4. Push to the branch (`git push origin feature/new-feature`)
5. Create a Pull Request
## 📄 License
This project is proprietary software developed for HMG Healthcare Group.
**Built with ❤️ for better healthcare accessibility**

@ -11,22 +11,109 @@ extension ResponsiveExtension on num {
double get _screenHeight => SizeUtils.height; double get _screenHeight => SizeUtils.height;
/// Scale horizontally (width-based) /// Check if device is likely a foldable
double get w => (this * _screenWidth) / figmaDesignWidth; bool get _isFoldable {
double aspectRatio = _screenWidth / _screenHeight;
// Foldable devices typically have aspect ratios close to 1:1 when unfolded
return (aspectRatio > 0.9 && aspectRatio < 1.1) && (_screenWidth > 700 || _screenHeight > 700);
}
/// Scale text size - enhanced for foldable devices
double get f {
double aspectRatio = _screenWidth / _screenHeight;
double scale = (_screenWidth < _screenHeight ? _screenWidth : _screenHeight) / figmaDesignWidth;
// Enhanced clamping for different device types
double clamp;
if (SizeUtils.deviceType == DeviceType.tablet || _isFoldable) {
// More conservative scaling for tablets and foldables
clamp = (aspectRatio > 1.5 || aspectRatio < 0.67) ? 1.4 : 1.1;
} else {
// Original logic for phones
clamp = (aspectRatio > 1.3 || aspectRatio < 0.77) ? 1.6 : 1.2;
}
/// Scale vertically (height-based) if (scale > clamp) scale = clamp;
double get h => (this * _screenHeight) / figmaDesignHeight; return this * scale;
}
//radius /// Scale horizontally (width-based) - enhanced for foldable devices
double get r => (this * _screenWidth) / figmaDesignWidth; double get w {
double baseScale = (this * _screenWidth) / figmaDesignWidth;
/// Scale text size if (_isFoldable) {
double get f { // For foldables, use more conservative width scaling
double scale = _screenWidth / figmaDesignWidth; double scale = _screenWidth / figmaDesignWidth;
if (scale > 1.6) scale = 1.6; // optional clamp for tablets scale = scale.clamp(0.8, 1.4);
return this * scale; return this * scale;
} }
return baseScale;
}
/// Scale vertically (height-based) - enhanced for foldable devices
double get h {
double baseScale = (this * _screenHeight) / figmaDesignHeight;
if (_isFoldable) {
// For foldables, use height-based scaling but with constraints
double scale = (_screenHeight / figmaDesignHeight).clamp(0.8, 1.4);
return this * scale;
}
return baseScale;
}
/// Radius - enhanced for foldable devices
double get r {
double baseScale = (this * _screenWidth) / figmaDesignWidth;
if (_isFoldable) {
// Use the same logic as enhanced width for foldables
double scale = _screenWidth / figmaDesignWidth;
scale = scale.clamp(0.8, 1.4);
return this * scale;
}
return baseScale;
}
// New enhanced getters (additional options)
/// Enhanced font scaling with device-specific adjustments
double get fh {
double baseScale = _screenHeight / figmaDesignHeight;
if (_isFoldable) {
// Special handling for foldable devices - use more conservative scaling
baseScale = baseScale.clamp(0.8, 1.3);
} else if (SizeUtils.deviceType == DeviceType.tablet) {
// Tablet-specific scaling
baseScale = baseScale.clamp(0.9, 1.5);
} else {
// Phone scaling
baseScale = baseScale.clamp(0.8, 1.8);
}
return this * baseScale;
}
/// Adaptive scaling - automatically chooses best scaling method
double get adaptive {
if (_isFoldable) {
return fh;
} else if (SizeUtils.deviceType == DeviceType.tablet) {
return f * 0.9; // Slightly smaller for tablets
}
return f;
}
/// Minimum size constraint (useful for touch targets)
double get minSize {
double scaled = adaptive;
return scaled < 44 ? 44 : scaled; // Minimum 44pt for accessibility
}
/// Optional: direct accessors for full width/height /// Optional: direct accessors for full width/height
static double get screenWidth => SizeUtils.width; static double get screenWidth => SizeUtils.width;
@ -91,10 +178,7 @@ class SizeUtils {
/// Device's Width /// Device's Width
static double width = 375; static double width = 375;
static void setScreenSize( static void setScreenSize(BoxConstraints constraints, Orientation currentOrientation) {
BoxConstraints constraints,
Orientation currentOrientation,
) {
boxConstraints = constraints; boxConstraints = constraints;
orientation = currentOrientation; orientation = currentOrientation;
@ -130,6 +214,7 @@ class SizeUtils {
} }
log("longerSide: $longerSide"); log("longerSide: $longerSide");
log("shorterSide: $shorterSide");
log("isTablet: $isTablet"); log("isTablet: $isTablet");
} }
} }
@ -140,3 +225,9 @@ bool get isTablet => SizeUtils.deviceType == DeviceType.tablet;
bool get isMobile => SizeUtils.deviceType == DeviceType.mobile; bool get isMobile => SizeUtils.deviceType == DeviceType.mobile;
bool get isDesktop => SizeUtils.deviceType == DeviceType.desktop; bool get isDesktop => SizeUtils.deviceType == DeviceType.desktop;
bool get isFoldable {
double aspectRatio = SizeUtils.width / SizeUtils.height;
// Foldable devices typically have aspect ratios close to 1:1 when unfolded
return (aspectRatio > 0.9 && aspectRatio < 1.1) && (SizeUtils.width > 700 || SizeUtils.height > 700);
}

@ -633,17 +633,19 @@ class Utils {
required String url, required String url,
required Color iconColor, required Color iconColor,
bool isDisabled = false, bool isDisabled = false,
double width = 24, double? width,
double height = 24, double? height,
}) { }) {
final iconH = height ?? 24.h;
final iconW = width ?? 24.w;
return SvgPicture.network( return SvgPicture.network(
url, url,
colorFilter: ColorFilter.mode( colorFilter: ColorFilter.mode(
isDisabled ? iconColor.withOpacity(0.5) : iconColor, isDisabled ? iconColor.withOpacity(0.5) : iconColor,
BlendMode.srcIn, BlendMode.srcIn,
), ),
width: width, width: iconW,
height: height, height: iconH,
); );
} }
@ -662,7 +664,7 @@ class Utils {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null,
borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 0) : null, borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 12.r) : null,
), ),
child: Image.asset(icon, width: iconW, height: iconH, fit: fit), child: Image.asset(icon, width: iconW, height: iconH, fit: fit),
); );
@ -670,16 +672,26 @@ class Utils {
} }
/// Widget to build an SVG from network /// Widget to build an SVG from network
static Widget buildImgWithNetwork({required String url, required Color iconColor, bool isDisabled = false, double width = 24, double height = 24, BoxFit fit = BoxFit.cover, ImageErrorWidgetBuilder? errorBuilder}) { static Widget buildImgWithNetwork({
required String url,
required Color iconColor,
bool isDisabled = false,
double? width,
double? height,
BoxFit fit = BoxFit.cover,
ImageErrorWidgetBuilder? errorBuilder,
}) {
final iconH = height ?? 24.h;
final iconW = width ?? 24.w;
return Image.network( return Image.network(
url, url,
width: width, width: iconW,
height: height, height: iconH,
fit: fit, fit: fit,
errorBuilder: errorBuilder??(_,__,___){ errorBuilder: errorBuilder ??
(_, __, ___) {
//todo change the error builder icon that it is returning //todo change the error builder icon that it is returning
return Utils.buildSvgWithAssets(width: width, return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon);
height: height,icon: AppAssets.no_visit_icon);
}, },
); );
} }

@ -167,8 +167,8 @@ extension EmailValidator on String {
decoration: isUnderLine ? TextDecoration.underline : null), decoration: isUnderLine ? TextDecoration.underline : null),
); );
Widget toText14( Widget toText14({
{Color? color, Color? color,
bool isUnderLine = false, bool isUnderLine = false,
bool isBold = false, bool isBold = false,
bool isCenter = false, bool isCenter = false,
@ -176,7 +176,8 @@ extension EmailValidator on String {
int? maxlines, int? maxlines,
double? letterSpacing = 0, double? letterSpacing = 0,
double? height, double? height,
TextOverflow? textOverflow}) => TextOverflow? textOverflow,
}) =>
Text( Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,

@ -16,18 +16,16 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/AppointmentFilter.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/AppointmentFilter.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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/collapsing_list_view.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/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../widgets/common_bottom_sheet.dart' import '../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
show showCommonBottomSheetWithoutHeight;
class MyAppointmentsPage extends StatefulWidget { class MyAppointmentsPage extends StatefulWidget {
const MyAppointmentsPage({super.key}); const MyAppointmentsPage({super.key});
@ -86,18 +84,14 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
} }
Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) { Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) {
return getAppointList( return getAppointList(myAppointmentsVM, myAppointmentsVM.filteredAppointmentList);
myAppointmentsVM, myAppointmentsVM.filteredAppointmentList);
} }
Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Visibility( Visibility(visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)),
visible: myAppointmentsVM.availableFilters.isNotEmpty,
child: getAppointmentFilters(myAppointmentsVM)),
ListView.separated( ListView.separated(
padding: EdgeInsets.only(top: 24.h), padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true, shrinkWrap: true,
@ -110,14 +104,9 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading return myAppointmentsVM.isMyAppointmentsLoading
? Container( ? Container(
decoration: RoundedRectangleBorder() decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true),
child: AppointmentCard( child: AppointmentCard(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsViewModel, myAppointmentsViewModel: myAppointmentsViewModel,
bookAppointmentsViewModel: bookAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel,
isLoading: true, isLoading: true,
@ -134,16 +123,10 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
child: AnimatedContainer( child: AnimatedContainer(
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
decoration: RoundedRectangleBorder() decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true),
child: AppointmentCard( child: AppointmentCard(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel: filteredAppointmentList[index],
filteredAppointmentList[index], myAppointmentsViewModel: myAppointmentsViewModel,
myAppointmentsViewModel:
myAppointmentsViewModel,
bookAppointmentsViewModel: bookAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel,
isLoading: false, isLoading: false,
isFromHomePage: false, isFromHomePage: false,
@ -154,8 +137,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
) )
: Utils.getNoDataWidget( : Utils.getNoDataWidget(
context, context,
noDataText: "You don't have any appointments yet." noDataText: "You don't have any appointments yet.".needTranslation,
.needTranslation,
callToActionButton: CustomButton( callToActionButton: CustomButton(
text: LocaleKeys.bookAppo.tr(context: context), text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () { onPressed: () {
@ -168,18 +150,17 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B), textColor: Color(0xffED1C2B),
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40, height: 40.h,
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
).paddingSymmetrical(48.h, 0.h), ).paddingSymmetrical(48.h, 0.h),
); );
}, },
separatorBuilder: (BuildContext cxt, int index) => separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
SizedBox(height: 16.h),
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
], ],
@ -201,16 +182,14 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
selectedFilter: myAppointmentsVM.selectedFilter, selectedFilter: myAppointmentsVM.selectedFilter,
item: myAppointmentsVM.availableFilters[index], item: myAppointmentsVM.availableFilters[index],
onClicked: () { onClicked: () {
if (myAppointmentsVM.availableFilters[index] == if (myAppointmentsVM.availableFilters[index] == AppointmentListingFilters.DATESELECTION) {
AppointmentListingFilters.DATESELECTION) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: "Set The Date Range".needTranslation, title: "Set The Date Range".needTranslation,
context, context,
child: DateRangeSelector( child: DateRangeSelector(
onRangeSelected: (start, end) { onRangeSelected: (start, end) {
// if (start != null) { // if (start != null) {
myAppointmentsVM.getSelectedDateRange( myAppointmentsVM.getSelectedDateRange(start, end);
start, end);
// } // }
}, },
), ),
@ -219,8 +198,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
callBackFunc: () {}, callBackFunc: () {},
); );
} else { } else {
myAppointmentsVM.setSelectedFilter( myAppointmentsVM.setSelectedFilter(myAppointmentsVM.availableFilters[index]);
myAppointmentsVM.availableFilters[index]);
myAppointmentsVM.filterTheListAsPerSelection(); myAppointmentsVM.filterTheListAsPerSelection();
} }
}, },
@ -230,4 +208,3 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
)).paddingOnly(top: 24.h, left: 24.h, right: 24.h); )).paddingOnly(top: 24.h, left: 24.h, right: 24.h);
} }
} }

@ -106,7 +106,9 @@ class _LandingPageState extends State<LandingPage> {
insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false); insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false);
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
return Scaffold( return PopScope(
canPop: false,
child: Scaffold(
backgroundColor: AppColors.bgScaffoldColor, backgroundColor: AppColors.bgScaffoldColor,
body: SingleChildScrollView( body: SingleChildScrollView(
padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24), padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24),
@ -135,7 +137,7 @@ class _LandingPageState extends State<LandingPage> {
textColor: Color(0xffED1C2B), textColor: Color(0xffED1C2B),
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
height: 40.h, height: 40.h,
), ),
@ -168,7 +170,7 @@ class _LandingPageState extends State<LandingPage> {
); );
}), }),
], ],
) ),
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
appState.isAuthenticated appState.isAuthenticated
@ -188,17 +190,18 @@ class _LandingPageState extends State<LandingPage> {
), ),
], ],
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push( Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage()));
CustomPageRoute(
page: MyAppointmentsPage(),
),
);
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { Consumer<MyAppointmentsViewModel>(
builder: (context, myAppointmentsVM, child) {
return myAppointmentsVM.isMyAppointmentsLoading return myAppointmentsVM.isMyAppointmentsLoading
? Container( ? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: AppointmentCard( child: AppointmentCard(
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsViewModel, myAppointmentsViewModel: myAppointmentsViewModel,
@ -210,8 +213,11 @@ class _LandingPageState extends State<LandingPage> {
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1
? Container( ? Container(
decoration: decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: AppointmentCard( child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first,
myAppointmentsViewModel: myAppointmentsViewModel, myAppointmentsViewModel: myAppointmentsViewModel,
@ -240,8 +246,11 @@ class _LandingPageState extends State<LandingPage> {
), ),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return Container( return Container(
decoration: RoundedRectangleBorder() decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
.toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: AppointmentCard( child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
myAppointmentsViewModel: myAppointmentsViewModel, myAppointmentsViewModel: myAppointmentsViewModel,
@ -255,7 +264,7 @@ class _LandingPageState extends State<LandingPage> {
: Container( : Container(
width: double.infinity, width: double.infinity,
decoration: decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true), RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
child: Padding( child: Padding(
padding: EdgeInsets.all(12.h), padding: EdgeInsets.all(12.h),
child: Column( child: Column(
@ -267,11 +276,7 @@ class _LandingPageState extends State<LandingPage> {
CustomButton( CustomButton(
text: LocaleKeys.bookAppo.tr(context: context), text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () { onPressed: () {
Navigator.of(context).push( Navigator.of(context).push(CustomPageRoute(page: BookAppointmentPage()));
CustomPageRoute(
page: BookAppointmentPage(),
),
);
}, },
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA),
@ -281,13 +286,16 @@ class _LandingPageState extends State<LandingPage> {
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
height: (isFoldable || isTablet) ? 56.h : 46.h,
), ),
], ],
), ),
), ),
).paddingSymmetrical(24.h, 0.h); ).paddingSymmetrical(24.h, 0.h);
}), },
Consumer<ImmediateLiveCareViewModel>(builder: (context, immediateLiveCareVM, child) { ),
Consumer<ImmediateLiveCareViewModel>(
builder: (context, immediateLiveCareVM, child) {
return immediateLiveCareVM.patientHasPendingLiveCareRequest return immediateLiveCareVM.patientHasPendingLiveCareRequest
? Column( ? Column(
children: [ children: [
@ -295,7 +303,7 @@ class _LandingPageState extends State<LandingPage> {
Container( Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 20.h, borderRadius: 20.r,
hasShadow: true, hasShadow: true,
side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
), ),
@ -335,17 +343,14 @@ class _LandingPageState extends State<LandingPage> {
), ),
), ),
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push( Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()));
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
}), }),
SizedBox(height: 12.h), SizedBox(height: 12.h),
], ],
) )
: SizedBox(height: 12.h); : SizedBox(height: 12.h);
}), },
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -359,19 +364,12 @@ class _LandingPageState extends State<LandingPage> {
), ),
], ],
).paddingSymmetrical(24.h, 0.h).onPress(() { ).paddingSymmetrical(24.h, 0.h).onPress(() {
Navigator.of(context).push( Navigator.of(context).push(CustomPageRoute(page: MedicalFilePage()));
CustomPageRoute(
page: MedicalFilePage(),
),
);
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Container( Container(
height: 120.h, height: 120.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
color: AppColors.whiteColor,
borderRadius: 24,
),
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
@ -411,10 +409,7 @@ class _LandingPageState extends State<LandingPage> {
) )
: Container( : Container(
height: 127.h, height: 127.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
color: AppColors.whiteColor,
borderRadius: 24,
),
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
@ -464,15 +459,12 @@ class _LandingPageState extends State<LandingPage> {
], ],
).paddingSymmetrical(24.h, 0.h), ).paddingSymmetrical(24.h, 0.h),
SizedBox( SizedBox(
height: 325.h, height: 340.h,
child: Column(
children: [
Expanded(
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length, itemCount: LandingPageData.getServiceCardsList.length,
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.only(left: 24.h, right: 24.h), padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList( return AnimationConfiguration.staggeredList(
position: index, position: index,
@ -490,16 +482,14 @@ class _LandingPageState extends State<LandingPage> {
), ),
); );
}, },
separatorBuilder: (BuildContext cxt, int index) => 8.width, separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.w),
),
),
],
), ),
), ),
appState.isAuthenticated ? HabibWalletCard() : SizedBox(), appState.isAuthenticated ? HabibWalletCard() : SizedBox(),
], ],
), ),
), ),
),
); );
} }
@ -509,7 +499,8 @@ class _LandingPageState extends State<LandingPage> {
title: "", title: "",
isCloseButtonVisible: false, isCloseButtonVisible: false,
child: StatefulBuilder(builder: (context, setState) { child: StatefulBuilder(
builder: (context, setState) {
return QuickLogin( return QuickLogin(
isDone: isDone, isDone: isDone,
onPressed: () { onPressed: () {
@ -521,7 +512,8 @@ class _LandingPageState extends State<LandingPage> {
}); });
}, },
); );
}), },
),
// height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3, // height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3,
isFullScreen: false, isFullScreen: false,
callBackFunc: () { callBackFunc: () {

@ -16,7 +16,7 @@ class LargeServiceCard extends StatelessWidget {
final String title; final String title;
final String subtitle; final String subtitle;
LargeServiceCard({ const LargeServiceCard({
super.key, super.key,
this.image = "", this.image = "",
this.icon = "", this.icon = "",
@ -26,52 +26,38 @@ class LargeServiceCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Container(
padding: EdgeInsets.symmetric(horizontal: 3.h), width: 150.w,
child: Container( padding: EdgeInsets.symmetric(horizontal: 3.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.transparent, borderRadius: 16.r),
color: Colors.transparent,
borderRadius: 16,
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 0.h),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset(AppAssets.livecare_service, width: 220.h, height: 220.h, fit: BoxFit.contain), Image.asset(AppAssets.livecare_service, width: 220.w, fit: BoxFit.contain),
SizedBox(height: 3.h), SizedBox(height: 10.h),
Row( Row(
children: [ children: [
Utils.buildSvgWithAssets(icon: icon, width: 24.h, height: 24.h), Utils.buildSvgWithAssets(icon: icon, width: 24.w, height: 24.h),
title.toText14(color: AppColors.blackColor, isBold: true), Flexible(child: title.toText14(color: AppColors.blackColor, isBold: true, textOverflow: TextOverflow.clip, maxlines: 1)),
], ],
), ),
SizedBox(width: 220.h, child: subtitle.toText11(color: AppColors.blackColor)), subtitle.toText11(color: AppColors.blackColor),
SizedBox(height: 6.h), SizedBox(height: 10.h),
SizedBox( CustomButton(
width: 220.h,
child: CustomButton(
text: LocaleKeys.bookNow.tr(context: context), text: LocaleKeys.bookNow.tr(context: context),
onPressed: () { onPressed: () {},
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (BuildContext context) => LandingPage()),
// );
},
backgroundColor: AppColors.borderOnlyColor, backgroundColor: AppColors.borderOnlyColor,
borderColor: AppColors.borderOnlyColor, borderColor: AppColors.borderOnlyColor,
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h, height: 40.h,
), ),
),
], ],
), ),
),
),
); );
} }
} }

@ -221,12 +221,13 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: "${appState.getAuthenticatedUser()!.age} Years Old", labelText: "${appState.getAuthenticatedUser()!.age} Years Old",
labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.blood_icon, icon: AppAssets.blood_icon,
labelText: "Blood: ${appState.getUserBloodGroup}", labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}",
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), labelPadding: EdgeInsetsDirectional.only(end: 8.w),
), ),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) { Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return AppCustomChipWidget( return AppCustomChipWidget(
@ -237,7 +238,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconSize: 12.w, iconSize: 12.w,
backgroundColor: backgroundColor:
insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1), insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), labelPadding: EdgeInsetsDirectional.only(end: 8.w),
); );
}), }),
], ],
@ -377,7 +378,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h, height: isFoldable ? 50.h : 40.h,
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
), ),
@ -821,7 +822,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 56.h, height: isFoldable ? 50.h : 40.h,
).paddingOnly(left: 12.w, right: 12.w, bottom: 12.h), ).paddingOnly(left: 12.w, right: 12.w, bottom: 12.h),
), ),
).paddingSymmetrical(24.w, 0.h); ).paddingSymmetrical(24.w, 0.h);
@ -868,14 +869,16 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon, svgIcon: AppAssets.eye_result_icon,
isLargeText: true, isLargeText: true,
iconSize: 36.w), iconSize: 36.w,
),
MedicalFileCard( MedicalFileCard(
label: "Ancillary Orders List".needTranslation, label: "Ancillary Orders List".needTranslation,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon, svgIcon: AppAssets.eye_result_icon,
isLargeText: true, isLargeText: true,
iconSize: 36.w), iconSize: 36.w,
),
], ],
).paddingSymmetrical(24.w, 0.0), ).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 16.h), SizedBox(height: 16.h),

@ -53,7 +53,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
left: false, left: false,
right: false, right: false,
child: Column( child: Column(
spacing: 24, spacing: 24.h,
children: [ children: [
PageView( PageView(
controller: pageController, controller: pageController,
@ -75,24 +75,24 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
}, },
).expanded, ).expanded,
Row( Row(
spacing: 4.h, spacing: 4.w,
children: [ children: [
AnimatedContainer( AnimatedContainer(
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
height: 6.h, height: 6.h,
width: selectedIndex == 0 ? 18.h : 6.h, width: selectedIndex == 0 ? 18.w : 6.w,
decoration: decoration:
BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30.r)),
), ),
AnimatedContainer( AnimatedContainer(
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
height: 6.h, height: 6.h,
width: selectedIndex == 1 ? 18.h : 6.h, width: selectedIndex == 1 ? 18.w : 6.w,
decoration: decoration:
BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30.r)),
), ),
], ],
).paddingOnly(left: 24.h, right: 24.h), ).paddingOnly(left: 24.w, right: 24.w),
Row( Row(
children: [ children: [
AnimatedSwitcher( AnimatedSwitcher(
@ -102,20 +102,20 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
? CustomButton( ? CustomButton(
text: "Skip".needTranslation, text: "Skip".needTranslation,
onPressed: () => goToHomePage(), onPressed: () => goToHomePage(),
width: 86.h, width: 86.w,
height: 56.h, height: 56.h,
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
borderColor: Colors.transparent, borderColor: Colors.transparent,
).paddingOnly(left: 24.h) ).paddingOnly(left: 24.w)
: const SizedBox.shrink(), : const SizedBox.shrink(),
), ),
const Spacer(), const Spacer(),
AnimatedContainer( AnimatedContainer(
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut, curve: Curves.easeInOut,
width: selectedIndex == 0 ? 86.h : MediaQuery.of(context).size.width - 48.h, width: selectedIndex == 0 ? 86.w : MediaQuery.of(context).size.width - 48.w,
margin: EdgeInsets.only(left: 24.h, right: 24.h), margin: EdgeInsets.only(left: 24.w, right: 24.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primaryRedColor, color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
@ -126,8 +126,8 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
child: selectedIndex == 0 child: selectedIndex == 0
? CustomButton( ? CustomButton(
icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward, icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward,
iconSize: 32.h, iconSize: 32.w,
width: 86.h, width: 86.w,
height: 56.h, height: 56.h,
text: "".needTranslation, text: "".needTranslation,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -146,7 +146,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
), ),
), ),
], ],
), ).paddingOnly(bottom: 10.h),
], ],
), ),
), ),
@ -157,7 +157,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12, spacing: 12.h,
children: [ children: [
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
@ -167,10 +167,9 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
repeat: true, repeat: true,
reverse: false, reverse: false,
frameRate: FrameRate(60), frameRate: FrameRate(60),
width: MediaQuery.sizeOf(context).width - 50, width: MediaQuery.sizeOf(context).width - 50.w,
height: MediaQuery.sizeOf(context).width - 50))) height: MediaQuery.sizeOf(context).width - 50.w)))
.expanded, .expanded,
// 12.height,
Text( Text(
heading, heading,
style: TextStyle(fontSize: 36.f, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1), style: TextStyle(fontSize: 36.f, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1),
@ -180,6 +179,6 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16),
), ),
], ],
).paddingOnly(left: 24.h, right: 24.h); ).paddingOnly(left: 24.w, right: 24.w);
} }
} }

@ -51,6 +51,33 @@ class _ProfileSettingsState extends State<ProfileSettings> {
super.dispose(); super.dispose();
} }
double dynamicItemHeight(BuildContext context) {
final double w = SizeUtils.width;
final double h = SizeUtils.height;
double longer = w > h ? w : h;
double shorter = w < h ? w : h;
final double aspect = longer / (shorter == 0 ? 1 : shorter);
// Choose multiplier based on aspect ratio (handles near-square / foldable)
double multiplier;
if (aspect < 1.05) {
multiplier = 0.28; // nearly square / foldable -> smaller card height
} else if (aspect > 1.8) {
multiplier = 0.40; // very tall/wide -> larger height
} else {
multiplier = 0.34; // normal phones/tablets
}
// Compute and clamp using sensible bounds (uses .h extension)
final double minH = 210.h;
final double maxH = 380.h;
final double computed = (shorter * multiplier);
return computed.clamp(minH, maxH);
}
int length = 3; int length = 3;
final SwiperController _controller = SwiperController(); final SwiperController _controller = SwiperController();
@ -72,11 +99,11 @@ class _ProfileSettingsState extends State<ProfileSettings> {
itemCount: medicalVm.patientFamilyFiles.length, itemCount: medicalVm.patientFamilyFiles.length,
layout: SwiperLayout.STACK, layout: SwiperLayout.STACK,
loop: true, loop: true,
itemWidth: SizeUtils.width - 42.w, itemHeight: dynamicItemHeight(context),
itemWidth: SizeUtils.width - 30.w,
indicatorLayout: PageIndicatorLayout.COLOR, indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right, axisDirection: AxisDirection.right,
controller: _controller, controller: _controller,
itemHeight: 220.h,
pagination: SwiperPagination( pagination: SwiperPagination(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(top: (210.h + 8.h + 24.h)), margin: EdgeInsets.only(top: (210.h + 8.h + 24.h)),
@ -97,13 +124,14 @@ class _ProfileSettingsState extends State<ProfileSettings> {
onFamilySwitchPress: (FamilyFileResponseModelLists profile) { onFamilySwitchPress: (FamilyFileResponseModelLists profile) {
medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber);
}, },
).paddingOnly(right: 16.w); ).paddingOnly(right: 16.w, left: 8.w);
}, },
), ),
SizedBox(height: 5.h),
GridView( GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 9.w, mainAxisSpacing: 9.h), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: isTablet ? 3 : 2),
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 24.w, top: 10.w), padding: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 24.h),
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
Container( Container(
@ -115,14 +143,14 @@ class _ProfileSettingsState extends State<ProfileSettings> {
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4.h, // spacing: 4.h,
children: [ children: [
Row( Row(
spacing: 8.w, spacing: 8.w,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h), Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h),
"Habib Wallet".needTranslation.toText14(weight: FontWeight.w600, maxlines: 2).expanded, "Habib Wallet".needTranslation.toText16(weight: FontWeight.w600, maxlines: 2).expanded,
Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward), Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward),
], ],
), ),
@ -131,10 +159,11 @@ class _ProfileSettingsState extends State<ProfileSettings> {
return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false) return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h);
}), }),
Spacer(),
CustomButton( CustomButton(
height: 40.h, height: 40.h,
icon: AppAssets.recharge_icon, icon: AppAssets.recharge_icon,
iconSize: 24.h, iconSize: 22.w,
iconColor: AppColors.infoColor, iconColor: AppColors.infoColor,
textColor: AppColors.infoColor, textColor: AppColors.infoColor,
text: "Recharge".needTranslation, text: "Recharge".needTranslation,
@ -152,7 +181,7 @@ class _ProfileSettingsState extends State<ProfileSettings> {
).onPress(() { ).onPress(() {
Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage())); Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
}), }),
) ),
], ],
), ),
"Quick Actions" "Quick Actions"
@ -220,7 +249,12 @@ class _ProfileSettingsState extends State<ProfileSettings> {
], ],
), ),
), ),
CustomButton(icon: AppAssets.minus, text: "Deactivate account".needTranslation, onPressed: () {}).paddingAll(24.w), CustomButton(
height: 56.h,
icon: AppAssets.minus,
text: "Deactivate account".needTranslation,
onPressed: () {},
).paddingAll(24.w),
], ],
); );
}, },
@ -275,7 +309,11 @@ class FamilyCardWidget extends StatelessWidget {
final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false; final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID);
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.r, hasShadow: true), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r,
hasShadow: true,
),
child: Column( child: Column(
children: [ children: [
Column( Column(
@ -284,7 +322,6 @@ class FamilyCardWidget extends StatelessWidget {
children: [ children: [
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
//
children: [ children: [
Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h), Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h),
Column( Column(
@ -404,7 +441,7 @@ class FamilyCardWidget extends StatelessWidget {
borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor, borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor,
textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor,
iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor,
height: 56.h, height: isFoldable ? 50.h : 40.h,
fontSize: 14.f, fontSize: 14.f,
).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); ).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h);
} }
@ -420,7 +457,7 @@ class FamilyCardWidget extends StatelessWidget {
textColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, textColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor,
iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor,
onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {}, onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {},
height: 40.h, height: isFoldable ? 50.h : 40.h,
fontSize: 14.f, fontSize: 14.f,
).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); ).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h);
} }

@ -56,7 +56,7 @@ class CustomButton extends StatelessWidget {
return GestureDetector( return GestureDetector(
onTap: isDisabled ? null : onPressed, onTap: isDisabled ? null : onPressed,
child: Container( child: Container(
height: height ?? (50.h), height: height ?? (56.h),
width: width, width: width,
padding: padding, padding: padding,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(

@ -88,7 +88,7 @@ class CustomTabBarState extends State<CustomTabBar> {
bool isSelected = selectedIndex == currentIndex; bool isSelected = selectedIndex == currentIndex;
return Container( return Container(
height: 54.h, height: 54.h,
padding: EdgeInsets.only(top: 4.h, bottom: 4.h, left: 16.w, right: 16.w), padding: EdgeInsets.only(top: 4.h, bottom: 4.h, left: 14.w, right: 14.w),
alignment: Alignment.center, alignment: Alignment.center,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor, color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor,

Loading…
Cancel
Save