pull/24/head
Sultan khan 2 months ago
commit 138862b115

@ -39,13 +39,15 @@ extension WidgetExtensions on Widget {
child: this,
);
Widget toShimmer2({bool isShow = true, double radius = 20}) => isShow
Widget toShimmer2({bool isShow = true, double radius = 20, double? width, double? height}) => isShow
? Shimmer.fromColors(
baseColor: const Color(0xffe8eff0),
highlightColor: Colors.white,
child: ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Container(
width: width,
height: height,
color: Colors.white,
child: this,
),

@ -0,0 +1,103 @@
import 'dart:ui';
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/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class CollapsingListView extends StatelessWidget {
final String title;
Widget? child;
VoidCallback? search;
Widget? bottomChild;
bool isClose;
CollapsingListView({required this.title, this.child, this.search, this.isClose = false, this.bottomChild});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Column(
children: [
CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
expandedHeight: 100,
stretch: true,
backgroundColor: AppColors.bgScaffoldColor,
leading: IconButton(
icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h),
padding: EdgeInsets.only(left: 12),
onPressed: () => Navigator.pop(context),
),
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
final double maxHeight = 100;
final double minHeight = kToolbarHeight;
double t = (constraints.maxHeight - minHeight) / (maxHeight - minHeight);
t = t - 1;
if (t < 0.7) t = 0.7;
t = t.clamp(0.0, 1.0);
final double fontSize = lerpDouble(14, 18, t)!;
final double bottomPadding = lerpDouble(0, 0, t)!;
final double leftPadding = lerpDouble(150, 24, t)!;
return Stack(
children: [
Align(
alignment: Alignment.lerp(
Alignment.center,
Alignment.bottomLeft,
t,
)!,
child: Padding(
padding: EdgeInsets.only(
left: leftPadding,
bottom: bottomPadding,
),
child: Row(
spacing: 4.h,
children: [
Text(
title,
maxLines: 1,
style: TextStyle(
fontSize: (27 - (5 * (2 - t))).fSize,
fontWeight: FontWeight.lerp(
FontWeight.w300,
FontWeight.w600,
t,
)!,
color: AppColors.blackColor,
letterSpacing: -0.5),
).expanded,
if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24)
],
)),
),
],
);
},
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => child,
childCount: 1,
),
),
],
).expanded,
if (bottomChild != null) bottomChild!
],
),
);
}
}

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -13,14 +14,19 @@ 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/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapse_example.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.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';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
import 'collapsing_list_view.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@ -32,7 +38,8 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
late LabViewModel labProvider;
List<List<TestDetails>?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText='';
String? selectedFilterText = '';
@override
void initState() {
scheduleMicrotask(() {
@ -45,214 +52,75 @@ class _LabOrdersPageState extends State<LabOrdersPage> {
Widget build(BuildContext context) {
labProvider = Provider.of<LabViewModel>(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
title: LocaleKeys.labResults.tr(context: context).toText18(),
backgroundColor: AppColors.bgScaffoldColor,
),
body: Padding(
padding: EdgeInsets.all(24.h),
child: SingleChildScrollView(
child: Consumer<LabViewModel>(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.labResults.tr(context: context).toText24(isBold: true),
Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(() {
if (model.isLabOrdersLoading) {
return;
}else {
showCommonBottomSheet(context, child: SearchLabResultsContent(labSuggestionsList: model.labSuggestions),
callBackFunc: (value) {
selectedFilterText = value;
model.filterLabReports(value!);
},
title: LocaleKeys.searchLabReport.tr(),
height: ResponsiveExtension.screenHeight,
isFullScreen: true,
isCloseButtonVisible: true);
}
}),
],
),
SizedBox(height: 16.h),
// Build Tab Bar
SizedBox(height: 16.h),
// Expandable list
selectedFilterText!.isNotEmpty ? CustomChipWidget(chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.length,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? const MoviesShimmerWidget()
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: 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: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(
text: getLabOrderStatusText(model.patientLabOrders[index].status!),
onPressed: () {},
backgroundColor: getLabOrderStatusColor(model.patientLabOrders[index].status!).withOpacity(0.15),
borderColor: getLabOrderStatusColor(model.patientLabOrders[index].status!).withOpacity(0.01),
textColor: getLabOrderStatusColor(model.patientLabOrders[index].status!),
fontSize: 10,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 30.h,
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
Row(
children: [
Image.network(
model.patientLabOrders[index].doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
).circle(100),
SizedBox(width: 4.h),
model.patientLabOrders[index].doctorName!.toText16(isBold: true)
],
),
SizedBox(height: 8.h),
Row(
children: [
CustomButton(
text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientLabOrders[index].createdOn), false),
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
SizedBox(width: 8.h),
CustomButton(
text: model.patientLabOrders[index].clinicDescription!,
onPressed: () {},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 12,
fontWeight: FontWeight.w500,
borderRadius: 8,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 24.h,
),
],
),
],
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...model.patientLabOrders[index].testDetails!.map((detail) {
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${detail.description}'.toText14(weight: FontWeight.w500),
);
}).toList(),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(),
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,
),
],
),
],
),
)
: SizedBox.shrink(key: ValueKey<int>(-index)),
),
],
),
),
),
body: CollapsingListView(
title: LocaleKeys.labResults.tr(),
search: () async {
final lavVM = Provider.of<LabViewModel>(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(CupertinoPageRoute(fullscreenDialog: true, builder: (context) => SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions)));
if (value != null) {
selectedFilterText = value;
lavVM.filterLabReports(value);
}
}
},
child: SingleChildScrollView(
padding: EdgeInsets.all(24),
physics: NeverScrollableScrollPhysics(),
child: Consumer<LabViewModel>(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,
chipType: ChipTypeEnum.alert,
isSelected: true,
)
: SizedBox(),
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.length,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)
: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabResultItemView(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
labOrder: model.patientLabOrders[index],
index: index,
isExpanded: isExpanded)),
),
),
);
},
),
],
);
},
);
},
),
],
);
},
),
),
),
),
);
));
}
Color getLabOrderStatusColor(num status) {

@ -0,0 +1,172 @@
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/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';
class LabResultItemView extends StatelessWidget {
final VoidCallback onTap;
final int index;
final PatientLabOrdersResponseModel? labOrder;
final bool isLoading;
final bool isExpanded;
LabResultItemView({Key? key, required this.onTap, this.labOrder, required this.index, this.isLoading = false, this.isExpanded = false}) : super(key: key);
@override
Widget 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: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
labelText: getLabOrderStatusText(labOrder?.status ?? 0, context),
backgroundColor: getLabOrderStatusColor(labOrder?.status ?? 0).withOpacity(0.15),
textColor: getLabOrderStatusColor(labOrder?.status ?? 0),
).toShimmer2(isShow: isLoading, width: 100),
if (!isLoading) Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
Row(
children: [
Image.network(
isLoading ? "" : labOrder!.doctorImageURL!,
width: 24.h,
height: 24.h,
fit: BoxFit.fill,
errorBuilder: (cxt, child, tr) {
return SizedBox(height: 24, width: 24);
},
).toShimmer2(isShow: isLoading).circle(100),
SizedBox(width: 4.h),
(labOrder?.doctorName ?? "").toText16(isBold: true).toShimmer2(isShow: isLoading, width: 200)
],
),
SizedBox(height: 8.h),
Wrap(
spacing: 8.h,
runSpacing: 0.h,
children: [
AppCustomChipWidget(labelText: isLoading ? "null" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(labOrder!.createdOn), false)).toShimmer2(isShow: isLoading, width: 70),
AppCustomChipWidget(labelText: isLoading ? "null" : labOrder!.clinicDescription!).toShimmer2(isShow: isLoading, width: 100),
],
),
],
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...labOrder!.testDetails!.map((detail) {
return Padding(
padding: EdgeInsets.only(bottom: 8.h),
child: '${detail.description}'.toText14(weight: FontWeight.w500),
);
}).toList(),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(),
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,
),
],
),
],
),
)
: SizedBox.shrink(key: ValueKey<int>(-index)),
),
],
),
),
);
}
String getLabOrderStatusText(num status, context) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
}

@ -4,6 +4,7 @@ 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/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.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/input_widget.dart';
@ -48,74 +49,75 @@ class _SearchLabResultsContentState extends State<SearchLabResultsContent> {
});
} else {
setState(() {
filteredSuggestions = widget.labSuggestionsList
.where((suggestion) => suggestion.toLowerCase().contains(query))
.toList();
filteredSuggestions = widget.labSuggestionsList.where((suggestion) => suggestion.toLowerCase().contains(query)).toList();
});
}
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextInputWidget(
labelText: "Search lab results",
hintText: "Type test name",
controller: searchEditingController,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
return CollapsingListView(
title: LocaleKeys.labResults.tr(),
isClose: true,
bottomChild: Container(
color: Colors.white,
padding: EdgeInsets.all(ResponsiveExtension(20).h),
child: CustomButton(
text: LocaleKeys.search.tr(),
icon: AppAssets.search_icon,
iconColor: Colors.white,
onPressed: () => Navigator.pop(context, searchEditingController.text),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 24, right: 24, top: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextInputWidget(
labelText: "Search lab results",
hintText: "Type test name",
controller: searchEditingController,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
),
),
SizedBox(height: ResponsiveExtension(20).h),
if (filteredSuggestions.isNotEmpty) ...[
"Suggestions".toText16(isBold: true),
const SizedBox(height: 12),
SizedBox(height: ResponsiveExtension(20).h),
if (filteredSuggestions.isNotEmpty) ...[
"Suggestions".toText16(isBold: true),
],
],
],
),
),
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16),
SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 24, top: 16),
child: Wrap(
alignment: WrapAlignment.start,
spacing: 10,
runSpacing: 10,
children: filteredSuggestions
.map((label) => SuggestionChip(
label: label,
onTap: () {
searchEditingController.text = label;
},
))
label: label,
onTap: () {
searchEditingController.text = label;
},
))
.toList(),
),
),
),
Container(
color: Colors.white,
padding: EdgeInsets.all(ResponsiveExtension(20).h),
child: CustomButton(
text: LocaleKeys.search.tr(),
icon: AppAssets.search_icon,
iconColor: Colors.white,
onPressed: () => Navigator.pop(context, searchEditingController.text),
),
),
],
],
),
);
}
}
@ -153,4 +155,4 @@ class SuggestionChip extends StatelessWidget {
),
);
}
}
}

@ -44,10 +44,12 @@ class AppCustomChipWidget extends StatelessWidget {
avatar: icon.isNotEmpty ? Utils.buildSvgWithAssets(icon: icon, width: iconSize.h, height: iconSize.h, iconColor: iconColor) : SizedBox.shrink(),
label: labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor),
padding: EdgeInsets.all(0.0),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
labelPadding: EdgeInsets.only(left: -4.h, right: 8.h),
backgroundColor: backgroundColor,
)
: Chip(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: labelText!.toText10(weight: FontWeight.w500, letterSpacing: -0.64, color: textColor),
padding: EdgeInsets.all(0.0),
backgroundColor: backgroundColor,

Loading…
Cancel
Save