Updated the dialogs to toast

master
FaizHashmi 3 weeks ago
parent 623957be1a
commit e05e97a536

@ -802,6 +802,10 @@
"loginMethod": "طريقة تسجيل الدخول",
"nationalIdAndDob": "الهوية الوطنية وتاريخ الميلاد",
"sponsorOtp": "رمز التحقق للكفيل",
"revokingPreventsAccessDesc": "إلغاء الوصول يمنع التابع من استخدام المزايا حتى تعيد السماح به مرة أخرى."
"revokingPreventsAccessDesc": "إلغاء الوصول يمنع التابع من استخدام المزايا حتى تعيد السماح به مرة أخرى.",
"locationPermissionRequiredForAttendance": "يجب منح إذن الموقع لتسجيل الحضور",
"locationServicesRequiredForAttendance": "يجب تفعيل خدمة الموقع لتسجيل الحضور",
"pleaseProvideLocationPermission": "الرجاء منح إذن الموقع",
"microphoneAccessRequired": "يحتاج التطبيق إلى إذن الوصول إلى الميكروفون لتسجيل الصوت."
}

@ -802,7 +802,11 @@
"loginMethod": "Login method",
"nationalIdAndDob": "National ID & Date of Birth",
"sponsorOtp": "Sponsor OTP",
"revokingPreventsAccessDesc": "Revoking prevents all future dependent access until you allow it again."
"revokingPreventsAccessDesc": "Revoking prevents all future dependent access until you allow it again.",
"locationPermissionRequiredForAttendance": "You need to give location permission to mark attendance",
"locationServicesRequiredForAttendance": "You need to enable location services to mark attendance",
"pleaseProvideLocationPermission": "Please provide location permission",
"microphoneAccessRequired": "The app needs microphone access to be able to record audio."
}

@ -7,6 +7,7 @@ import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/models/marathon/marathon_generic_model.dart';
import 'package:mohem_flutter_app/models/marathon/marathon_model.dart';
@ -114,10 +115,10 @@ class MarathonApiClient {
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 404 || marathonModel.statusCode == 208 || marathonModel.statusCode == 204 || marathonModel.statusCode == 500) {
Utils.confirmDialog(
AppRoutes.navigatorKey.currentContext,
AppToast.show(
marathonModel.message!,
onTap: () {
seconds: AppToast.actionSeconds,
onComplete: () {
AppRoutes.navigatorKey.currentContext!.read<MarathonProvider>().resetValues();
Navigator.of(
AppRoutes.navigatorKey.currentContext!,

@ -1,5 +1,5 @@
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
@ -18,20 +18,18 @@ class AppPermissions {
}
static void showErrorLocationDialog(bool isPermissionError, BuildContext context) {
showDialog(
context: context,
builder: (cxt) => ConfirmDialog(
message: "Please provide location permission",
onTap: () {
if (isPermissionError) {
Geolocator.openAppSettings();
} else {
Geolocator.openLocationSettings();
}
Navigator.pop(context);
// createVacationRule(list);
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.pleaseProvideLocationPermission.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () {
if (isPermissionError) {
Geolocator.openAppSettings();
} else {
Geolocator.openLocationSettings();
}
},
);
}

@ -4,12 +4,12 @@ class ApiConsts {
// static String baseUrl = "http://10.201.214.101:2027"; // Local server
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server
// static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
// static String baseUrl = "http://10.201.204.101:2024";
// static String baseUrl = "https://webservices.hmg.com"; // PreProd
// static String baseUrl = "https://hmgwebservices.com"; // Live server
static String baseUrl = "https://mohemm.hmg.com"; // New Live server
// static String baseUrl = "https://mohemm.hmg.com"; // New Live server
static String ssoBaseUrl = "https://sso.hmg.com"; // New Live server
//
// static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver

@ -6,7 +6,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
@ -19,6 +18,7 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/loading_dialog.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:nfc_manager/platform_tags.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -31,16 +31,10 @@ class Utils {
static bool get isLoading => _isLoadingVisible;
// longDuration is accepted but no longer varies the duration - every caller already used the
// default (true), so AppToast.plainSeconds covers both cases with one fixed value.
static void showToast(String message, {bool longDuration = true}) {
Fluttertoast.showToast(
msg: message,
toastLength: longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Colors.black54,
textColor: Colors.white,
fontSize: 13.0,
);
AppToast.show(message, seconds: AppToast.plainSeconds, showIcon: false, showCountdownText: false);
}
static dynamic getNotNullValue(List<dynamic> list, int index) {
@ -118,11 +112,7 @@ class Utils {
lower.contains("e4 :")) {
return true;
}
if (str.contains("طلب منح") ||
str.contains("منح نشط") ||
str.contains("لا يوجد طلب منح") ||
str.contains("تم إلغاء وصول مزايا") ||
str.contains("إلغاء مزايا")) {
if (str.contains("طلب منح") || str.contains("منح نشط") || str.contains("لا يوجد طلب منح") || str.contains("تم إلغاء وصول مزايا") || str.contains("إلغاء مزايا")) {
return true;
}
return false;
@ -135,15 +125,7 @@ class Utils {
depName = AppState().walaplusDepInfoModel?.pDependentName ?? "";
}
if (cxt != null) {
Navigator.push(
cxt,
MaterialPageRoute(
builder: (context) => MazayaAccessResultScreen(
isGranted: false,
dependentName: depName,
),
),
);
Navigator.push(cxt, MaterialPageRoute(builder: (context) => MazayaAccessResultScreen(isGranted: false, dependentName: depName)));
return;
}
}
@ -165,17 +147,12 @@ class Utils {
onErrorMessage(errorMessage);
} else {
if (!AppState().isAuthenticated) {
showDialog(
barrierDismissible: false,
context: cxt,
builder:
(cxt) => ConfirmDialog(
message: errorMessage,
onTap: () {
Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.login, (Route<dynamic> route) => false);
},
onCloseTap: () {},
),
AppToast.show(
errorMessage,
seconds: AppToast.actionSeconds,
onComplete: () {
Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.login, (Route<dynamic> route) => false);
},
);
} else {
if (cxt != null) {
@ -193,7 +170,13 @@ class Utils {
}
static void confirmDialog(cxt, String message, {VoidCallback? onTap}) {
showDialog(context: cxt, builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap));
// No onTap means there's nothing to confirm - it's a plain notice, so a self-dismissing
// toast replaces the old "OK just closes" dialog instead of requiring a tap.
if (onTap == null) {
AppToast.show(message);
} else {
showDialog(context: cxt, builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap));
}
}
static Widget getNoDataWidget(BuildContext context) {
@ -448,9 +431,7 @@ class Utils {
if (chatData != null) {
try {
chatData.disposeData();
} catch (e) {
}
} catch (e) {}
}
// SharedPreferences prefs = await SharedPreferences.getInstance();
// await prefs.clear();

@ -814,7 +814,11 @@ class CodegenLoader extends AssetLoader{
"loginMethod": "طريقة تسجيل الدخول",
"nationalIdAndDob": "الهوية الوطنية وتاريخ الميلاد",
"sponsorOtp": "رمز التحقق للكفيل",
"revokingPreventsAccessDesc": "إلغاء الوصول يمنع التابع من استخدام المزايا حتى تعيد السماح به مرة أخرى."
"revokingPreventsAccessDesc": "إلغاء الوصول يمنع التابع من استخدام المزايا حتى تعيد السماح به مرة أخرى.",
"locationPermissionRequiredForAttendance": "يجب منح إذن الموقع لتسجيل الحضور",
"locationServicesRequiredForAttendance": "يجب تفعيل خدمة الموقع لتسجيل الحضور",
"pleaseProvideLocationPermission": "الرجاء منح إذن الموقع",
"microphoneAccessRequired": "يحتاج التطبيق إلى إذن الوصول إلى الميكروفون لتسجيل الصوت."
};
static const Map<String,dynamic> _en_US = {
"mohemm": "Mohemm",
@ -1616,7 +1620,11 @@ static const Map<String,dynamic> _en_US = {
"loginMethod": "Login method",
"nationalIdAndDob": "National ID & Date of Birth",
"sponsorOtp": "Sponsor OTP",
"revokingPreventsAccessDesc": "Revoking prevents all future dependent access until you allow it again."
"revokingPreventsAccessDesc": "Revoking prevents all future dependent access until you allow it again.",
"locationPermissionRequiredForAttendance": "You need to give location permission to mark attendance",
"locationServicesRequiredForAttendance": "You need to enable location services to mark attendance",
"pleaseProvideLocationPermission": "Please provide location permission",
"microphoneAccessRequired": "The app needs microphone access to be able to record audio."
};
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US};
}

@ -787,5 +787,9 @@ abstract class LocaleKeys {
static const nationalIdAndDob = 'nationalIdAndDob';
static const sponsorOtp = 'sponsorOtp';
static const revokingPreventsAccessDesc = 'revokingPreventsAccessDesc';
static const locationPermissionRequiredForAttendance = 'locationPermissionRequiredForAttendance';
static const locationServicesRequiredForAttendance = 'locationServicesRequiredForAttendance';
static const pleaseProvideLocationPermission = 'pleaseProvideLocationPermission';
static const microphoneAccessRequired = 'microphoneAccessRequired';
}

@ -16,6 +16,8 @@ import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/encryption.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart';
@ -1638,20 +1640,22 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
void startRecoding(BuildContext context) async {
await Permission.microphone.request().then((PermissionStatus status) {
if (status.isPermanentlyDenied) {
Utils.confirmDialog(
ConfirmBottomSheet.show(
context,
"The app needs microphone access to be able to record audio.",
onTap: () {
Navigator.of(context).pop();
message: LocaleKeys.microphoneAccessRequired.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () {
openAppSettings();
},
);
} else if (status.isDenied) {
Utils.confirmDialog(
ConfirmBottomSheet.show(
context,
"The app needs microphone access to be able to record audio.",
onTap: () {
Navigator.of(context).pop();
message: LocaleKeys.microphoneAccessRequired.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () {
openAppSettings();
},
);

@ -27,7 +27,7 @@ import 'package:mohem_flutter_app/models/greetings/greeting_card_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_response_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/models/sso_auth_model.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool
// ignore: prefer_mixin
@ -190,15 +190,12 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
Utils.handleException(ex, context, (err) {
Utils.hideLoading(context);
showDialog(
context: context,
builder:
(cxt) => ConfirmDialog(
message: err,
onTap: () {
Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.login, (Route<dynamic> route) => false);
},
),
AppToast.show(
err,
seconds: AppToast.actionSeconds,
onComplete: () {
Navigator.pushNamedAndRemoveUntil(context, AppRoutes.login, (Route<dynamic> route) => false);
},
);
});
}

@ -24,7 +24,7 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart';
import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart';
@ -490,15 +490,14 @@ class _AddVacationRuleScreenState extends State<AddVacationRuleScreen> {
}
}
showDialog(
context: context,
builder: (cxt) => ConfirmDialog(
message: LocaleKeys.areYouSureYouWantToSubmit.tr(),
onTap: () {
Navigator.pop(context);
createVacationRule(list);
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.areYouSureYouWantToSubmit.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
createVacationRule(list);
},
);
},
).insideContainer,

@ -19,6 +19,7 @@ import 'package:mohem_flutter_app/ui/chat/create_group.dart';
import 'package:mohem_flutter_app/ui/chat/group_chat_detaied_screen.dart';
import 'package:mohem_flutter_app/ui/chat/manage_group.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:provider/provider.dart';
@ -269,11 +270,13 @@ void goToSelected(GroupResponse? groupDetails, ChatProviderModel m, String value
void deleteGroup(
GroupResponse? groupDetails, ChatProviderModel m, BuildContext context) {
groupDetails!.groupUserList;
Utils.confirmDialog(
ConfirmBottomSheet.show(
context,
LocaleKeys.areYouSureWantTodelete.tr(),
onTap: () {
Navigator.pop(context);
message: LocaleKeys.areYouSureWantTodelete.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
isDestructive: true,
onConfirm: () {
m.deleteGroup(groupDetails);
},
);

@ -14,6 +14,7 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
import 'package:provider/provider.dart';
class EtqanOvrCreateRequest extends StatefulWidget {
@ -87,23 +88,11 @@ class _EtqanOvrCreateRequestState extends State<EtqanOvrCreateRequest> {
DefaultButton(LocaleKeys.submit.tr(), () async {
provider.setDescription(_descriptionController.text);
if (provider.selectedProject == null) {
Utils.showErrorDialog(
context: context,
message: LocaleKeys.pleaseSelectProject.tr(),
onOkTapped: () {
Navigator.of(context).pop();
},
);
AppToast.show(LocaleKeys.pleaseSelectProject.tr());
return;
}
if (!provider.isDescriptionValid()) {
Utils.showErrorDialog(
context: context,
message: LocaleKeys.pleaseEnterDescription.tr(),
onOkTapped: () {
Navigator.of(context).pop();
},
);
AppToast.show(LocaleKeys.pleaseEnterDescription.tr());
return;
}
Map<String, dynamic>? ticketInfo = await provider.submitRequest(context);
@ -112,15 +101,10 @@ class _EtqanOvrCreateRequestState extends State<EtqanOvrCreateRequest> {
String ticketId = ticketInfo['id'] ?? '';
// String successMessage = '${LocaleKeys.requestCreatedSuccessfully.tr()}\n\n${LocaleKeys.ticketNumber.tr()}: $ticketNumber\n${LocaleKeys.ticketId.tr()}: $ticketId';
String successMessage = '${LocaleKeys.requestCreatedSuccessfully.tr()}\n\n${LocaleKeys.ticketNumber.tr()}: $ticketNumber';
Utils.showErrorDialog(
context: context,
message: successMessage,
onOkTapped: () {
Navigator.pop(context);
Navigator.pop(context);
// Navigator.popAndPushNamed(context, AppRoutes.etqanOvr);
},
onCloseTap: () {
AppToast.show(
successMessage,
seconds: AppToast.actionSeconds,
onComplete: () {
Navigator.pop(context);
Navigator.pop(context);
// Navigator.popAndPushNamed(context, AppRoutes.etqanOvr);

@ -15,6 +15,7 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/info_chip_widget.dart';
import 'package:shimmer/shimmer.dart';
@ -346,10 +347,13 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
}
void _performLogout() {
Utils.confirmDialog(
ConfirmBottomSheet.show(
context,
LocaleKeys.areYouSureWantToLogOut.tr(),
onTap: () {
message: LocaleKeys.areYouSureWantToLogOut.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
isDestructive: true,
onConfirm: () {
ApiConsts.selectedMemberType = MemberType.employee;
ApiConsts.ISDependetLogin = false;
AppState().setPostParamsInitConfig();
@ -391,7 +395,7 @@ class _DashboardScreenForDependetState extends State<DashboardScreenForDependet>
child: Icon(isAr ? Icons.arrow_forward_ios_rounded : Icons.arrow_back_ios_new_rounded, size: 20, color: const Color(0xFF1E293B)),
),
),
const SizedBox(width: 8),
const SizedBox(width: 8) ,
SvgPicture.asset("assets/icons/mazaya_brand.svg", height: 48, fit: BoxFit.contain),
],
),

@ -143,22 +143,23 @@ class _WalaPlusRetailerDetailsScreenState extends State<WalaPlusRetailerDetailsS
Future<void> _openInAppBrowser(String url, {String? offerType}) async {
String title = _retailer?.name ?? LocaleKeys.offerDetails.tr();
bool shouldStartScanning = offerType?.trim().toLowerCase() == "discount";
// Offer types seen so far: "promo", "discount", "unique_promo", "promo_unique_qr" (and
// possibly others) - any type that mentions "discount" or "qr" needs the in-page scanner,
// regardless of exact spelling/casing, so check by substring rather than exact match.
String normalizedOfferType = offerType?.trim().toLowerCase() ?? "";
bool shouldStartScanning = normalizedOfferType.contains("discount") || normalizedOfferType.contains("qr");
log("shouldStartScanning: ${shouldStartScanning.toString()} for offerType: ${offerType.toString()}");
// Ask for camera permission app-side, before the WebView (and the page's own
// getUserMedia() call) even exists. By the time the page asks, the OS decision is
// already made, so onPermissionRequest can answer instantly - no mid-flight dialog
// racing the page's own timeout for its original getUserMedia() call.
// Discount (QR) offers need the camera to work at all, so if the user denies here,
// don't open the transaction URL - promo offers never reach this check.
// QR/discount-style offers need the camera to work at all, so if the user denies here,
// don't open the transaction URL - plain promo offers never reach this check.
if (shouldStartScanning) {
try {
PermissionStatus status = await Permission.camera.status;
log("permission status: ${status.toString()}");
// Permanently denied: Permission.camera.request() won't show a dialog at all in this
// state (Android just returns denied silently) - only the app settings screen can fix it.
if (status.isPermanentlyDenied) {
_showCameraPermissionSheet(LocaleKeys.cameraPermissionPermanentlyDenied.tr());
return;
@ -219,7 +220,8 @@ class _WalaPlusRetailerDetailsScreenState extends State<WalaPlusRetailerDetailsS
// This can be injected more than once (e.g. once on page load, again once the native camera
// permission result comes back) - state lives on `window`, not in the closure, so a repeat
// call never starts a second observer or spams clicks.
// Only called for offerType "discount" - "promo" offers just show a promo code, no scanner.
// Only called for offer types mentioning "discount" or "qr" - plain promo offers just show a
// promo code, no scanner.
void _clickStartScanningButton(InAppWebViewController controller) {
controller.evaluateJavascript(
source: r"""

@ -18,6 +18,8 @@ import 'package:mohem_flutter_app/classes/notifications.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/dialogs/otp_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
@ -372,19 +374,21 @@ class _LoginScreenState extends State<LoginScreen> {
print(ex.toString());
Utils.hideLoading(context);
Utils.handleException(ex, context, (msg) {
Utils.confirmDialog(
context,
msg,
onTap: () async {
if (msg.toLowerCase().contains("password has expired")) {
Navigator.pop(context);
// await Navigator.pushNamed(context, AppRoutes.newPassword, arguments: username.text);
if (msg.toLowerCase().contains("password has expired")) {
// Sends an OTP/activation code via performForgotPassword() - requires an explicit
// tap, not an auto-firing toast, since it's a real side effect, not just navigation.
ConfirmBottomSheet.show(
context,
message: msg,
confirmText: LocaleKeys.forgotPassword.tr(),
showCancelButton: false,
onConfirm: () {
performForgotPassword();
} else {
Navigator.pop(context);
}
},
);
},
);
} else {
AppToast.show(msg);
}
});
}
}

@ -23,7 +23,7 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card_builder.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:provider/provider.dart';
class MarathonScreen extends StatelessWidget {
@ -299,16 +299,16 @@ class MarathonScreen extends StatelessWidget {
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
} else {
showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
message: LocaleKeys.youWantToLeaveMarathon.tr(),
onTap: () {
provider.resetValues();
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.youWantToLeaveMarathon.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
provider.resetValues();
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
);
}
},
@ -318,15 +318,15 @@ class MarathonScreen extends StatelessWidget {
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
} else {
showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
message: LocaleKeys.youWantToLeaveMarathon.tr(),
onTap: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.youWantToLeaveMarathon.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
);
}
},
@ -390,15 +390,15 @@ class MarathonScreen extends StatelessWidget {
),
),
onWillPop: () {
showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
message: LocaleKeys.youWantToLeaveMarathon.tr(),
onTap: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.youWantToLeaveMarathon.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
);
return Future<bool>.value(false);
},

@ -10,7 +10,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:provider/provider.dart';
class MarathonWaitingScreen extends StatelessWidget {
@ -21,15 +21,15 @@ class MarathonWaitingScreen extends StatelessWidget {
MarathonProvider provider = context.watch<MarathonProvider>();
return WillPopScope(
onWillPop: () {
showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
message: LocaleKeys.youWantToLeaveMarathon.tr(),
onTap: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.youWantToLeaveMarathon.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
);
return Future<bool>.value(false);
},
@ -38,27 +38,27 @@ class MarathonWaitingScreen extends StatelessWidget {
context,
title: LocaleKeys.brainMarathon.tr(),
onHomeTapped: () {
showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
message: LocaleKeys.youWantToLeaveMarathon.tr(),
onTap: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.youWantToLeaveMarathon.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
);
},
onBackTapped: () {
showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
message: LocaleKeys.youWantToLeaveMarathon.tr(),
onTap: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.youWantToLeaveMarathon.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
provider.resetValues();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
},
);
},
),

@ -21,7 +21,7 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/button/simple_button.dart';
import 'package:mohem_flutter_app/widgets/circular_avatar.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:mohem_flutter_app/widgets/input_widget.dart';
@ -216,15 +216,14 @@ class _RequestSubmitScreenState extends State<RequestSubmitScreen> {
return;
}
showDialog(
context: context,
builder: (cxt) => ConfirmDialog(
message: LocaleKeys.areYouSureYouWantToSubmit.tr(),
onTap: () {
Navigator.pop(context);
submitRequest();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.areYouSureYouWantToSubmit.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
submitRequest();
},
);
},
).insideContainer,

@ -18,6 +18,7 @@ import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.
import 'package:mohem_flutter_app/models/profile_menu.model.dart';
import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart';
import 'package:mohem_flutter_app/models/worklist/replacement_list_model.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/circular_avatar.dart';
import 'package:url_launcher/url_launcher.dart';
@ -217,19 +218,19 @@ class _EmployeeDetailsState extends State<EmployeeDetails> {
}
void showFavoriteAlertDialog(BuildContext context) {
Widget cancelButton = TextButton(
child: Text(
LocaleKeys.cancel.tr(),
),
onPressed: () {
Navigator.pop(context);
},
);
Widget continueButton = TextButton(
child: Text(
LocaleKeys.ok.tr(),
),
onPressed: () {
ConfirmBottomSheet.show(
context,
message: LocaleKeys.addFavoriteList.tr(namedArgs: <String, String>{'name': '${getEmployeeSubordinates!.eMPLOYEENAME}'}),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
avatar: getEmployeeSubordinates!.eMPLOYEEIMAGE == null
? SvgPicture.asset("assets/images/user.svg", height: 68, width: 68)
: CircleAvatar(
radius: 68,
backgroundImage: MemoryImage(Utils.dataFromBase64String(getEmployeeSubordinates!.eMPLOYEEIMAGE!)),
backgroundColor: Colors.black,
),
onConfirm: () {
fetchChangeFav(
email: getEmployeeSubordinates?.eMPLOYEEEMAILADDRESS ?? "",
employeName: getEmployeeSubordinates!.eMPLOYEENAME ?? "",
@ -238,54 +239,6 @@ class _EmployeeDetailsState extends State<EmployeeDetails> {
isFav: true,
);
setState(() {});
Navigator.pop(context);
},
);
AlertDialog alert = AlertDialog(
title: Text(
LocaleKeys.confirm.tr(),
),
content: Container(
height: 150,
child: Column(
children: [
Text(LocaleKeys.addFavoriteList).tr(namedArgs: {'name': '${getEmployeeSubordinates!.eMPLOYEENAME}'}),
getEmployeeSubordinates!.eMPLOYEEIMAGE == null
? SvgPicture.asset(
"assets/images/user.svg",
height: 68,
width: 68,
)
: CircleAvatar(
radius: 68,
backgroundImage: MemoryImage(Utils.dataFromBase64String(getEmployeeSubordinates!.eMPLOYEEIMAGE!)),
backgroundColor: Colors.black,
),
// Image.memory(
// Utils.getPostBytes(
// getEmployeeSubordinates!.eMPLOYEEIMAGE ?? "",
// ),
// errorBuilder: (BuildContext context, error, stackTrace) {
// return SvgPicture.asset(
// "assets/images/user.svg", height: 50, width: 50,
// );
// },
// width: 50,
// height: 50,
// fit: BoxFit.cover,
// ).circle(50).paddingOnly(top: 21),
],
),
),
actions: [
cancelButton,
continueButton,
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}

@ -533,28 +533,27 @@ class _FamilyMembersState extends State<FamilyMembers> {
}
void showUpdateAlertDialog(BuildContext context, int relationId, int flag, String actionType) {
Widget cancelButton = TextButton(child: LocaleKeys.cancel.tr().toText14(), onPressed: () => Navigator.pop(context));
Widget continueButton = TextButton(
child: LocaleKeys.ok.tr().toText14(),
onPressed: () {
Navigator.pop(context);
ConfirmBottomSheet.show(
context,
message: LocaleKeys.updateMember.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
onConfirm: () {
Navigator.pushNamed(context, AppRoutes.addUpdateFamilyMember, arguments: FamilyMemberScreenParams(relationId, flag: 2, actionType: "UPDATE")).then((_) => getEmployeeContacts());
},
);
AlertDialog alert = AlertDialog(title: LocaleKeys.confirm.tr().toText16(isBold: true), content: LocaleKeys.updateMember.tr().toText14(), actions: <Widget>[cancelButton, continueButton]);
showDialog(context: context, builder: (BuildContext context) => alert);
}
void showRemoveAlertDialog(BuildContext context, int relationId) {
Widget cancelButton = TextButton(child: LocaleKeys.cancel.tr().toText14(), onPressed: () => Navigator.pop(context));
Widget continueButton = TextButton(
child: LocaleKeys.ok.tr().toText14(),
onPressed: () {
Navigator.pop(context);
ConfirmBottomSheet.show(
context,
message: LocaleKeys.removeThisMember.tr(),
confirmText: LocaleKeys.yes.tr(),
cancelText: LocaleKeys.no.tr(),
isDestructive: true,
onConfirm: () {
Navigator.pushNamed(context, AppRoutes.deleteFamilyMember, arguments: relationId).then((_) => getEmployeeContacts());
},
);
AlertDialog alert = AlertDialog(title: LocaleKeys.confirm.tr().toText16(isBold: true), content: LocaleKeys.removeThisMember.tr().toText14(), actions: <Widget>[cancelButton, continueButton]);
showDialog(context: context, builder: (BuildContext context) => alert);
}
}

@ -17,7 +17,7 @@ import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_transactions.dart
import 'package:mohem_flutter_app/models/mowadhafhi/get_transaction_attachment_model.dart';
import 'package:mohem_flutter_app/ui/screens/mowadhafhi/view_transaction_attachment.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
@ -283,15 +283,7 @@ class _RequestDetailsState extends State<MowadhafhiRequestDetails> {
Utils.showToast("Cannot open file.");
}
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to give storage permission to view files.",
onTap: () {
Navigator.pop(context);
},
),
);
AppToast.show("You need to give storage permission to view files.");
}
});
}

@ -46,7 +46,7 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/dialogs/accept_reject_input_dialog.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
class WorkListDetailScreen extends StatefulWidget {
WorkListDetailScreen({Key? key}) : super(key: key);
@ -606,24 +606,23 @@ class _WorkListDetailScreenState extends State<WorkListDetailScreen> {
}
Future<void> performNetworkCall(BuildContext context, {String? email, String? userId}) async {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: LocaleKeys.wantToReject.tr(),
okTitle: LocaleKeys.reject.tr(),
onTap: () async {
Navigator.pop(cxt);
Utils.showLoading(context);
try {
await WorkListApiClient().submitComment(comment: "", email: email, userId: userId, notificationId: workListData!.nOTIFICATIONID, apiMode: "REJECT");
Utils.hideLoading(context);
Navigator.pop(context, "delegate_reload");
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
}
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.wantToReject.tr(),
confirmText: LocaleKeys.reject.tr(),
cancelText: LocaleKeys.cancel.tr(),
isDestructive: true,
onConfirm: () async {
Utils.showLoading(context);
try {
await WorkListApiClient().submitComment(comment: "", email: email, userId: userId, notificationId: workListData!.nOTIFICATIONID, apiMode: "REJECT");
Utils.hideLoading(context);
Navigator.pop(context, "delegate_reload");
} catch (ex) {
Utils.hideLoading(context);
Utils.handleException(ex, context, null);
}
},
);
}

@ -26,6 +26,9 @@ class ConfirmBottomSheet extends StatelessWidget {
final Color? confirmButtonTextColor;
final Color? cancelButtonColor;
final Color? cancelButtonTextColor;
// Optional custom content (e.g. an avatar/photo) rendered below the message, above the
// buttons - for cases that need more than the single IconData `icon` slot can show.
final Widget? avatar;
const ConfirmBottomSheet({
Key? key,
@ -44,6 +47,7 @@ class ConfirmBottomSheet extends StatelessWidget {
this.confirmButtonTextColor,
this.cancelButtonColor,
this.cancelButtonTextColor,
this.avatar,
}) : super(key: key);
/// Convenience launcher - mirrors Utils.confirmDialog's call shape but as a bottom sheet.
@ -64,6 +68,7 @@ class ConfirmBottomSheet extends StatelessWidget {
Color? confirmButtonTextColor,
Color? cancelButtonColor,
Color? cancelButtonTextColor,
Widget? avatar,
}) {
return showModalBottomSheet(
context: context,
@ -86,6 +91,7 @@ class ConfirmBottomSheet extends StatelessWidget {
confirmButtonTextColor: confirmButtonTextColor,
cancelButtonColor: cancelButtonColor,
cancelButtonTextColor: cancelButtonTextColor,
avatar: avatar,
);
},
);
@ -127,6 +133,7 @@ class ConfirmBottomSheet extends StatelessWidget {
(title ?? LocaleKeys.confirm.tr()).toText18(isBold: true, isCentered: true, color: MyColors.darkTextColor),
8.height,
message.toText14(color: MyColors.grey70Color, textAlign: TextAlign.center),
if (avatar != null) ...[16.height, Center(child: avatar)],
24.height,
Row(

@ -17,8 +17,9 @@ import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/privilege_list_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/dialogs/success_dialog.dart';
import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheets/confirm_bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/dialogs/dialogs.dart';
import 'package:mohem_flutter_app/widgets/toast/app_toast.dart';
import 'package:mohem_flutter_app/widgets/location/Location.dart' as location;
import 'package:mohem_flutter_app/widgets/nfc/nfc_reader_sheet.dart';
import 'package:mohem_flutter_app/widgets/qr_scanner_dialog.dart';
@ -83,28 +84,19 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
if (has) {
getHuaweiCurrentLocation(attendanceType);
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to give location permission to mark attendance",
onTap: () {
Navigator.pop(context);
},
),
);
AppToast.show(LocaleKeys.locationPermissionRequiredForAttendance.tr());
}
}
});
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to enable location services to mark attendance",
onTap: () async {
Navigator.pop(context);
await Geolocator.openLocationSettings();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.locationServicesRequiredForAttendance.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () async {
await Geolocator.openLocationSettings();
},
);
}
});
@ -119,7 +111,7 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// message: LocaleKeys.locationPermissionRequiredForAttendance.tr(),
// onTap: () {
// Navigator.pop(context);
// },
@ -188,28 +180,26 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
context,
);
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to give location permission to mark attendance",
onTap: () async {
Navigator.pop(context);
await Geolocator.openAppSettings();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.locationPermissionRequiredForAttendance.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () async {
await Geolocator.openAppSettings();
},
);
}
});
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to enable location services to mark attendance",
onTap: () async {
Navigator.pop(context);
await Geolocator.openLocationSettings();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.locationServicesRequiredForAttendance.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () async {
await Geolocator.openLocationSettings();
},
);
}
});
@ -242,28 +232,26 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
context,
);
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to give location permission to mark attendance",
onTap: () async {
Navigator.pop(context);
await Geolocator.openAppSettings();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.locationPermissionRequiredForAttendance.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () async {
await Geolocator.openAppSettings();
},
);
}
});
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to enable location services to mark attendance",
onTap: () async {
Navigator.pop(context);
await Geolocator.openLocationSettings();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.locationServicesRequiredForAttendance.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () async {
await Geolocator.openLocationSettings();
},
);
}
});
@ -296,28 +284,26 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
context,
);
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to give location permission to mark attendance",
onTap: () async {
Navigator.pop(context);
await Geolocator.openAppSettings();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.locationPermissionRequiredForAttendance.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () async {
await Geolocator.openAppSettings();
},
);
}
});
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to enable location services to mark attendance",
onTap: () async {
Navigator.pop(context);
await Geolocator.openLocationSettings();
},
),
ConfirmBottomSheet.show(
context,
message: LocaleKeys.locationServicesRequiredForAttendance.tr(),
confirmText: LocaleKeys.openSettings.tr(),
showCancelButton: false,
onConfirm: () async {
await Geolocator.openLocationSettings();
},
);
}
});
@ -410,15 +396,7 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId, isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng);
if (g?.messageStatus != 1) {
Utils.hideLoading(context);
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: g?.errorEndUserMessage ?? "Unexpected error occurred",
onTap: () {
Navigator.pop(context);
},
),
);
AppToast.show(g?.errorEndUserMessage ?? "Unexpected error occurred");
} else {
bool status = await model.fetchAttendanceTracking(context);
if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3));
@ -442,15 +420,7 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? "", isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng);
if (g?.messageStatus != 1) {
Utils.hideLoading(context);
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: g?.errorEndUserMessage ?? "Unexpected error occurred",
onTap: () {
Navigator.pop(context);
},
),
);
AppToast.show(g?.errorEndUserMessage ?? "Unexpected error occurred");
} else {
bool status = await model.fetchAttendanceTracking(context);
Utils.hideLoading(context);
@ -512,17 +482,7 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
Utils.hideLoading(context);
await closeWifiRequest();
if (g?.messageStatus == 2) {
showDialog(
barrierDismissible: true,
context: context,
builder: (cxt) => ConfirmDialog(
message: g?.errorEndUserMessage ?? "",
onTap: () {
Navigator.pop(context);
},
onCloseTap: () {},
),
);
AppToast.show(g?.errorEndUserMessage ?? "");
} else {
showMDialog(
context,
@ -566,17 +526,7 @@ class _MarkAttendanceWidgetState extends State<MarkAttendanceWidget> {
bool status = await model.fetchAttendanceTracking(context);
Utils.hideLoading(context);
if (g?.messageStatus == 2) {
showDialog(
barrierDismissible: true,
context: context,
builder: (cxt) => ConfirmDialog(
message: g?.errorEndUserMessage ?? "",
onTap: () {
Navigator.pop(context);
},
onCloseTap: () {},
),
);
AppToast.show(g?.errorEndUserMessage ?? "");
} else {
showMDialog(
context,

@ -0,0 +1,141 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/config/routes.dart';
/// Self-dismissing toast with a visible countdown, replacing the generic "just closes"
/// ConfirmDialog for pure-info messages. Mounts via the root navigator's Overlay, so callers
/// don't need to pass a BuildContext (avoids the stale-context issues the dialog path has).
class AppToast {
static OverlayEntry? _entry;
// Single place to retune how long an action-performing toast waits before firing its
// onComplete (either naturally, or immediately if the user taps it early - see _dismiss).
static const int actionSeconds = 4;
// Duration for the plain toast that replaces Utils.showToast's old Fluttertoast call -
// change this single value to retune it everywhere that uses it.
static const int plainSeconds = 3;
static void show(String message, {int seconds = 5, VoidCallback? onComplete, bool showIcon = true, bool showCountdownText = true}) {
OverlayState? overlay = AppRoutes.navigatorKey.currentState?.overlay;
if (overlay == null) return;
// Only one toast on screen at a time - a new call replaces whatever is currently showing.
// Its onComplete (if any) never fires in that case - _dismiss only acts on the entry
// that's still current.
_entry?.remove();
_entry = null;
late OverlayEntry entry;
entry = OverlayEntry(
builder: (BuildContext context) => _CountdownToast(
message: message,
seconds: seconds,
showIcon: showIcon,
showCountdownText: showCountdownText,
onDismiss: () => _dismiss(entry, onComplete),
),
);
_entry = entry;
overlay.insert(entry);
}
// Fires on both the timer completing and an early tap - a tap just means "don't make me
// wait", not "cancel this". Guarded so a toast that's already been replaced never fires its
// (now stale) action.
static void _dismiss(OverlayEntry entry, VoidCallback? onComplete) {
if (_entry == entry) {
entry.remove();
_entry = null;
onComplete?.call();
}
}
}
class _CountdownToast extends StatefulWidget {
final String message;
final int seconds;
final VoidCallback onDismiss;
final bool showIcon;
final bool showCountdownText;
const _CountdownToast({
required this.message,
required this.seconds,
required this.onDismiss,
this.showIcon = true,
this.showCountdownText = true,
});
@override
State<_CountdownToast> createState() => _CountdownToastState();
}
class _CountdownToastState extends State<_CountdownToast> {
late int _remaining = widget.seconds;
Timer? _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
setState(() => _remaining--);
if (_remaining <= 0) {
t.cancel();
widget.onDismiss();
}
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Positioned(
left: 16,
right: 16,
bottom: 32,
child: SafeArea(
child: Material(
color: Colors.transparent,
child: GestureDetector(
onTap: widget.onDismiss,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: MyColors.darkTextColor,
borderRadius: BorderRadius.circular(12),
boxShadow: const <BoxShadow>[BoxShadow(color: Color(0x33000000), blurRadius: 12, offset: Offset(0, 4))],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (widget.showIcon) ...<Widget>[const Icon(Icons.error_outline, color: Colors.white, size: 20), const SizedBox(width: 10)],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(widget.message, style: const TextStyle(color: Colors.white, fontSize: 14, height: 1.3)),
if (widget.showCountdownText) ...<Widget>[
const SizedBox(height: 4),
Text("Dismissing in ${_remaining}s", style: const TextStyle(color: Colors.white70, fontSize: 11)),
],
],
),
),
],
),
),
),
),
),
);
}
}
Loading…
Cancel
Save