no message
parent
0895d94df2
commit
f7021f685a
Binary file not shown.
|
After Width: | Height: | Size: 2.4 MiB |
@ -0,0 +1,102 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
|
||||||
|
/// UI-only helper model for Vital Sign cards.
|
||||||
|
///
|
||||||
|
/// Keeps presentation logic (chip colors, icon colors, simple status rules)
|
||||||
|
/// in one place so it can be reused across multiple pages.
|
||||||
|
class VitalSignUiModel {
|
||||||
|
final Color iconBg;
|
||||||
|
final Color iconFg;
|
||||||
|
final Color chipBg;
|
||||||
|
final Color chipFg;
|
||||||
|
|
||||||
|
const VitalSignUiModel({
|
||||||
|
required this.iconBg,
|
||||||
|
required this.iconFg,
|
||||||
|
required this.chipBg,
|
||||||
|
required this.chipFg,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Returns a color scheme for a card based on its [status] and [label].
|
||||||
|
///
|
||||||
|
/// Rules (mirrors existing behavior in Medical File page):
|
||||||
|
/// - Height is always blue.
|
||||||
|
/// - High => red scheme.
|
||||||
|
/// - Low => yellow scheme.
|
||||||
|
/// - Otherwise => green scheme (Normal).
|
||||||
|
static VitalSignUiModel scheme({required String? status, required String label}) {
|
||||||
|
final s = (status ?? '').toLowerCase();
|
||||||
|
final l = label.toLowerCase();
|
||||||
|
|
||||||
|
// Height should always be blue.
|
||||||
|
if (l.contains('height')) {
|
||||||
|
return VitalSignUiModel(
|
||||||
|
iconBg: AppColors.infoColor.withValues(alpha: 0.12),
|
||||||
|
iconFg: AppColors.infoColor,
|
||||||
|
chipBg: AppColors.infoColor.withValues(alpha: 0.12),
|
||||||
|
chipFg: AppColors.infoColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.contains('high')) {
|
||||||
|
return const VitalSignUiModel(
|
||||||
|
iconBg: AppColors.chipSecondaryLightRedColor,
|
||||||
|
iconFg: AppColors.primaryRedColor,
|
||||||
|
chipBg: AppColors.chipSecondaryLightRedColor,
|
||||||
|
chipFg: AppColors.primaryRedColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.contains('low')) {
|
||||||
|
final Color yellowBg = AppColors.warningColor.withValues(alpha: 0.12);
|
||||||
|
return VitalSignUiModel(
|
||||||
|
iconBg: yellowBg,
|
||||||
|
iconFg: AppColors.warningColor,
|
||||||
|
chipBg: yellowBg,
|
||||||
|
chipFg: AppColors.warningColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal (green)
|
||||||
|
final Color greenBg = AppColors.lightGreenColor;
|
||||||
|
return VitalSignUiModel(
|
||||||
|
iconBg: greenBg,
|
||||||
|
iconFg: AppColors.bgGreenColor,
|
||||||
|
chipBg: greenBg,
|
||||||
|
chipFg: AppColors.bgGreenColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple, user-friendly classification:
|
||||||
|
/// - Low: systolic < 90 OR diastolic < 60
|
||||||
|
/// - High: systolic >= 140 OR diastolic >= 90
|
||||||
|
/// - Normal: otherwise
|
||||||
|
/// Returns null if values are missing/unparseable.
|
||||||
|
static String? bloodPressureStatus({dynamic systolic, dynamic diastolic}) {
|
||||||
|
final int? s = toIntOrNull(systolic);
|
||||||
|
final int? d = toIntOrNull(diastolic);
|
||||||
|
if (s == null || d == null) return null;
|
||||||
|
|
||||||
|
if (s < 90 || d < 60) return 'Low';
|
||||||
|
if (s >= 140 || d >= 90) return 'High';
|
||||||
|
return 'Normal';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int? toIntOrNull(dynamic v) {
|
||||||
|
if (v == null) return null;
|
||||||
|
if (v is int) return v;
|
||||||
|
if (v is double) return v.round();
|
||||||
|
return int.tryParse(v.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
static String bmiStatus(dynamic bmi) {
|
||||||
|
if (bmi == null) return 'N/A';
|
||||||
|
final double bmiValue = double.tryParse(bmi.toString()) ?? 0;
|
||||||
|
if (bmiValue < 18.5) return 'Underweight';
|
||||||
|
if (bmiValue < 25) return 'Normal';
|
||||||
|
if (bmiValue < 30) return 'Overweight';
|
||||||
|
return 'High';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
class VitalSignShimmerWidget extends StatelessWidget {
|
||||||
|
const VitalSignShimmerWidget({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
// BMI Card Shimmer
|
||||||
|
Expanded(child: _buildShimmerCard()),
|
||||||
|
SizedBox(width: 8.w),
|
||||||
|
// Height Card Shimmer
|
||||||
|
Expanded(child: _buildShimmerCard()),
|
||||||
|
SizedBox(width: 8.w),
|
||||||
|
// Weight Card Shimmer
|
||||||
|
Expanded(child: _buildShimmerCard()),
|
||||||
|
SizedBox(width: 8.w),
|
||||||
|
// Blood Pressure Card Shimmer
|
||||||
|
Expanded(child: _buildShimmerCard()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildShimmerCard() {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: BorderRadius.circular(12.r),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(12.w),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Icon shimmer
|
||||||
|
Container(
|
||||||
|
width: 32.w,
|
||||||
|
height: 32.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
).toShimmer(),
|
||||||
|
SizedBox(height: 8.h),
|
||||||
|
// Label shimmer
|
||||||
|
Container(
|
||||||
|
width: 50.w,
|
||||||
|
height: 10.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
).toShimmer(),
|
||||||
|
SizedBox(height: 4.h),
|
||||||
|
// Value shimmer
|
||||||
|
Container(
|
||||||
|
width: 40.w,
|
||||||
|
height: 16.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(4.r),
|
||||||
|
),
|
||||||
|
).toShimmer(),
|
||||||
|
SizedBox(height: 4.h),
|
||||||
|
// Chip shimmer
|
||||||
|
Container(
|
||||||
|
width: 45.w,
|
||||||
|
height: 18.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12.r),
|
||||||
|
),
|
||||||
|
).toShimmer(),
|
||||||
|
SizedBox(height: 4.h),
|
||||||
|
// Arrow shimmer
|
||||||
|
Align(
|
||||||
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
|
child: Container(
|
||||||
|
width: 10.w,
|
||||||
|
height: 10.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(2.r),
|
||||||
|
),
|
||||||
|
).toShimmer(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue