diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 8945fee..fc9d255 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -856,5 +856,14 @@ "onboardingHeading1": "حجز المواعيد لم يكن أسهل من قبل", "onboardingBody1": "ببضع نقرات فقط يمكنك استشارة الطبيب الذي تختاره.", "onboardingHeading2": "الوصول إلى السجل الطبي بين يديك", - "onboardingBody2": "تتبع تاريخك الطبي بما في ذلك الفحوصات المخبرية، الوصفات الطبية، التأمين، وغيرها." + "onboardingBody2": "تتبع تاريخك الطبي بما في ذلك الفحوصات المخبرية، الوصفات الطبية، التأمين، وغيرها.", + "normal": "عادي", + "attention": "انتباه", + "monitor": "مراقبة", + "noSpecialResult": "لا توجد نتائج خاصة", + "setTheDateRange": "تعيين النطاق الزمني", + "historyFlowchart": "مخطط تدفق التاريخ", + "to": "إلى", + "startDate": "تاريخ البدء", + "endDate": "تاريخ الانتهاء" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 843daf8..146d96f 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -852,5 +852,16 @@ "onboardingHeading1": "Booking appointment has never been easy", "onboardingBody1": "In few clicks find yourself having consultation with the doctor of your choice.", "onboardingHeading2": "Access the medical history on finger tips", - "onboardingBody2": "Keep track on your medical history including labs, prescription, insurance, etc" + "onboardingBody2": "Keep track on your medical history including labs, prescription, insurance, etc", + "normal": "Normal", + "attention": "Attention", + "monitor": "Monitor", + "noSpecialResult": "No Special Results", + "setTheDateRange": "Set The Date Range", + "historyFlowchart": "History FlowChart", + "to": "to", + "startDate" : "Start Date", + "endDate": "End Date" + + } \ No newline at end of file diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index a4e76fc..5925641 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -176,8 +176,12 @@ class ApiClientImp implements ApiClient { body[_appState.isAuthenticated ? 'TokenID' : 'LogInTokenID'] = _appState.appAuthToken; } - // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 4767477; + body['TokenID'] = "@dm!n"; + body['PatientID'] = 1018977; + body['PatientTypeID'] = 1; + + body['PatientOutSA'] = 0; + body['SessionID'] = "45786230487560q"; } body.removeWhere((key, value) => value == null); @@ -198,7 +202,7 @@ class ApiClientImp implements ApiClient { final response = await http.post(Uri.parse(url.trim()), body: json.encode(body), headers: headers); final int statusCode = response.statusCode; - log("response.body: ${response.body}"); + // log("response.body: ${response.body}"); if (statusCode < 200 || statusCode >= 400) { var parsed = json.decode(utf8.decode(response.bodyBytes)); onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data")); diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index fa02b2e..5886dfa 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -727,7 +727,7 @@ const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatu class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT diff --git a/lib/features/lab/lab_repo.dart b/lib/features/lab/lab_repo.dart index 36f9767..2618ab6 100644 --- a/lib/features/lab/lab_repo.dart +++ b/lib/features/lab/lab_repo.dart @@ -4,6 +4,7 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:dartz/dartz.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; +import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_special_result.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'models/resp_models/lab_result.dart' show LabResult; @@ -11,6 +12,14 @@ import 'models/resp_models/lab_result.dart' show LabResult; abstract class LabRepo { Future>>> getPatientLabOrders(); Future>>> getPatientLabResults(PatientLabOrdersResponseModel laborder, bool isVidaPlus, String procedureName); + + Future>>> + getPatientLabResultsByHospitals( + PatientLabOrdersResponseModel laborder, bool isVidaPlus); + + Future>>> + getSpecialLabResult( + PatientLabOrdersResponseModel laborder, bool isVidaPlus); } class LabRepoImp implements LabRepo { @@ -73,7 +82,6 @@ class LabRepoImp implements LabRepo { request['ProjectID'] = laborder.projectID; request['ClinicID'] = laborder.clinicID; request['Procedure'] = procedureName; - request['LanguageID'] = 1; try { GenericApiModel>? apiResponse; Failure? failure; @@ -90,6 +98,58 @@ class LabRepoImp implements LabRepo { throw Exception("lab list is empty"); } + final labOrders = list + .map((item) => LabResult.fromJson(item as Map)) + .toList() + .cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: labOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>>> + getPatientLabResultsByHospitals( + PatientLabOrdersResponseModel laborder, bool isVidaPlus) async { + Map request = Map(); + request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0"; + request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo; + request['OrderNo'] = laborder!.orderNo; + request['isDentalAllowedBackend'] = false; + request['SetupID'] = laborder!.setupID; + request['ProjectID'] = laborder.projectID; + request['ClinicID'] = laborder.clinicID; + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_Patient_LAB_RESULT, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['ListPLR']; + if (list == null || list.isEmpty) { + throw Exception("lab list is empty"); + } + final labOrders = list.map((item) => LabResult.fromJson(item as Map)).toList().cast(); apiResponse = GenericApiModel>( @@ -110,4 +170,57 @@ class LabRepoImp implements LabRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> + getSpecialLabResult( + PatientLabOrdersResponseModel laborder, bool isVidaPlus) async { + Map request = Map(); + request['InvoiceNo_VP'] = isVidaPlus ? laborder!.invoiceNo : "0"; + request['InvoiceNo'] = isVidaPlus ? "0" : laborder!.invoiceNo; + request['OrderNo'] = laborder!.orderNo; + request['isDentalAllowedBackend'] = false; + request['SetupID'] = laborder!.setupID; + request['ProjectID'] = laborder.projectID; + request['ClinicID'] = laborder.clinicID; + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_Patient_LAB_SPECIAL_RESULT, + body: request, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response['ListPLSR']; + if (list == null || list.isEmpty) { + throw Exception("lab list is empty"); + } + + final labOrders = list + .map((item) => PatientLabSpecialResult.fromJson( + item as Map)) + .toList() + .cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: labOrders, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 5e89cd3..1bbc27c 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -20,6 +20,8 @@ import 'package:logger/logger.dart'; class LabViewModel extends ChangeNotifier { bool isLabOrdersLoading = false; bool isLabResultsLoading = false; + bool isLabResultByHospitalLoading = false; + bool isSpecialResultsLoading = false; LabRepo labRepo; ErrorHandlerService errorHandlerService; @@ -28,6 +30,11 @@ class LabViewModel extends ChangeNotifier { List patientLabOrders = []; List filteredLabOrders = []; List tempLabOrdersList = []; + String labSpecialResult = ""; + + PatientLabOrdersResponseModel? currentlySelectedPatientOrder; + + List mainLabResultsByHospitals = []; List mainLabResults = []; List mainGraphPoints = []; @@ -138,6 +145,31 @@ class LabViewModel extends ChangeNotifier { }; } + Future getPatientLabResultByHospital( + PatientLabOrdersResponseModel laborder) async { + isLabResultByHospitalLoading = true; + notifyListeners(); + mainLabResultsByHospitals.clear; + + final result = await labRepo.getPatientLabResultsByHospitals(laborder, + Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0"))); + + result.fold( + (failure) async { + isLabResultByHospitalLoading = false; + // await errorHandlerService.handleError(failure: failure); + }, + (apiResponse) { + isLabResultByHospitalLoading = false; + if (apiResponse.messageStatus == 2) { + } else if (apiResponse.messageStatus == 1) { + mainLabResultsByHospitals = apiResponse.data ?? []; + notifyListeners(); + } + }, + ); + } + Future getPatientLabResult( PatientLabOrdersResponseModel laborder, String procedureName) async { LoaderBottomSheet.showLoader(); @@ -199,6 +231,42 @@ class LabViewModel extends ChangeNotifier { ); } + Future getPatientSpecialResult( + PatientLabOrdersResponseModel laborder) async { + isSpecialResultsLoading = true; + labSpecialResult = ""; + notifyListeners(); + final result = await labRepo.getSpecialLabResult( + laborder, + Utils.isVidaPlusProject(int.parse(laborder.projectID ?? "0")), + ); + + result.fold( + (failure) async { + isSpecialResultsLoading = false; + notifyListeners(); + // await errorHandlerService.handleError(failure: failure); + }, + (apiResponse) { + isSpecialResultsLoading = false; + if (apiResponse.messageStatus == 2) { + } else if (apiResponse.messageStatus == 1) { + StringBuffer htmlbuffer = StringBuffer(""); + + apiResponse.data?.forEach((element) { + if(element.resultDataHTML != null && element.resultDataHTML?.isNotEmpty == true) + htmlbuffer.write("${element.resultDataHTML}

"); + }); + + labSpecialResult = htmlbuffer.toString(); + + notifyListeners(); + } + notifyListeners(); + }, + ); + } + String resultDate(DateTime date){ @@ -460,4 +528,21 @@ class LabViewModel extends ChangeNotifier { return true; } } + + String getSeverityText(String refernceValue) { + switch (refernceValue) { + case 'N': + return "normal"; + case 'L': + case 'H': + return "monitor"; + case 'CL': + case 'LCL': + case 'CH': + case 'HCH': + return "attention"; + default: + return "normal"; + } + } } diff --git a/lib/features/lab/models/resp_models/lab_result.dart b/lib/features/lab/models/resp_models/lab_result.dart index d4e9223..3f14d54 100644 --- a/lib/features/lab/models/resp_models/lab_result.dart +++ b/lib/features/lab/models/resp_models/lab_result.dart @@ -24,6 +24,7 @@ class LabResult { String? referenceHigh; String? criticalLow; String? referenceLow; + String? packageShortDescription; LabResult( {this.description, @@ -78,6 +79,7 @@ class LabResult { referenceHigh = json['ReferenceHigh']; criticalLow = json['CriticalLow']; referenceLow = json['ReferenceLow']; + packageShortDescription = json['PackageShortDescription']; } Map toJson() { diff --git a/lib/features/lab/models/resp_models/patient_lab_special_result.dart b/lib/features/lab/models/resp_models/patient_lab_special_result.dart new file mode 100644 index 0000000..87301f9 --- /dev/null +++ b/lib/features/lab/models/resp_models/patient_lab_special_result.dart @@ -0,0 +1,32 @@ +class PatientLabSpecialResult { + String? invoiceNo; + String? moduleID; + String? resultData; + String? resultDataHTML; + dynamic resultDataTxt; + + PatientLabSpecialResult( + {this.invoiceNo, + this.moduleID, + this.resultData, + this.resultDataHTML, + this.resultDataTxt}); + + PatientLabSpecialResult.fromJson(Map json) { + invoiceNo = json['InvoiceNo']; + moduleID = json['ModuleID']; + resultData = json['ResultData']; + resultDataHTML = json['ResultDataHTML']; + resultDataTxt = json['ResultDataTxt']; + } + + Map toJson() { + final Map data = new Map(); + data['InvoiceNo'] = this.invoiceNo; + data['ModuleID'] = this.moduleID; + data['ResultData'] = this.resultData; + data['ResultDataHTML'] = this.resultDataHTML; + data['ResultDataTxt'] = this.resultDataTxt; + return data; + } +} diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 3d12e5b..3c382f7 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -849,5 +849,20 @@ abstract class LocaleKeys { static const selectCountry = 'selectCountry'; static const forLoginVerification = 'forLoginVerification'; static const searchHospital = 'searchHospital'; + static const skip = 'skip'; + static const getStarted = 'getStarted'; + static const onboardingHeading1 = 'onboardingHeading1'; + static const onboardingBody1 = 'onboardingBody1'; + static const onboardingHeading2 = 'onboardingHeading2'; + static const onboardingBody2 = 'onboardingBody2'; + static const normal = 'normal'; + static const attention = 'attention'; + static const monitor = 'monitor'; + static const noSpecialResult = 'noSpecialResult'; + static const setTheDateRange = 'setTheDateRange'; + static const historyFlowchart = 'historyFlowchart'; + static const to = 'to'; + static const startDate = 'startDate'; + static const endDate = 'endDate'; } diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 6fd8234..1c78bbd 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1,229 +1 @@ -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/enums.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/lab/lab_range_view_model.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/lab_order_by_test.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/chip/custom_chip_widget.dart'; -import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:provider/provider.dart'; -import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; -import '../../widgets/appbar/collapsing_list_view.dart'; - -class LabOrdersPage extends StatefulWidget { - const LabOrdersPage({super.key}); - - @override - State createState() => _LabOrdersPageState(); -} - -class _LabOrdersPageState extends State { - late LabViewModel labProvider; - late LabRangeViewModel rangeViewModel; - - List?> labSuggestions = []; - int? expandedIndex; - String? selectedFilterText = ''; - int activeIndex = 0; - - @override - void initState() { - scheduleMicrotask(() { - labProvider.initLabProvider(); - }); - super.initState(); - } - - @override - Widget build(BuildContext context) { - labProvider = Provider.of(context); - rangeViewModel = Provider.of(context); - - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title: LocaleKeys.labResults.tr(), - search: () async { - final lavVM = Provider.of(context, listen: false); - if (lavVM.isLabOrdersLoading) { - return; - } else { - String? value = await Navigator.of(context).push( - CustomPageRoute( - page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), - fullScreenDialog: true, - direction: AxisDirection.down, - ), - ); - if (value != null) { - selectedFilterText = value; - lavVM.filterLabReports(value); - } - } - }, - child: SingleChildScrollView( - padding: EdgeInsets.all(24.h), - physics: NeverScrollableScrollPhysics(), - child: Consumer( - builder: (context, model, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - CustomTabBar( - activeTextColor: Color(0xffED1C2B), - activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), - tabs: [ - CustomTabBarModel(null, "By Visit".needTranslation), - CustomTabBarModel(null, "By Test".needTranslation), - // CustomTabBarModel(null, "Completed".needTranslation), - ], - onTabChange: (index) { - activeIndex = index; - setState(() {}); - }, - ), - SizedBox(height: 16.h), - selectedFilterText!.isNotEmpty - ? CustomChipWidget( - chipText: selectedFilterText!, - chipType: ChipTypeEnum.alert, - isSelected: true, - ) - : SizedBox(), - activeIndex == 0 - ? ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - itemCount: model.isLabOrdersLoading - ? 5 - : model.patientLabOrders.isNotEmpty - ? model.patientLabOrders.length - : 1, - itemBuilder: (context, index) { - final isExpanded = expandedIndex == index; - return model.isLabOrdersLoading - ? LabResultItemView( - onTap: () {}, - labOrder: null, - index: index, - isLoading: true, - ) - : model.patientLabOrders.isNotEmpty - ? 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)), - ), - ) - : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); - }, - ) - : ListView.builder( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().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: LabOrderByTest( - onTap: () { - if(model.uniqueTests.toList()[index].model != null) { - rangeViewModel.flush(); - model.getPatientLabResult( - model.uniqueTests - .toList()[index] - .model!, model.uniqueTests - .toList()[index].description!); - } - }, - tests: model.uniqueTests.toList()[index], - index: index, - isExpanded: isExpanded)), - ), - ); - }, - ) - ], - ); - }, - ), - ), - )); - } - - 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; - } - } - - String getLabOrderStatusText(num status) { - 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 ""; - } - } - - getLabSuggestions(LabViewModel model) { - if (model.patientLabOrders.isEmpty) { - return []; - } - return model.patientLabOrders.map((m) => m.testDetails).toList(); - } -} +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/enums.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/lab/lab_range_view_model.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/lab_order_by_test.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultByHospital.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/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import '../../widgets/appbar/collapsing_list_view.dart'; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late LabRangeViewModel rangeViewModel; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context); rangeViewModel = Provider.of(context); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( title: LocaleKeys.labResults.tr(), search: () async { final lavVM = Provider.of(context, listen: false); if (lavVM.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; lavVM.filterLabReports(value); } } }, child: SingleChildScrollView( padding: EdgeInsets.all(24.h), physics: NeverScrollableScrollPhysics(), child: Consumer( builder: (context, model, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, "By Visit".needTranslation), CustomTabBarModel(null, "By Test".needTranslation), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), SizedBox(height: 16.h), selectedFilterText!.isNotEmpty ? CustomChipWidget( chipText: selectedFilterText!, chipType: ChipTypeEnum.alert, isSelected: true, ) : SizedBox(), activeIndex == 0 ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.patientLabOrders.isNotEmpty ? model.patientLabOrders.length : 1, itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return model.isLabOrdersLoading ? LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ) : model.patientLabOrders.isNotEmpty ? AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: LabResultItemView( onTap: () { model.currentlySelectedPatientOrder = model.patientLabOrders[ index]; scheduleMicrotask(() { labProvider .getPatientLabResultByHospital( model.patientLabOrders[ index]); labProvider .getPatientSpecialResult( model.patientLabOrders[ index]); }); Navigator.push(context,FadePage( page: LabResultByHospitals(), )); }, labOrder: model.patientLabOrders[index], index: index, isExpanded: isExpanded)), ), ) : Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation); }, ) : ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: model.isLabOrdersLoading ? 5 : model.uniqueTests.toList().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: LabOrderByTest( onTap: () { if(model.uniqueTests.toList()[index].model != null) { rangeViewModel.flush(); model.getPatientLabResult( model.uniqueTests .toList()[index] .model!, model.uniqueTests .toList()[index].description!); } }, tests: model.uniqueTests.toList()[index], index: index, isExpanded: isExpanded)), ), ); }, ) ], ); }, ), ), )); } 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; } } String getLabOrderStatusText(num status) { 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 ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_via_hospital/LabResultByHospital.dart b/lib/presentation/lab/lab_result_via_hospital/LabResultByHospital.dart new file mode 100644 index 0000000..fb88908 --- /dev/null +++ b/lib/presentation/lab/lab_result_via_hospital/LabResultByHospital.dart @@ -0,0 +1,69 @@ +import 'package:easy_localization/easy_localization.dart' + show tr, StringTranslateExtension; +import 'package:flutter/material.dart'; +import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; +import 'package:hmg_patient_app_new/core/utils/size_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/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/LabResultList.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/lab_order_specialResult.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:provider/provider.dart'; + +class LabResultByHospitals extends StatelessWidget { + @override + Widget build(BuildContext context) { + return CollapsingListView( + title: LocaleKeys.labResults.tr(), + child: SingleChildScrollView( + child: Column( + spacing: 8.h, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Selector( + selector: (_, model) => model.isLabResultByHospitalLoading, + builder: (_, isLoading, __) { + if (isLoading) { + return Column( + children: [ + LabResultItemView( + onTap: () {}, + labOrder: null, + index: 0, + isLoading: true, + ), + LabResultItemView( + onTap: () {}, + labOrder: null, + index: 0, + isLoading: true, + ), + LabResultItemView( + onTap: () {}, + labOrder: null, + index: 0, + isLoading: true, + ), + LabResultItemView( + onTap: () {}, + labOrder: null, + index: 0, + isLoading: true, + ), + ], + ); + } else { + return LabResultList(); + } + }, + ), + LabOrderSpecialResult() + ], + ).paddingAll(24.h), + )); + } +} diff --git a/lib/presentation/lab/lab_result_via_hospital/LabResultList.dart b/lib/presentation/lab/lab_result_via_hospital/LabResultList.dart new file mode 100644 index 0000000..9e1f08c --- /dev/null +++ b/lib/presentation/lab/lab_result_via_hospital/LabResultList.dart @@ -0,0 +1,40 @@ +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/lab/lab_view_model.dart'; +import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart'; +import 'package:provider/provider.dart' show Selector, Provider; + +class LabResultList extends StatelessWidget { + late LabViewModel model; + + @override + Widget build(BuildContext context) { + model = Provider.of(context); + return Selector>( + selector: (_, model) => model.mainLabResultsByHospitals, + builder: (__, list, ___) { + if (list.isEmpty) { + return Utils.getNoDataWidget(context, + noDataText: "You don't have any lab results yet." + .needTranslation); + } else { + return ListView.builder( + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) { + var labItem = list[index]; + return LabOrderResultItem(onTap: () { + model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description??""); + }, + tests: labItem, + index: index, + iconColor: model.getColor(labItem.calculatedResultFlag ?? "N"), + severityText: model.getSeverityText(labItem.calculatedResultFlag ?? "N")); + }); + } + }, + ); + } +} \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart b/lib/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart new file mode 100644 index 0000000..59638de --- /dev/null +++ b/lib/presentation/lab/lab_result_via_hospital/lab_order_result_item.dart @@ -0,0 +1,96 @@ +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/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/lab/models/resp_models/lab_result.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 LabOrderResultItem extends StatelessWidget { + final VoidCallback onTap; + final int index; + final LabResult? tests; + final String severityText; + final bool isLoading; + final bool isExpanded; + final Color iconColor; + + const LabOrderResultItem({super.key, required this.onTap, this.tests, required this.index, this.isLoading = false, this.isExpanded = false,required this.iconColor, required this.severityText}); + + @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: Container( + key: ValueKey(index), + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 16.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), + ), + '${tests!.packageShortDescription}'.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + // + SizedBox(height: 24.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + spacing: 6.h, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + Text(severityText.tr(), + style: TextStyle( + fontFamily: 'Poppins', + fontSize: 10.fSize, + fontWeight: FontWeight.w500, + color: AppColors.greyTextColor + )), + Utils.buildSvgWithAssets( + icon: AppAssets.lab_result_indicator, + width: 21, + height: 23, + iconColor: iconColor + ), + ], + ), + CustomButton( + icon: AppAssets.view_report_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + text: LocaleKeys.viewReport.tr(context: context), + onPressed: () { + onTap(); + }, + 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, + ), + ], + ), + ], + ), + )); + } +} diff --git a/lib/presentation/lab/lab_result_via_hospital/lab_order_specialResult.dart b/lib/presentation/lab/lab_result_via_hospital/lab_order_specialResult.dart new file mode 100644 index 0000000..5f71a7e --- /dev/null +++ b/lib/presentation/lab/lab_result_via_hospital/lab_order_specialResult.dart @@ -0,0 +1,120 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; +import 'package:hmg_patient_app_new/core/utils/size_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/lab/lab_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:provider/provider.dart'; + +class LabOrderSpecialResult extends StatelessWidget { + const LabOrderSpecialResult({super.key}); + + @override + Widget build(BuildContext context) { + return Selector( + selector: (_, model) => model.isSpecialResultsLoading, + builder: (_, isLoading, __) { + return Selector( + selector: (_, model) => model.labSpecialResult, + builder: (_, data, __) { + if(isLoading){ + return Container( + margin: EdgeInsets.symmetric(vertical: 8.h), + padding: EdgeInsets.symmetric( + horizontal: 16.h, vertical: 16.h), + width: MediaQuery.sizeOf(context).width - 24, + decoration: RoundedRectangleBorder() + .toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true), + child:Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 12.h, + children: [ + "loading".toText14().toShimmer2(isShow: isLoading), + "loading".toText14().toShimmer2(isShow: isLoading), + ], + ) + ); + } + if(data.isNotEmpty ) { + 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: Container( + padding: EdgeInsets.symmetric( + horizontal: 16.h, vertical: 16.h), + width: MediaQuery.sizeOf(context).width - 24, + child: Column( + spacing: 8.h, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // ...labOrder!.testDetails!.map((detail) { + LocaleKeys.specialResult + .tr() + .toText14(weight: FontWeight.w500) + .toShimmer2(isShow: isLoading), + + data.isEmpty + ? LocaleKeys.noSpecialResult + .tr() + .toText12( + fontWeight: FontWeight.w500, + color: AppColors.textColorLight) + .toShimmer2(isShow: isLoading) + : HtmlWidget(data).toShimmer2(isShow: isLoading) + + // + ], + ), + )); + } return SizedBox.shrink(); + }); + + }); + } +} + +/*Text( + "Special Results", + style: TextStyle( + fontSize: 18.fSize, + fontWeight: FontWeight.w600, + color: AppColors.blackColor), + ), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true + ), + padding: EdgeInsets.all(16.h), + width: MediaQuery.sizeOf(context).width-24, + child: Selector( + selector: (_, model) => + model.isLabResultByHospitalLoading, + builder: (_, isLoading, __) { + return Selector( + selector: (_, model) => model.labSpecialResult, + builder: (_, data, __) { + return (data.isEmpty) + ? Text("No result available".needTranslation, + style: TextStyle( + fontSize: 12.fSize, + fontWeight: FontWeight.w500, + color: AppColors.textColorLight)) + .toShimmer2(isShow: isLoading) + : HtmlWidget(data) + .toShimmer2(isShow: isLoading); + }); + }))*/ diff --git a/lib/presentation/lab/lab_results/lab_result_calender.dart b/lib/presentation/lab/lab_results/lab_result_calender.dart index b118293..5750fdd 100644 --- a/lib/presentation/lab/lab_results/lab_result_calender.dart +++ b/lib/presentation/lab/lab_results/lab_result_calender.dart @@ -68,7 +68,7 @@ class _LabResultCalenderState extends State { children: [ fromDateComponent(), Text( - "to".needTranslation, + LocaleKeys.to.tr(), style: TextStyle( color: AppColors.calenderTextColor, fontSize: 14.h, @@ -205,7 +205,7 @@ class _LabResultCalenderState extends State { fromDateComponent() { return Consumer( builder: (_, model, __) { - return displayDate("Start Date".needTranslation, + return displayDate(LocaleKeys.startDate.tr(), model.getDateString(model.fromDate), model.fromDate == null); }, ); @@ -214,7 +214,7 @@ class _LabResultCalenderState extends State { toDateComponent() { return Consumer( builder: (_, model, __) { - return displayDate("End Date".needTranslation, + return displayDate(LocaleKeys.endDate.tr(), model.getDateString(model.toDate), model.toDate == null); }, ); diff --git a/lib/presentation/lab/lab_results/lab_result_details.dart b/lib/presentation/lab/lab_results/lab_result_details.dart index e3ffaea..382a846 100644 --- a/lib/presentation/lab/lab_results/lab_result_details.dart +++ b/lib/presentation/lab/lab_results/lab_result_details.dart @@ -13,10 +13,11 @@ import 'package:hmg_patient_app_new/features/lab/history/lab_history_viewmodel.d import 'package:hmg_patient_app_new/features/lab/lab_range_view_model.dart' show LabRangeViewModel; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/lab_result.dart'; -import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_calender.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_list_item.dart'; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart'; import 'package:provider/provider.dart' show Consumer, Provider; @@ -157,7 +158,7 @@ class LabResultDetails extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - model.isGraphVisible?"History FlowChart".needTranslation: "History".needTranslation, + model.isGraphVisible?LocaleKeys.historyFlowchart.tr(): LocaleKeys.history.tr(), style: TextStyle( fontSize: 16, fontFamily: 'Poppins', @@ -183,7 +184,7 @@ class LabResultDetails extends StatelessWidget { height: 24) .onPress(() { showCommonBottomSheetWithoutHeight( - title: "Set The Date Range".needTranslation, + title: LocaleKeys.setTheDateRange.tr(), context, child: LabResultCalender( onRangeSelected: (start, end) { @@ -235,15 +236,17 @@ class LabResultDetails extends StatelessWidget { Widget historyBody(LabRangeViewModel model, LabViewModel labmodel) { if(model.isGraphVisible){ + print("the labmodel.filteredGraphValues.length is ${labmodel.filteredGraphValues.length}"); var graphColor = labmodel.getColor(recentLabResult.calculatedResultFlag??"N"); return CustomGraph( dataPoints: labmodel.filteredGraphValues, // maxY: 100, + makeGraphBasedOnActualValue: true, leftLabelReservedSize: 40, leftLabelInterval: getInterval(labmodel), - maxY: (labmodel.maxY)+(getInterval(labmodel)??0)/2, - + maxY: (labmodel.maxY)+(getInterval(labmodel)??0)/5, + maxX: labmodel.filteredGraphValues.length.toDouble()-.75, leftLabelFormatter: (value) { return leftLabels(value.toStringAsFixed(2).tr()); // switch (value.toInt()) { @@ -263,7 +266,7 @@ class LabResultDetails extends StatelessWidget { // } }, graphColor:graphColor , - graphShadowColor: graphColor.withOpacity(.4), + graphShadowColor: graphColor.withOpacity(.1), graphGridColor: graphColor.withOpacity(.4), bottomLabelFormatter: (value, data) { if(data.isEmpty) return SizedBox.shrink(); diff --git a/lib/splashPage.dart b/lib/splashPage.dart index 3e70742..117db61 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart'; +import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart'; import 'package:hmg_patient_app_new/presentation/onboarding/onboarding_screen.dart'; import 'package:hmg_patient_app_new/presentation/onboarding/splash_animation_screen.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; @@ -48,11 +49,12 @@ class _SplashScreenState extends State { Timer(Duration(seconds: 2, milliseconds: 500), () async { LocalNotification.init(onNotificationClick: (payload) {}); - if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) { - Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); - } else { - Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); - } + // if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) { + // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); + // } else { + // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation()))); + // } + Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LabOrdersPage()))); }); var zoom = ZoomVideoSdk(); InitConfig initConfig = InitConfig( diff --git a/lib/widgets/graph/custom_graph.dart b/lib/widgets/graph/custom_graph.dart index fa72b19..1e0bd60 100644 --- a/lib/widgets/graph/custom_graph.dart +++ b/lib/widgets/graph/custom_graph.dart @@ -236,7 +236,7 @@ class CustomGraph extends StatelessWidget { gradient: LinearGradient( colors: [ graphShadowColor, - Colors.transparent, + Colors.white, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, @@ -244,6 +244,8 @@ class CustomGraph extends StatelessWidget { ), ) ]; + var max = LineChartHelper().calculateMaxAxisValues(data); + print("the maxX is the -------> ${max.$2}"); return data; } diff --git a/pubspec.yaml b/pubspec.yaml index 3bdaa4a..fe114dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,6 +85,7 @@ dependencies: gms_check: ^1.0.4 huawei_location: ^6.14.2+301 intl: ^0.20.2 + flutter_widget_from_html: ^0.17.1 dev_dependencies: flutter_test: