Merge branch 'refs/heads/master' into dev_sikander
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 161 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5525 2.46209C9.79631 1.38751 8.20843 1.81413 7.24747 2.53374L7.24696 2.53412L6.99967 2.71856L6.75249 2.53412C5.79154 1.81445 4.20322 1.38747 2.44697 2.46209C1.21445 3.21625 0.537796 4.77917 0.775779 6.54921C1.01526 8.33035 2.16771 10.3232 4.57364 12.0996L4.64687 12.1537C5.46216 12.7562 6.04566 13.1875 6.99985 13.1875C7.95405 13.1875 8.53754 12.7562 9.35283 12.1537L9.42606 12.0996C10.3581 11.4114 11.1012 10.6918 11.6765 9.96419C11.6797 9.96013 11.6828 9.95603 11.6859 9.95191C14.0531 6.94717 13.5653 3.69371 11.5525 2.46209ZM7.74698 6.53198L10.4778 9.62245C10.0168 10.1444 9.44544 10.6701 8.74767 11.1853C7.83876 11.8564 7.54669 12.05 6.99985 12.05C6.45302 12.05 6.16095 11.8564 5.25204 11.1853C3.02962 9.54441 2.09491 7.80338 1.90596 6.39801C1.71551 4.98153 2.26916 3.90531 3.0432 3.43169C4.33507 2.64121 5.40954 2.95773 6.05358 3.43314L4.70267 4.46071C3.88293 5.08424 3.8086 6.28812 4.54542 7.00729C4.86355 7.31779 5.27961 7.48142 5.70118 7.48816C5.71901 7.48845 5.73685 7.48845 5.75469 7.48818C6.05478 7.48357 6.3557 7.39938 6.62418 7.232L7.74698 6.53198Z" fill="#2E3039"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
///class used to provide value for the [DynamicResultChart] to plot the values
|
||||
class DataPoint {
|
||||
///values that is displayed on the graph and dot is plotted on this
|
||||
final double value;
|
||||
///label shown on the bottom of the graph
|
||||
String label;
|
||||
|
||||
DataPoint(
|
||||
{required this.value,
|
||||
required this.label,
|
||||
});
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
import 'dart:ui' show Color;
|
||||
|
||||
class ThresholdRange {
|
||||
final String label;
|
||||
final double value;
|
||||
final Color color;
|
||||
final Color lineColor;
|
||||
final String? actualValue;
|
||||
|
||||
ThresholdRange(
|
||||
{required this.label,
|
||||
required this.value,
|
||||
required this.color,
|
||||
required this.lineColor,
|
||||
this.actualValue});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ThresholdRange(label: $label, value: $value, color: ${color.value.toRadixString(16)}, lineColor: ${lineColor.value.toRadixString(16)})';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
import 'dart:convert';
|
||||
|
||||
class FamilyFileResponseModelLists {
|
||||
int? id;
|
||||
int? patientId;
|
||||
int? responseId;
|
||||
dynamic relationshipId;
|
||||
dynamic relationship;
|
||||
dynamic relationshipN;
|
||||
int? regionId;
|
||||
int? familyRegionId;
|
||||
int? status;
|
||||
dynamic isActive;
|
||||
String? editedOn;
|
||||
String? createdOn;
|
||||
int? age;
|
||||
String? emaiLAddress;
|
||||
int? gender;
|
||||
String? genderDescription;
|
||||
String? genderImage;
|
||||
String? mobileNumber;
|
||||
int? patientDataVerified;
|
||||
String? patientIdenficationNumber;
|
||||
String? patientName;
|
||||
String? statusDescription;
|
||||
|
||||
FamilyFileResponseModelLists({
|
||||
this.id,
|
||||
this.patientId,
|
||||
this.responseId,
|
||||
this.relationshipId,
|
||||
this.relationship,
|
||||
this.relationshipN,
|
||||
this.regionId,
|
||||
this.familyRegionId,
|
||||
this.status,
|
||||
this.isActive,
|
||||
this.editedOn,
|
||||
this.createdOn,
|
||||
this.age,
|
||||
this.emaiLAddress,
|
||||
this.gender,
|
||||
this.genderDescription,
|
||||
this.genderImage,
|
||||
this.mobileNumber,
|
||||
this.patientDataVerified,
|
||||
this.patientIdenficationNumber,
|
||||
this.patientName,
|
||||
this.statusDescription,
|
||||
});
|
||||
|
||||
factory FamilyFileResponseModelLists.fromRawJson(String str) => FamilyFileResponseModelLists.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory FamilyFileResponseModelLists.fromJson(Map<String, dynamic> json) => FamilyFileResponseModelLists(
|
||||
id: json["ID"],
|
||||
patientId: json["PatientID"],
|
||||
responseId: json["ResponseID"],
|
||||
relationshipId: json["RelationshipID"],
|
||||
relationship: json["Relationship"],
|
||||
relationshipN: json["RelationshipN"],
|
||||
regionId: json["RegionID"],
|
||||
familyRegionId: json["FamilyRegionID"],
|
||||
status: json["Status"],
|
||||
isActive: json["IsActive"],
|
||||
editedOn: json["EditedOn"],
|
||||
createdOn: json["CreatedOn"],
|
||||
age: json["Age"],
|
||||
emaiLAddress: json["EmaiLAddress"],
|
||||
gender: json["Gender"],
|
||||
genderDescription: json["GenderDescription"],
|
||||
genderImage: json["GenderImage"],
|
||||
mobileNumber: json["MobileNumber"],
|
||||
patientDataVerified: json["PatientDataVerified"],
|
||||
patientIdenficationNumber: json["PatientIdenficationNumber"],
|
||||
patientName: json["PatientName"],
|
||||
statusDescription: json["StatusDescription"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"ID": id,
|
||||
"PatientID": patientId,
|
||||
"ResponseID": responseId,
|
||||
"RelationshipID": relationshipId,
|
||||
"Relationship": relationship,
|
||||
"RelationshipN": relationshipN,
|
||||
"RegionID": regionId,
|
||||
"FamilyRegionID": familyRegionId,
|
||||
"Status": status,
|
||||
"IsActive": isActive,
|
||||
"EditedOn": editedOn,
|
||||
"CreatedOn": createdOn,
|
||||
"Age": age,
|
||||
"EmaiLAddress": emaiLAddress,
|
||||
"Gender": gender,
|
||||
"GenderDescription": genderDescription,
|
||||
"GenderImage": genderImage,
|
||||
"MobileNumber": mobileNumber,
|
||||
"PatientDataVerified": patientDataVerified,
|
||||
"PatientIdenficationNumber": patientIdenficationNumber,
|
||||
"PatientName": patientName,
|
||||
"StatusDescription": statusDescription,
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
|
||||
|
||||
class LabOrderByTest extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
final int index;
|
||||
final TestDetails? tests;
|
||||
final bool isLoading;
|
||||
final bool isExpanded;
|
||||
|
||||
const LabOrderByTest({super.key, required this.onTap, this.tests, required this.index, this.isLoading = false, this.isExpanded = false});
|
||||
|
||||
@override
|
||||
build(BuildContext context) {
|
||||
return AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
margin: EdgeInsets.symmetric(vertical: 8.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (!isLoading) {
|
||||
onTap();
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
key: ValueKey<int>(index),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// ...labOrder!.testDetails!.map((detail) {
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 8.h),
|
||||
child: '${tests!.description}'.toText14(weight: FontWeight.w500),
|
||||
),
|
||||
|
||||
SizedBox(height: 12.h),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
AppCustomChipWidget(
|
||||
richText: '${"Last Tested:".needTranslation} ${ DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(isBold: true),
|
||||
// chipType: ChipTypeEnum.lightBg,
|
||||
backgroundColor: AppColors.greyLightColor,
|
||||
textColor: AppColors.textColor,
|
||||
// borderRadius: 5,
|
||||
|
||||
),
|
||||
CustomButton(
|
||||
icon: AppAssets.view_report_icon,
|
||||
iconColor: AppColors.primaryRedColor,
|
||||
iconSize: 16.h,
|
||||
text: LocaleKeys.viewReport.tr(context: context),
|
||||
onPressed: () {},
|
||||
backgroundColor: AppColors.secondaryLightRedColor,
|
||||
borderColor: AppColors.secondaryLightRedColor,
|
||||
textColor: AppColors.primaryRedColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
borderRadius: 12,
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||
height: 40.h,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.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/core/utils/validation_utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
|
||||
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart';
|
||||
import 'package:hmg_patient_app_new/services/dialog_service.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||
|
||||
class FamilyMedicalScreen extends StatefulWidget {
|
||||
final List<FamilyFileResponseModelLists> profiles;
|
||||
final Function(FamilyFileResponseModelLists) onSelect;
|
||||
|
||||
const FamilyMedicalScreen({
|
||||
Key? key,
|
||||
required this.profiles,
|
||||
required this.onSelect,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<FamilyMedicalScreen> createState() => _FamilyMedicalScreenState();
|
||||
}
|
||||
|
||||
class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
|
||||
List<CustomTabBarModel> tabs = [CustomTabBarModel("", LocaleKeys.medicalFile.tr()), CustomTabBarModel("", LocaleKeys.request.tr())];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.scaffoldBgColor,
|
||||
appBar: CustomAppBar(
|
||||
onBackPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onLanguageChanged: (lang) {},
|
||||
hideLogoAndLang: true,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
LocaleKeys.myMedicalFile.tr().toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2),
|
||||
SizedBox(height: 25.h),
|
||||
CustomTabBar(
|
||||
tabs: tabs,
|
||||
onTabChange: (int index) {},
|
||||
),
|
||||
SizedBox(height: 25.h),
|
||||
FamilyCards(profiles: widget.profiles, onSelect: widget.onSelect, isShowDetails: true),
|
||||
SizedBox(height: 20.h),
|
||||
],
|
||||
),
|
||||
).paddingSymmetrical(20, 0),
|
||||
bottomSheet: Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
customBorder: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 20.h),
|
||||
child: CustomButton(
|
||||
text: "Add a new family member",
|
||||
onPressed: () {
|
||||
showModelSheet();
|
||||
},
|
||||
icon: AppAssets.add_icon,
|
||||
height: 56.h,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
void showModelSheet() {
|
||||
AuthenticationViewModel authVm = getIt.get<AuthenticationViewModel>();
|
||||
return showCommonBottomSheetWithoutHeight(context,
|
||||
title: "Add Family Member",
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
"Please fill the below field to add a new family member to your profile".toText16(color: AppColors.textColor, weight: FontWeight.w500),
|
||||
SizedBox(height: 20.h),
|
||||
Container(
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
|
||||
child: Column(
|
||||
children: [
|
||||
CustomCountryDropdown(
|
||||
countryList: CountryEnum.values,
|
||||
onCountryChange: authVm.onCountryChange,
|
||||
).paddingOnly(top: 8.h, bottom: 16.h),
|
||||
Divider(
|
||||
height: 1.h,
|
||||
color: AppColors.spacerLineColor,
|
||||
),
|
||||
TextInputWidget(
|
||||
labelText: LocaleKeys.nationalIdNumber.tr(),
|
||||
hintText: "xxxxxxxxx",
|
||||
controller: authVm.nationalIdController,
|
||||
// focusNode: _nationalIdFocusNode,
|
||||
isEnable: true,
|
||||
prefix: null,
|
||||
isAllowRadius: true,
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
autoFocus: true,
|
||||
keyboardType: TextInputType.number,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h),
|
||||
leadingIcon: AppAssets.student_card,
|
||||
).paddingOnly(top: 8.h, bottom: 8.h),
|
||||
Divider(
|
||||
height: 1.h,
|
||||
color: AppColors.spacerLineColor,
|
||||
),
|
||||
TextInputWidget(
|
||||
labelText: LocaleKeys.phoneNumber.tr(),
|
||||
hintText: "574345434",
|
||||
controller: authVm.phoneNumberController,
|
||||
isEnable: true,
|
||||
prefix: authVm.selectedCountrySignup.countryCode,
|
||||
isAllowRadius: true,
|
||||
isBorderAllowed: false,
|
||||
isAllowLeadingIcon: true,
|
||||
autoFocus: true,
|
||||
keyboardType: TextInputType.number,
|
||||
padding: EdgeInsets.symmetric(vertical: 8.h),
|
||||
leadingIcon: AppAssets.smart_phone,
|
||||
).paddingOnly(top: 8.h, bottom: 4),
|
||||
|
||||
//TextInputWidget(
|
||||
// labelText: widget.isForEmail ? LocaleKeys.email.tr() : LocaleKeys.phoneNumber.tr(),
|
||||
// hintText: widget.isForEmail ? "demo@gmail.com" : "5xxxxxxxx",
|
||||
// controller: widget.textController!,
|
||||
// focusNode: _textFieldFocusNode,
|
||||
// autoFocus: widget.autoFocus,
|
||||
// padding: EdgeInsets.all(8.h),
|
||||
// keyboardType: widget.isForEmail ? TextInputType.emailAddress : TextInputType.number,
|
||||
// onChange: (value) {
|
||||
// if (widget.onChange != null) {
|
||||
// widget.onChange!(value);
|
||||
// }
|
||||
// },
|
||||
// onCountryChange: (value) {
|
||||
// if (widget.onCountryChange != null) {
|
||||
// widget.onCountryChange!(value);
|
||||
// }
|
||||
// },
|
||||
// isEnable: true,
|
||||
// isReadOnly: widget.isFromSavedLogin,
|
||||
// prefix: widget.isForEmail ? null : widget.countryCode,
|
||||
// isBorderAllowed: false,
|
||||
// isAllowLeadingIcon: true,
|
||||
// fontSize: 13.h,
|
||||
// isCountryDropDown: widget.isEnableCountryDropdown,
|
||||
// leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone,
|
||||
// )
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.h),
|
||||
CustomButton(
|
||||
text: "Verify the member",
|
||||
onPressed: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
if (ValidationUtils.isValidatedIdAndPhoneWithCountryValidation(
|
||||
nationalId: authVm.nationalIdController.text,
|
||||
selectedCountry: authVm.selectedCountrySignup,
|
||||
phoneNumber: authVm.phoneNumberController.text,
|
||||
onOkPress: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
)) {}
|
||||
},
|
||||
icon: AppAssets.add_icon,
|
||||
height: 56.h,
|
||||
fontWeight: FontWeight.w600),
|
||||
SizedBox(height: 20.h),
|
||||
],
|
||||
),
|
||||
callBackFunc: () {});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||
import 'package:hmg_patient_app_new/core/enums.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart';
|
||||
|
||||
class FamilyCards extends StatefulWidget {
|
||||
final List<FamilyFileResponseModelLists> profiles;
|
||||
final Function(FamilyFileResponseModelLists) onSelect;
|
||||
final bool isShowDetails;
|
||||
final bool isBottomSheet;
|
||||
|
||||
const FamilyCards({super.key, required this.profiles, required this.onSelect, this.isShowDetails = false, this.isBottomSheet = false});
|
||||
|
||||
@override
|
||||
State<FamilyCards> createState() => _FamilyCardsState();
|
||||
}
|
||||
|
||||
class _FamilyCardsState extends State<FamilyCards> {
|
||||
AppState appState = getIt<AppState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: widget.profiles.length,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10.h,
|
||||
mainAxisSpacing: 10.h,
|
||||
childAspectRatio: widget.isShowDetails ? 0.56.h : 0.74.h,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final profile = widget.profiles[index];
|
||||
final isActive = (profile.responseId == appState
|
||||
.getAuthenticatedUser()
|
||||
?.patientId);
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
|
||||
child: Opacity(
|
||||
opacity: isActive ? 0.4 : 1.0, // Fade all content if active
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 5.h),
|
||||
Utils.buildImgWithAssets(
|
||||
icon: profile.gender == 1 ? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img) : (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg),
|
||||
width: 80.h,
|
||||
height: 78.h),
|
||||
SizedBox(height: 8.h),
|
||||
(profile.patientName ?? "Unknown").toText16(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
|
||||
SizedBox(height: 4.h),
|
||||
CustomChipWidget(
|
||||
chipType: ChipTypeEnum.alert,
|
||||
backgroundColor: AppColors.lightGrayBGColor,
|
||||
chipText: "Relation: ${profile.relationship ?? "N/A"}",
|
||||
iconAsset: AppAssets.heart,
|
||||
isShowBorder: false,
|
||||
borderRadius: 8.h,
|
||||
textColor: AppColors.textColor),
|
||||
widget.isShowDetails ? SizedBox(height: 4.h) : SizedBox(),
|
||||
widget.isShowDetails
|
||||
? CustomChipWidget(
|
||||
chipType: ChipTypeEnum.alert,
|
||||
backgroundColor: AppColors.lightGrayBGColor,
|
||||
chipText: "Age: ${profile.age ?? "N/A"} Years",
|
||||
isShowBorder: false,
|
||||
borderRadius: 8.h,
|
||||
textColor: AppColors.textColor,
|
||||
)
|
||||
: SizedBox(),
|
||||
widget.isShowDetails ? SizedBox(height: 8.h) : SizedBox(),
|
||||
Spacer(),
|
||||
if (isActive)
|
||||
CustomButton(
|
||||
height: 40.h,
|
||||
onPressed: () {},
|
||||
text: LocaleKeys.active.tr(),
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
borderColor: Colors.grey.shade200,
|
||||
textColor: AppColors.greyTextColor,
|
||||
fontSize: 13.h,
|
||||
).paddingOnly(top: 0, bottom: 0)
|
||||
else
|
||||
CustomButton(
|
||||
height: 40.h,
|
||||
onPressed: () => widget.onSelect(profile),
|
||||
text: LocaleKeys.select.tr(),
|
||||
backgroundColor: AppColors.secondaryLightRedColor,
|
||||
borderColor: AppColors.secondaryLightRedColor,
|
||||
textColor: AppColors.primaryRedColor,
|
||||
fontSize: 13.h,
|
||||
icon: widget.isBottomSheet ? null : AppAssets.heart,
|
||||
iconColor: AppColors.primaryRedColor,
|
||||
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
|
||||
).paddingOnly(top: 0, bottom: 0),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||
import 'package:hmg_patient_app_new/features/medical_file/models/family_file_response_model.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/my_family/my_Family.dart';
|
||||
import 'package:hmg_patient_app_new/presentation/my_family/widget/family_cards.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
||||
|
||||
class MyFamilySheet {
|
||||
static void show(BuildContext context, List<FamilyFileResponseModelLists> familyLists, Function(FamilyFileResponseModelLists) onSelect) {
|
||||
return showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
titleWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
'Please select a profile'.toText21(isBold: true),
|
||||
'switch from the below list of medical file'.toText16(weight: FontWeight.w100, color: AppColors.greyTextColor),
|
||||
],
|
||||
),
|
||||
child: FamilyCards(
|
||||
profiles: familyLists,
|
||||
onSelect: (profile) {
|
||||
Navigator.of(context).pop(); // Close the bottom sheet
|
||||
onSelect(profile); // Call the onSelect callback
|
||||
},
|
||||
isBottomSheet: true),
|
||||
callBackFunc: () {},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,287 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
|
||||
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||
///
|
||||
/// CustomGraph(dataPoints: sampleData, scrollDirection: Axis.horizontal,height: 200,maxY: 100, maxX:2.5,
|
||||
/// leftLabelFormatter: (value){
|
||||
/// Widget buildLabel(String label) {
|
||||
/// return Padding(
|
||||
/// padding: const EdgeInsets.only(right: 8),
|
||||
/// child: Text(
|
||||
/// label,
|
||||
/// style: TextStyle(
|
||||
/// fontSize: 8.fSize, color: AppColors.textColor,
|
||||
/// fontFamily:
|
||||
/// FontUtils.getFontFamilyForLanguage(false)
|
||||
/// ),
|
||||
/// textAlign: TextAlign.right,
|
||||
/// ),
|
||||
/// );
|
||||
/// }
|
||||
/// switch (value.toInt()) {
|
||||
///
|
||||
/// case 20:
|
||||
/// return buildLabel("Critical Low");
|
||||
/// case 40:
|
||||
/// return buildLabel("Low");
|
||||
/// case 60:
|
||||
/// return buildLabel("Normal");
|
||||
/// case 80:
|
||||
/// return buildLabel("High");
|
||||
/// case 100:
|
||||
/// return buildLabel("Critical High");
|
||||
/// }
|
||||
/// return const SizedBox.shrink();
|
||||
/// },
|
||||
///
|
||||
/// ),
|
||||
class CustomGraph extends StatelessWidget {
|
||||
final List<DataPoint> dataPoints;
|
||||
final double? width;
|
||||
final double height;
|
||||
final double? maxY;
|
||||
final double? maxX;
|
||||
final Color spotColor;
|
||||
final Color graphColor;
|
||||
final Color graphShadowColor;
|
||||
final Color graphGridColor;
|
||||
final Color bottomLabelColor;
|
||||
final double? bottomLabelSize;
|
||||
final FontWeight? bottomLabelFontWeight;
|
||||
|
||||
///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart
|
||||
final Widget Function(double value) leftLabelFormatter;
|
||||
|
||||
final Axis scrollDirection;
|
||||
final bool showBottomTitleDates;
|
||||
final bool isFullScreeGraph;
|
||||
|
||||
const CustomGraph({
|
||||
super.key,
|
||||
required this.dataPoints,
|
||||
required this.leftLabelFormatter,
|
||||
this.width,
|
||||
required this.scrollDirection,
|
||||
required this.height,
|
||||
this.maxY,
|
||||
this.maxX,
|
||||
this.showBottomTitleDates = true,
|
||||
this.isFullScreeGraph = false,
|
||||
this.spotColor = AppColors.bgGreenColor,
|
||||
this.graphColor = AppColors.bgGreenColor,
|
||||
this.graphShadowColor = AppColors.graphGridColor,
|
||||
this.graphGridColor = AppColors.graphGridColor,
|
||||
this.bottomLabelColor = AppColors.textColor,
|
||||
this.bottomLabelFontWeight = FontWeight.w500,
|
||||
this.bottomLabelSize,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// var maxY = 0.0;
|
||||
double interval = 20;
|
||||
if ((maxY ?? 0) > 10 && (maxY ?? 0) <= 20) {
|
||||
interval = 2;
|
||||
} else if ((maxY ?? 0) > 5 && (maxY ?? 0) <= 10) {
|
||||
interval = 1;
|
||||
} else if ((maxY ?? 0) >= 0 && (maxY ?? 0) <= 5) {
|
||||
interval = .4;
|
||||
}
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
minY: 0,
|
||||
maxY:
|
||||
((maxY?.ceilToDouble() ?? 0.0) + interval).floorToDouble(),
|
||||
// minX: dataPoints.first.labelValue - 1,
|
||||
maxX: maxX,
|
||||
minX: -0.2,
|
||||
lineTouchData: LineTouchData(
|
||||
getTouchLineEnd: (_, __) => 0,
|
||||
getTouchedSpotIndicator: (barData, indicators) {
|
||||
// Only show custom marker for touched spot
|
||||
return indicators.map((int index) {
|
||||
return TouchedSpotIndicatorData(
|
||||
FlLine(color: Colors.transparent),
|
||||
FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (spot, percent, barData, idx) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 8,
|
||||
color: spotColor,
|
||||
strokeWidth: 2,
|
||||
strokeColor: Colors.white,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
enabled: true,
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (_) => Colors.white,
|
||||
getTooltipItems: (touchedSpots) {
|
||||
if (touchedSpots.isEmpty) return [];
|
||||
// Only show tooltip for the first touched spot, hide others
|
||||
return touchedSpots.map((spot) {
|
||||
if (spot == touchedSpots.first) {
|
||||
final dataPoint = dataPoints[spot.x.toInt()];
|
||||
|
||||
return LineTooltipItem(
|
||||
// '${dataPoint.label} ${spot.y.toStringAsFixed(2)}',
|
||||
'${dataPoint.value} ',
|
||||
TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 12.fSize,
|
||||
fontWeight: FontWeight.w500),
|
||||
);
|
||||
}
|
||||
return null; // hides the rest
|
||||
}).toList();
|
||||
},
|
||||
),
|
||||
),
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 77,
|
||||
interval: .1, // Let fl_chart handle it
|
||||
getTitlesWidget: (value, _) {
|
||||
return leftLabelFormatter(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameSize: 60,
|
||||
sideTitles: SideTitles(
|
||||
showTitles: showBottomTitleDates,
|
||||
reservedSize: 50,
|
||||
getTitlesWidget: (value, _) {
|
||||
if ((value.toDouble() >= 0) &&
|
||||
(value.toDouble() < (maxX ?? dataPoints.length))) {
|
||||
var label = dataPoints[value.toInt()].label;
|
||||
|
||||
return buildBottomLabel(label);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
interval: 1, // ensures 1:1 mapping with spots
|
||||
),
|
||||
),
|
||||
topTitles: AxisTitles(),
|
||||
rightTitles: AxisTitles(),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: const Border(
|
||||
bottom: BorderSide.none,
|
||||
left: BorderSide(color: Colors.grey, width: .5),
|
||||
right: BorderSide.none,
|
||||
top: BorderSide.none,
|
||||
),
|
||||
),
|
||||
lineBarsData: _buildColoredLineSegments(dataPoints),
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: false,
|
||||
horizontalInterval: 20,
|
||||
checkToShowHorizontalLine: (value) =>
|
||||
value >= 0 && value <= 100,
|
||||
getDrawingHorizontalLine: (value) {
|
||||
return FlLine(
|
||||
color: AppColors.graphGridColor,
|
||||
strokeWidth: 1,
|
||||
dashArray: [5, 5],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
List<LineChartBarData> _buildColoredLineSegments(List<DataPoint> dataPoints) {
|
||||
final List<FlSpot> allSpots = dataPoints.asMap().entries.map((entry) {
|
||||
return FlSpot(entry.key.toDouble(), entry.value.value);
|
||||
}).toList();
|
||||
|
||||
var data = [
|
||||
LineChartBarData(
|
||||
spots: allSpots,
|
||||
isCurved: true,
|
||||
isStrokeCapRound: true,
|
||||
isStrokeJoinRound: true,
|
||||
barWidth: 4,
|
||||
gradient: LinearGradient(
|
||||
colors: [graphColor, graphColor],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
dotData: FlDotData(
|
||||
show: false,
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
graphShadowColor,
|
||||
Colors.transparent,
|
||||
],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
)
|
||||
];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Widget buildLabel(String label) {
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.only(right: 8),
|
||||
// child: Text(
|
||||
// label,
|
||||
// style: TextStyle(
|
||||
// fontSize: leftLabelSize ?? 8.fSize, color: leftLabelColor),
|
||||
// textAlign: TextAlign.right,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildBottomLabel(String label) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: bottomLabelSize ?? 8.fSize, color: bottomLabelColor),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final List<DataPoint> sampleData = [
|
||||
DataPoint(
|
||||
value: 20,
|
||||
label: 'Jan 2024',
|
||||
),
|
||||
DataPoint(
|
||||
value: 36,
|
||||
label: 'Feb 2024',
|
||||
),
|
||||
DataPoint(
|
||||
value: 80,
|
||||
label: 'This result',
|
||||
),
|
||||
];
|
||||
@ -1,44 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ProfileSelector extends StatelessWidget {
|
||||
final List<Map<String, dynamic>> profiles;
|
||||
final Function(Map<String, dynamic>) onSelect;
|
||||
|
||||
const ProfileSelector({
|
||||
Key? key,
|
||||
required this.profiles,
|
||||
required this.onSelect,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: profiles.map((profile) {
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
radius: 22,
|
||||
backgroundImage: profile["GenderImage"] != null &&
|
||||
profile["GenderImage"].toString().isNotEmpty
|
||||
? NetworkImage(profile["GenderImage"])
|
||||
: AssetImage(
|
||||
profile["Gender"] == 1
|
||||
? "assets/images/male.png"
|
||||
: "assets/images/female.png")
|
||||
as ImageProvider,
|
||||
),
|
||||
title: Text(
|
||||
profile["PatientName"] ?? "Unknown",
|
||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
profile["Relationship"] ?? "Self",
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||
onTap: () => onSelect(profile),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../common_bottom_sheet.dart';
|
||||
import 'my_Family.dart';
|
||||
class MyFamilySheet {
|
||||
static void show(BuildContext context, List<Map<String, dynamic>> profiles, Function(Map<String, dynamic>) onSelect) {
|
||||
showCommonBottomSheetWithoutHeight(
|
||||
context,
|
||||
title: 'Select Profile',
|
||||
child: ProfileSelector(profiles: profiles, onSelect: (profile) {
|
||||
Navigator.of(context).pop(); // Close the bottom sheet
|
||||
onSelect(profile); // Call the onSelect callback
|
||||
}), callBackFunc: () {},
|
||||
);
|
||||
}
|
||||
}
|
||||