Merge pull request 'radiology ai Analysis' (#246) from dev_aamir into master
Reviewed-on: #246dev_aamir
commit
8e9c5ae6df
@ -0,0 +1,255 @@
|
||||
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/utils/size_utils.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/radiology/radiology_view_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/appbar/collapsing_list_view.dart';
|
||||
import 'package:hmg_patient_app_new/widgets/expandable_list_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class RadiologyAiAnalysisDetailedPage extends StatefulWidget {
|
||||
const RadiologyAiAnalysisDetailedPage({super.key});
|
||||
|
||||
@override
|
||||
State<RadiologyAiAnalysisDetailedPage> createState() => _RadiologyAiAnalysisDetailedPageState();
|
||||
}
|
||||
|
||||
class _RadiologyAiAnalysisDetailedPageState extends State<RadiologyAiAnalysisDetailedPage> {
|
||||
late RadiologyViewModel radiologyProvider;
|
||||
|
||||
List<String> _parseStringToList(String? text) {
|
||||
if (text == null || text.isEmpty) return [];
|
||||
// Split by newlines and filter out empty lines
|
||||
return text.split('\n').where((line) => line.trim().isNotEmpty).map((line) => line.trim()).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
radiologyProvider = Provider.of<RadiologyViewModel>(context, listen: false);
|
||||
return CollapsingListView(
|
||||
isClose: true,
|
||||
title: LocaleKeys.aiAnalysis.tr(context: context),
|
||||
child: Consumer<RadiologyViewModel>(
|
||||
builder: (context, model, child) {
|
||||
if (model.radiologyAiAnalysisResponse == null) {
|
||||
return Utils.getNoDataWidget(context);
|
||||
}
|
||||
|
||||
final analysis = model.radiologyAiAnalysisResponse!;
|
||||
List<ExpandableListItem> expandableItems = [];
|
||||
|
||||
// Parse string fields into lists
|
||||
final predictions = _parseStringToList(analysis.aiPredictions);
|
||||
final preventions = _parseStringToList(analysis.prevention);
|
||||
final riskFactorsText = _parseStringToList(analysis.riskFactors);
|
||||
|
||||
// Summary Section
|
||||
if (analysis.summary != null && analysis.summary!.isNotEmpty) {
|
||||
expandableItems.add(
|
||||
ExpandableListItem(
|
||||
backgroundColor: Colors.transparent,
|
||||
expandedBackgroundColor: Colors.transparent,
|
||||
title: LocaleKeys.summary.tr(context: context).toText18(weight: FontWeight.w700, color: AppColors.blackColor),
|
||||
initiallyExpanded: true,
|
||||
children: [
|
||||
SizedBox(height: 10.h),
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
side: BorderSide(width: 1, color: Color(0xFF0B85F7)),
|
||||
),
|
||||
padding: EdgeInsets.all(16.r),
|
||||
child: (analysis.summary ?? "").toText14(color: AppColors.textColorLight, height: 1.5, weight: FontWeight.w400),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Lab Results Section (Radiology Results)
|
||||
if (analysis.labResults != null && analysis.labResults!.isNotEmpty) {
|
||||
expandableItems.add(
|
||||
ExpandableListItem(
|
||||
backgroundColor: Colors.transparent,
|
||||
expandedBackgroundColor: Colors.transparent,
|
||||
title: LocaleKeys.labResults.tr(context: context).toText18(weight: FontWeight.w700, color: AppColors.blackColor),
|
||||
children: [
|
||||
SizedBox(height: 10.h),
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
side: BorderSide(width: 1, color: Color(0xFF0B85F7)),
|
||||
),
|
||||
padding: EdgeInsets.all(16.r),
|
||||
child: (analysis.labResults ?? "").toText14(color: AppColors.textColorLight, height: 1.5, weight: FontWeight.w400),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// AI Predictions Section
|
||||
if (predictions.isNotEmpty) {
|
||||
expandableItems.add(
|
||||
ExpandableListItem(
|
||||
backgroundColor: Colors.transparent,
|
||||
expandedBackgroundColor: Colors.transparent,
|
||||
title: LocaleKeys.prediction.tr(context: context).toText18(weight: FontWeight.w700, color: AppColors.blackColor),
|
||||
children: [
|
||||
SizedBox(height: 10.h),
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
side: BorderSide(width: 1, color: Color(0xFFFFAF15)),
|
||||
),
|
||||
padding: EdgeInsets.all(16.r),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: predictions.map((prediction) => _buildBulletItem(prediction)).toList(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Risk Factors Section
|
||||
if (riskFactorsText.isNotEmpty) {
|
||||
expandableItems.add(
|
||||
ExpandableListItem(
|
||||
backgroundColor: Colors.transparent,
|
||||
expandedBackgroundColor: Colors.transparent,
|
||||
title: LocaleKeys.riskFactors.tr(context: context).toText18(weight: FontWeight.w700, color: AppColors.blackColor),
|
||||
children: [
|
||||
SizedBox(height: 10.h),
|
||||
...riskFactorsText.asMap().entries.map((entry) {
|
||||
final riskText = entry.value;
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: Color(0xFFFBCB6E).withOpacity(0.10),
|
||||
borderRadius: 12.r,
|
||||
side: BorderSide(width: 1, color: Color(0xFFFBCB6E).withOpacity(0.10)),
|
||||
),
|
||||
padding: EdgeInsets.all(16.r),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Utils.buildSvgWithAssets(icon: AppAssets.guradIcon),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: LocaleKeys.riskFactors.tr(context: context).toText16(weight: FontWeight.w700, color: AppColors.blackColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
riskText.toText14(
|
||||
color: AppColors.textColorLight,
|
||||
height: 1.5,
|
||||
weight: FontWeight.w400,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Prevention Section
|
||||
if (preventions.isNotEmpty) {
|
||||
expandableItems.add(
|
||||
ExpandableListItem(
|
||||
backgroundColor: Colors.transparent,
|
||||
expandedBackgroundColor: Colors.transparent,
|
||||
title: LocaleKeys.prevention.tr(context: context).toText18(weight: FontWeight.w700, color: AppColors.blackColor),
|
||||
children: [
|
||||
SizedBox(height: 10.h),
|
||||
Container(
|
||||
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||
color: AppColors.whiteColor,
|
||||
borderRadius: 12.r,
|
||||
side: BorderSide(width: 1, color: Color(0xFF18C273)),
|
||||
),
|
||||
padding: EdgeInsets.all(16.r),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: preventions.map((prevention) => _buildBulletItem(prevention)).toList(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
if (expandableItems.isEmpty)
|
||||
Utils.getNoDataWidget(context)
|
||||
else
|
||||
CustomExpandableList(
|
||||
expansionMode: ExpansionMode.exactlyOne,
|
||||
dividerColor: AppColors.dividerColor,
|
||||
itemPadding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 14.h),
|
||||
items: expandableItems,
|
||||
theme: ExpandableListTheme.custom(
|
||||
defaultTrailingIcon: Utils.buildSvgWithAssets(
|
||||
icon: AppAssets.arrow_down,
|
||||
height: 22.h,
|
||||
width: 22.w,
|
||||
iconColor: AppColors.textColor,
|
||||
),
|
||||
),
|
||||
).paddingSymmetrical(16.w, 0.0),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(Icons.info_outline, color: AppColors.greyTextColor, size: 20.r),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: ("${LocaleKeys.disclaimer.tr()}:${LocaleKeys.thisAboveInfo.tr()}")
|
||||
.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w400),
|
||||
),
|
||||
],
|
||||
).paddingSymmetrical(16.w, 16.h)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBulletItem(String text) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 12.h),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: text.toText14(color: AppColors.textColorLight, height: 1.5, weight: FontWeight.w400),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue