monthly report

pull/152/head
Fatimah.Alshammari 1 month ago
parent 86a91ab383
commit 0b3fea230f

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

@ -826,6 +826,7 @@ class ApiConsts {
static final String removeFileFromFamilyMembers = 'Services/Authentication.svc/REST/ActiveDeactive_PatientFile';
static final String acceptAndRejectFamilyFile = 'Services/Authentication.svc/REST/Update_FileStatus';
static final String getActivePrescriptionsDetails = 'Services/Patients.svc/Rest/GetActivePrescriptionReportByPatientID';
static final String getTermsConditions = 'Services/Patients.svc/Rest/GetUserTermsAndConditions';
// Ancillary Order Apis
static final String getOnlineAncillaryOrderList = 'Services/Doctors.svc/REST/GetOnlineAncillaryOrderList';

@ -1,6 +1,7 @@
class AppAssets {
static const String svgBasePath = 'assets/images/svg';
static const String pngBasePath = 'assets/images/png';
static const String jpgBasePath = 'assets/images/jpg';
static const String hmg = '$svgBasePath/hmg.svg';
static const String arrow_back = '$svgBasePath/arrow-back.svg';
@ -206,6 +207,9 @@ class AppAssets {
static const String dummy_user = '$pngBasePath/dummy_user.png';
static const String comprehensiveCheckupEn = '$pngBasePath/cc_en.png';
static const String comprehensiveCheckupAr = '$pngBasePath/cc_er.png';
// JPGS //
static const String report = '$jpgBasePath/hmg_logo.jpg';
}
class AppAnimations {

@ -55,6 +55,8 @@ import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../features/active_prescriptions/active_prescriptions_repo.dart';
import '../features/terms_conditions/terms_conditions_repo.dart';
import '../features/terms_conditions/terms_conditions_view_model.dart';
GetIt getIt = GetIt.instance;
@ -121,6 +123,10 @@ class AppDependencies {
getIt.registerLazySingleton<ContactUsRepo>(() => ContactUsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<HmgServicesRepo>(() => HmgServicesRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ActivePrescriptionsRepo>(() => ActivePrescriptionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<TermsConditionsRepo>(() => TermsConditionsRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerFactory<TermsConditionsViewModel>(() => TermsConditionsViewModel(termsConditionsRepo: getIt<TermsConditionsRepo>(), errorHandlerService: getIt<ErrorHandlerService>(),
),
);
// ViewModels
// Global/shared VMs LazySingleton

@ -1,165 +1,78 @@
import 'dart:convert';
class ActivePrescriptionsResponseModel {
dynamic address;
int? appointmentNo;
dynamic clinic;
dynamic companyName;
int? days;
dynamic doctorName;
int? doseDailyQuantity; // doses per day
String? itemId;
String? itemDescription;
String? route;
String? frequency;
int? frequencyNumber;
dynamic image;
dynamic imageExtension;
dynamic imageSrcUrl;
String? imageString;
dynamic imageThumbUrl;
dynamic isCovered;
String? itemDescription;
int? itemId;
int? doseDailyQuantity;
int? days;
String? startDate;
String? endDate;
String? orderDate;
int? patientId;
dynamic patientName;
dynamic phoneOffice1;
dynamic prescriptionQr;
dynamic prescriptionTimes;
dynamic productImage;
String? productImageBase64;
String? productImageString;
int? projectId;
dynamic projectName;
dynamic remarks;
String? route;
String? sku;
int? scaleOffset;
String? startDate;
// Added for reminder feature
bool isReminderOn;
List<String?> selectedDoseTimes = [];
bool isReminderOn = false; // toggle status
ActivePrescriptionsResponseModel({
this.address,
this.appointmentNo,
this.clinic,
this.companyName,
this.days,
this.doctorName,
this.doseDailyQuantity,
this.itemId,
this.itemDescription,
this.route,
this.frequency,
this.frequencyNumber,
this.image,
this.imageExtension,
this.imageSrcUrl,
this.imageString,
this.imageThumbUrl,
this.isCovered,
this.itemDescription,
this.itemId,
this.doseDailyQuantity,
this.days,
this.startDate,
this.endDate,
this.orderDate,
this.patientId,
this.patientName,
this.phoneOffice1,
this.prescriptionQr,
this.prescriptionTimes,
this.productImage,
this.productImageBase64,
this.productImageString,
this.projectId,
this.projectName,
this.remarks,
this.route,
this.sku,
this.scaleOffset,
this.startDate,
// Default values for new fields (wont break API)
List<String?>? selectedDoseTimes,
this.isReminderOn = false,
}) : selectedDoseTimes = selectedDoseTimes ?? [];
factory ActivePrescriptionsResponseModel.fromRawJson(String str) =>
ActivePrescriptionsResponseModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory ActivePrescriptionsResponseModel.fromJson(Map<String, dynamic> json) =>
ActivePrescriptionsResponseModel(
address: json["Address"],
appointmentNo: json["AppointmentNo"],
clinic: json["Clinic"],
companyName: json["CompanyName"],
days: json["Days"],
doctorName: json["DoctorName"],
doseDailyQuantity: json["DoseDailyQuantity"],
frequency: json["Frequency"],
frequencyNumber: json["FrequencyNumber"],
image: json["Image"],
imageExtension: json["ImageExtension"],
imageSrcUrl: json["ImageSRCUrl"],
imageString: json["ImageString"],
imageThumbUrl: json["ImageThumbUrl"],
isCovered: json["IsCovered"],
itemDescription: json["ItemDescription"],
itemId: json["ItemID"],
orderDate: json["OrderDate"],
patientId: json["PatientID"],
patientName: json["PatientName"],
phoneOffice1: json["PhoneOffice1"],
prescriptionQr: json["PrescriptionQR"],
prescriptionTimes: json["PrescriptionTimes"],
productImage: json["ProductImage"],
productImageBase64: json["ProductImageBase64"],
productImageString: json["ProductImageString"],
projectId: json["ProjectID"],
projectName: json["ProjectName"],
remarks: json["Remarks"],
route: json["Route"],
sku: json["SKU"],
scaleOffset: json["ScaleOffset"],
startDate: json["StartDate"],
List<String?>? selectedDoseTimes,
}) {
this.selectedDoseTimes = selectedDoseTimes ?? [];
}
// Ensure local reminder values are not overwritten by API
selectedDoseTimes: [],
isReminderOn: false,
);
/// ========== JSON FROM ==========
factory ActivePrescriptionsResponseModel.fromJson(Map<String, dynamic> json) {
return ActivePrescriptionsResponseModel(
itemId: json["ItemID"]?.toString() ?? "",
itemDescription: json["ItemDescription"] ?? "",
route: json["Route"] ?? "",
frequency: json["Frequency"] ?? "",
frequencyNumber: json["FrequencyNumber"],
doseDailyQuantity: json["DoseDailyQuantity"] ?? 1,
days: json["Days"] ?? 0,
startDate: json["StartDate"] ?? "",
endDate: json["EndDate"] ?? "",
orderDate: json["OrderDate"] ?? "",
productImageString: json["ProductImageString"] ?? "",
isReminderOn: json["IsReminderOn"] == true,
selectedDoseTimes:
(json["SelectedDoseTimes"] as List<dynamic>?)
?.map((e) => e?.toString())
.toList() ??
[],
);
}
Map<String, dynamic> toJson() => {
"Address": address,
"AppointmentNo": appointmentNo,
"Clinic": clinic,
"CompanyName": companyName,
"Days": days,
"DoctorName": doctorName,
"DoseDailyQuantity": doseDailyQuantity,
"Frequency": frequency,
"FrequencyNumber": frequencyNumber,
"Image": image,
"ImageExtension": imageExtension,
"ImageSRCUrl": imageSrcUrl,
"ImageString": imageString,
"ImageThumbUrl": imageThumbUrl,
"IsCovered": isCovered,
"ItemDescription": itemDescription,
"ItemID": itemId,
"OrderDate": orderDate,
"PatientID": patientId,
"PatientName": patientName,
"PhoneOffice1": phoneOffice1,
"PrescriptionQR": prescriptionQr,
"PrescriptionTimes": prescriptionTimes,
"ProductImage": productImage,
"ProductImageBase64": productImageBase64,
"ProductImageString": productImageString,
"ProjectID": projectId,
"ProjectName": projectName,
"Remarks": remarks,
"Route": route,
"SKU": sku,
"ScaleOffset": scaleOffset,
"StartDate": startDate,
};
/// ========== JSON TO ==========
Map<String, dynamic> toJson() {
return {
"ItemID": itemId,
"ItemDescription": itemDescription,
"Route": route,
"Frequency": frequency,
"FrequencyNumber": frequencyNumber,
"DoseDailyQuantity": doseDailyQuantity,
"Days": days,
"StartDate": startDate,
"EndDate": endDate,
"OrderDate": orderDate,
"ProductImageString": productImageString,
"IsReminderOn": isReminderOn,
"SelectedDoseTimes": selectedDoseTimes,
};
}
}

@ -10,6 +10,7 @@ class HmgServicesComponentModel {
Color bgColor;
Color textColor;
String route;
bool isExternalLink;
HmgServicesComponentModel(
this.action,
@ -21,5 +22,6 @@ class HmgServicesComponentModel {
this.bgColor = Colors.white,
this.textColor = Colors.black,
this.route = '',
this.isExternalLink = false,
});
}

@ -0,0 +1,60 @@
import 'package:dartz/dartz.dart';
import '../../core/api/api_client.dart';
import '../../core/api_consts.dart';
import '../../core/exceptions/api_failure.dart';
import '../../services/logger_service.dart';
abstract class TermsConditionsRepo {
Future<Either<Failure, String>> getTermsConditions();
}
class TermsConditionsRepoImp implements TermsConditionsRepo {
final ApiClient apiClient;
final LoggerService loggerService;
TermsConditionsRepoImp({
required this.loggerService,
required this.apiClient,
});
@override
Future<Either<Failure, String>> getTermsConditions() async {
Failure? failure;
String? html;
try {
await apiClient.post(
ApiConsts.getTermsConditions,
body: <String, dynamic>{},
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType ?? ServerFailure(error.toString());
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final content = response['UserAgreementContent'];
if (content is String && content.isNotEmpty) {
html = content;
} else {
failure = DataParsingFailure(
'UserAgreementContent is null or not String');
}
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
} catch (e) {
failure = UnknownFailure(e.toString());
}
if (failure != null) return Left(failure!);
if (html == null || html!.isEmpty) {
return Left(ServerFailure('No terms and conditions returned'));
}
return Right(html!);
}
}

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/terms_conditions/terms_conditions_repo.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class TermsConditionsViewModel extends ChangeNotifier {
final TermsConditionsRepo termsConditionsRepo;
final ErrorHandlerService errorHandlerService;
String? termsConditionsHtml;
bool isLoading = false;
TermsConditionsViewModel({
required this.termsConditionsRepo,
required this.errorHandlerService,
});
Future<void> getTermsConditions({
Function()? onSuccess,
Function(String)? onError,
}) async {
isLoading = true;
notifyListeners();
final result = await termsConditionsRepo.getTermsConditions();
result.fold(
(failure) async {
await errorHandlerService.handleError(failure: failure);
isLoading = false;
notifyListeners();
if (onError != null) {
onError(failure.message ?? 'Something went wrong');
}
},
(html) {
termsConditionsHtml = html;
isLoading = false;
notifyListeners();
if (onSuccess != null) onSuccess();
},
);
}
}

@ -38,6 +38,7 @@ import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'core/utils/size_utils.dart';
import 'features/terms_conditions/terms_conditions_view_model.dart';
import 'firebase_options.dart';
@pragma('vm:entry-point')
@ -146,9 +147,12 @@ void main() async {
),
ChangeNotifierProvider<HmgServicesViewModel>(
create: (_) => getIt.get<HmgServicesViewModel>(),
)
),
ChangeNotifierProvider<ActivePrescriptionsViewModel>(
create: (_) => getIt.get<ActivePrescriptionsViewModel>(),
),
ChangeNotifierProvider<TermsConditionsViewModel>(
create: (_) => getIt.get<TermsConditionsViewModel>(),
)
], child: MyApp()),
),

@ -547,7 +547,7 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
AppColors.textGreenColor,
AppColors.infoColor,
AppColors.labelColorYellow,
AppColors.purpleBg
AppColors.mainPurple
][doseIndex % 4];
final doseLabel =
"${doseIndex + 1}${_getSuffix(doseIndex + 1)}";

@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_s
import 'package:hmg_patient_app_new/presentation/hmg_services/services_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:url_launcher/url_launcher.dart';
class ServicesPage extends StatelessWidget {
ServicesPage({super.key});
@ -41,8 +42,30 @@ class ServicesPage extends StatelessWidget {
textColor: AppColors.blackColor,
route: AppRoutes.homeHealthCarePage,
),
HmgServicesComponentModel(
12,
"Latest News".needTranslation,
"".needTranslation,
AppAssets.news,
true,
bgColor: AppColors.bgGreenColor,
textColor: AppColors.blackColor,
route: "https://twitter.com/HMG",
isExternalLink: true,
),
HmgServicesComponentModel(
12,
"Monthly Reports".needTranslation,
"".needTranslation,
AppAssets.report_icon,
true,
bgColor: AppColors.bgGreenColor,
textColor: AppColors.blackColor,
route: AppRoutes.monthlyReports,
),
];
@override
Widget build(BuildContext context) {
return CollapsingListView(
@ -72,7 +95,7 @@ class ServicesPage extends StatelessWidget {
return ServiceGridViewItem(hmgServices[index], index, false);
},
),
)
),
],
),
),

@ -5,6 +5,7 @@ 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/hmg_services/models/ui_models/hmg_services_component_model.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:url_launcher/url_launcher.dart';
class ServiceGridViewItem extends StatelessWidget {
final HmgServicesComponentModel hmgServiceComponentModel;
@ -12,12 +13,18 @@ class ServiceGridViewItem extends StatelessWidget {
final bool isHomePage;
final bool isLocked;
const ServiceGridViewItem(this.hmgServiceComponentModel, this.index, this.isHomePage, {super.key, this.isLocked = false});
const ServiceGridViewItem(
this.hmgServiceComponentModel, this.index, this.isHomePage,
{super.key, this.isLocked = false});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () => getIt.get<NavigationService>().pushPageRoute(hmgServiceComponentModel.route),
onTap: () => hmgServiceComponentModel.isExternalLink
? _openLink(hmgServiceComponentModel.route)
: getIt
.get<NavigationService>()
.pushPageRoute(hmgServiceComponentModel.route),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
@ -47,4 +54,14 @@ class ServiceGridViewItem extends StatelessWidget {
],
));
}
Future<void> _openLink(String link) async {
final Uri url = Uri.parse(link);
if (await canLaunchUrl(url)) {
await launchUrl(url, mode: LaunchMode.externalApplication);
} else {
throw "Could not launch $url";
}
}
}

@ -0,0 +1,283 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/presentation/monthly_reports/user_agreement_page.dart';
import '../../generated/locale_keys.g.dart';
import '../../theme/colors.dart';
import '../../widgets/appbar/app_bar_widget.dart';
import '../../widgets/input_widget.dart';
class MonthlyReportsPage extends StatefulWidget {
const MonthlyReportsPage({super.key});
@override
State<MonthlyReportsPage> createState() => _MonthlyReportsPageState();
}
class _MonthlyReportsPageState extends State<MonthlyReportsPage> {
bool isHealthSummaryEnabled = false;
bool isTermsAccepted = false;
final TextEditingController emailController = TextEditingController();
@override
void dispose() {
emailController.dispose();
super.dispose();
}
void _showError(String message) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
),
);
}
void _onSavePressed() {
if (!isTermsAccepted) {
_showError("Please accept the terms and conditions".needTranslation);
return;
}
final email = emailController.text.trim();
if (email.isEmpty) {
_showError("Please enter your email".needTranslation);
return;
}
setState(() {
isHealthSummaryEnabled = true;
});
// TODO: هنا حطي API/logic حق الحفظ
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar(
onBackPressed: () => Navigator.of(context).pop(),
onLanguageChanged: (_) {},
hideLogoAndLang: true,
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Monthly Reports".needTranslation,
style: TextStyle(
color: AppColors.textColor,
fontSize: 27.f,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 16.h),
Container(
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
height: 54.h,
alignment: Alignment.center,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: (12.r),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Patient Health Summary Report".needTranslation,
style: TextStyle(
color: AppColors.textColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
),
),
_buildToggle(),
],
),
),
SizedBox(height: 16.h),
TextInputWidget(
controller: emailController,
labelText: "Eamil*".needTranslation,
hintText: "email@email.com",
isEnable: true,
prefix: null,
isAllowRadius: true,
isBorderAllowed: false,
isAllowLeadingIcon: true,
autoFocus: true,
keyboardType: TextInputType.emailAddress,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
onChange: (value) {
setState(() {});
},
).paddingOnly(top: 8.h, bottom: 8.h),
Row(
children: [
Text(
"To View The Terms and Conditions".needTranslation,
style: TextStyle(
color: AppColors.textColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
),
),
InkWell(
child: Text(
"Click here".needTranslation,
style: TextStyle(
color: AppColors.errorColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const UserAgreementPage(),
),
);
},
),
],
),
SizedBox(height: 12.h),
GestureDetector(
onTap: () => setState(() => isTermsAccepted = !isTermsAccepted),
child: Row(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: 24.h,
width: 24.h,
decoration: BoxDecoration(
color: isTermsAccepted
? AppColors.textGreenColor
: Colors.transparent,
borderRadius: BorderRadius.circular(6),
border: Border.all(
color: isTermsAccepted
? AppColors.lightGreenColor
: AppColors.greyColor,
width: 2.h,
),
),
child: isTermsAccepted
? Icon(Icons.check, size: 16.f, color: Colors.white)
: null,
),
SizedBox(width: 12.h),
Text(
"I agree to the terms and conditions".needTranslation,
style: context.dynamicTextStyle(
fontSize: 12.f,
fontWeight: FontWeight.w500,
color: const Color(0xFF2E3039),
),
),
],
),
),
SizedBox(height: 12.h),
Text(
"This is monthly health summary report".needTranslation,
style: TextStyle(
color: AppColors.textColor,
fontSize: 10.f,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 12.h),
Image.asset('assets/images/jpg/report.jpg'),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.successColor,
foregroundColor: AppColors.whiteColor,
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: _onSavePressed,
child: Text(
LocaleKeys.save.tr(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.f,
),
),
),
),
],
),
],
),
).paddingAll(16),
);
}
Widget _buildToggle() {
final value = isHealthSummaryEnabled;
return AbsorbPointer(
absorbing: true,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: 50.h,
height: 28.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: value
? AppColors.lightGreenColor
: AppColors.greyColor.withOpacity(0.3),
),
child: AnimatedAlign(
duration: const Duration(milliseconds: 200),
alignment: value ? Alignment.centerRight : Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(3),
child: Container(
width: 22.h,
height: 22.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: value
? AppColors.textGreenColor
: AppColors.greyTextColor,
),
),
),
),
),
);
}
}

@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/terms_conditions/terms_conditions_view_model.dart';
import 'package:provider/provider.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../theme/colors.dart';
import '../../widgets/appbar/app_bar_widget.dart';
class UserAgreementPage extends StatefulWidget {
const UserAgreementPage({super.key});
@override
State<UserAgreementPage> createState() => _UserAgreementPageState();
}
class _UserAgreementPageState extends State<UserAgreementPage> {
late final WebViewController _webViewController;
bool _isLoading = true;
String? _errorMessage;
@override
void initState() {
super.initState();
_webViewController = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (_) {
setState(() {
_isLoading = true;
});
},
onPageFinished: (_) {
setState(() {
_isLoading = false;
});
},
onWebResourceError: (error) {
},
),
);
WidgetsBinding.instance.addPostFrameCallback((_) {
final vm =
Provider.of<TermsConditionsViewModel>(context, listen: false);
vm.getTermsConditions(
onSuccess: () {
final htmlString = vm.termsConditionsHtml ?? '';
if (htmlString.isNotEmpty) {
setState(() {
_errorMessage = null;
_isLoading = true;
});
_webViewController.loadHtmlString(htmlString);
} else {
setState(() {
_isLoading = false;
_errorMessage = 'لا توجد شروط متاحة حالياً'.needTranslation;
});
}
},
onError: (msg) {
setState(() {
_isLoading = false;
_errorMessage = msg;
});
},
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar(
onBackPressed: () => Navigator.of(context).pop(),
onLanguageChanged: (_) {},
hideLogoAndLang: true,
),
body: Stack(
children: [
WebViewWidget(controller: _webViewController),
if (_errorMessage != null)
Center(
child: Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(8),
),
child: Text(
_errorMessage!,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.red,
fontWeight: FontWeight.w600,
),
),
),
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
),
],
),
);
}
}

@ -9,6 +9,8 @@ import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/splashPage.dart';
import '../presentation/monthly_reports/monthly_reports_page.dart';
class AppRoutes {
static const String initialRoute = '/initialRoute';
static const String loginScreen = '/loginScreen';
@ -19,7 +21,7 @@ class AppRoutes {
static const String eReferralPage = '/erReferralPage';
static const String comprehensiveCheckupPage = '/comprehensiveCheckupPage';
static const String homeHealthCarePage = '/homeHealthCarePage';
static const String monthlyReports = '/monthlyReportsPage';
static Map<String, WidgetBuilder> get routes => {
initialRoute: (context) => SplashPage(),
loginScreen: (context) => LoginScreen(),
@ -29,6 +31,7 @@ class AppRoutes {
medicalFilePage: (context) => MedicalFilePage(),
eReferralPage: (context) => EReferralPage(),
comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(),
homeHealthCarePage: (context) => HhcProceduresPage()
homeHealthCarePage: (context) => HhcProceduresPage(),
monthlyReports: (context) => MonthlyReportsPage()
};
}

@ -79,7 +79,7 @@ dependencies:
path_provider: ^2.0.8
open_filex: ^4.7.0
flutter_swiper_view: ^1.1.8
webview_flutter: ^4.9.0
location: ^8.0.1
gms_check: ^1.0.4
huawei_location: ^6.14.2+301

Loading…
Cancel
Save