TF-3953 Close several modals when click outside

This commit is contained in:
DatDang
2025-08-07 14:14:50 +07:00
committed by Dat H. Pham
parent 44c17bc762
commit 9e7298bc76
34 changed files with 575 additions and 580 deletions
@@ -26,6 +26,9 @@ mixin DateRangePickerMixin {
initStartDate: initStartDate,
initEndDate: initEndDate,
autoClose: false,
barrierDismissible: true,
barrierLabel: 'Multiple view date range picker',
usePointerInterceptor: false,
selectDateRangeActionCallback: (startDate, endDate) {
_handleSelectDateRangeResult(
context,
+3 -3
View File
@@ -1,9 +1,9 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.dart';
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
mixin LogoutMixin implements MessageDialogActionMixin {
mixin LogoutMixin {
void showLogoutConfirmDialog({
required BuildContext context,
required String userAddress,
@@ -11,7 +11,7 @@ mixin LogoutMixin implements MessageDialogActionMixin {
}) {
final appLocalizations = AppLocalizations.of(context);
showConfirmDialogAction(
MessageDialogActionManager().showConfirmDialogAction(
context,
'',
appLocalizations.yesLogout,
@@ -1,15 +1,12 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/app_toast.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart';
import 'package:core/presentation/views/dialog/confirmation_dialog_builder.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:model/extensions/presentation_mailbox_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -53,7 +50,6 @@ mixin MailboxActionHandlerMixin {
MailboxDashBoardController dashboardController
) {
final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
final appToast = Get.find<AppToast>();
if (responsiveUtils.isScreenWithShortestSide(context)) {
@@ -76,34 +72,29 @@ mixin MailboxActionHandlerMixin {
}))
.show();
} else {
Get.dialog(
PointerInterceptor(
child: ConfirmationDialogBuilder(
key: const Key('confirm_dialog_empty_trash'),
imagePath: imagePaths,
title: AppLocalizations.of(context).emptyTrash,
textContent: AppLocalizations.of(context).empty_trash_dialog_message,
cancelText: AppLocalizations.of(context).cancel,
confirmText: AppLocalizations.of(context).delete,
onCloseButtonAction: popBack,
onConfirmButtonAction: () {
popBack();
if (mailbox.countTotalEmails > 0) {
dashboardController.emptyTrashFolderAction(
trashFolderId: mailbox.id,
totalEmails: mailbox.countTotalEmails
);
} else {
appToast.showToastWarningMessage(
context,
AppLocalizations.of(context).noEmailInYourCurrentFolder
);
}
},
onCancelButtonAction: popBack,
),
),
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
MessageDialogActionManager().showConfirmDialogAction(
key: const Key('confirm_dialog_empty_trash'),
context,
title: AppLocalizations.of(context).emptyTrash,
AppLocalizations.of(context).empty_trash_dialog_message,
cancelTitle: AppLocalizations.of(context).cancel,
AppLocalizations.of(context).delete,
onCloseButtonAction: popBack,
onConfirmAction: () {
popBack();
if (mailbox.countTotalEmails > 0) {
dashboardController.emptyTrashFolderAction(
trashFolderId: mailbox.id,
totalEmails: mailbox.countTotalEmails
);
} else {
appToast.showToastWarningMessage(
context,
AppLocalizations.of(context).noEmailInYourCurrentFolder
);
}
},
onCancelAction: popBack,
);
}
}
@@ -118,7 +109,6 @@ mixin MailboxActionHandlerMixin {
}
final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
final appToast = Get.find<AppToast>();
if (responsiveUtils.isScreenWithShortestSide(context)) {
@@ -138,31 +128,26 @@ mixin MailboxActionHandlerMixin {
}))
.show();
} else {
Get.dialog(
PointerInterceptor(
child: ConfirmationDialogBuilder(
key: const Key('confirm_dialog_empty_spam'),
imagePath: imagePaths,
title: AppLocalizations.of(context).emptySpamFolder,
textContent: AppLocalizations.of(context).emptySpamMessageDialog,
cancelText: AppLocalizations.of(context).cancel,
confirmText: AppLocalizations.of(context).delete_all,
onCloseButtonAction: popBack,
onConfirmButtonAction: () {
popBack();
if (mailbox.countTotalEmails > 0) {
dashboardController.emptySpamFolderAction(spamFolderId: mailbox.id, totalEmails: mailbox.countTotalEmails);
} else {
appToast.showToastWarningMessage(
context,
AppLocalizations.of(context).noEmailInYourCurrentFolder
);
}
},
onCancelButtonAction: popBack,
),
),
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
MessageDialogActionManager().showConfirmDialogAction(
key: const Key('confirm_dialog_empty_spam'),
context,
title: AppLocalizations.of(context).emptySpamFolder,
AppLocalizations.of(context).emptySpamMessageDialog,
cancelTitle: AppLocalizations.of(context).cancel,
AppLocalizations.of(context).delete_all,
onCloseButtonAction: popBack,
onConfirmAction: () {
popBack();
if (mailbox.countTotalEmails > 0) {
dashboardController.emptySpamFolderAction(spamFolderId: mailbox.id, totalEmails: mailbox.countTotalEmails);
} else {
appToast.showToastWarningMessage(
context,
AppLocalizations.of(context).noEmailInYourCurrentFolder
);
}
},
onCancelAction: popBack
);
}
}
@@ -8,13 +8,21 @@ import 'package:core/presentation/views/dialog/edit_text_dialog_builder.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
mixin MessageDialogActionMixin {
class MessageDialogActionManager {
static final _instance = MessageDialogActionManager._();
Future<dynamic> showConfirmDialogAction(
factory MessageDialogActionManager() => _instance;
MessageDialogActionManager._();
final _isConfirmDialogOpened = RxBool(false);
final _isInputDialogOpened = RxBool(false);
bool get isDialogOpened => _isConfirmDialogOpened.value || _isInputDialogOpened.value;
Future<void> showConfirmDialogAction(
BuildContext context,
String message,
String actionName,
@@ -33,6 +41,7 @@ mixin MessageDialogActionMixin {
bool usePopScope = false,
List<TextSpan>? listTextSpan,
Widget? icon,
Widget? additionalWidgetContent,
Color? actionButtonColor,
Color? cancelButtonColor,
Color? cancelLabelButtonColor,
@@ -45,31 +54,82 @@ mixin MessageDialogActionMixin {
EdgeInsets? outsideDialogPadding,
}
) async {
if (PlatformInfo.isWeb) {
_isConfirmDialogOpened.value = true;
}
final responsiveUtils = Get.find<ResponsiveUtils>();
final imagePaths = Get.find<ImagePaths>();
if (alignCenter) {
final childWidget = PointerInterceptor(
child: ConfirmationDialogBuilder(
final childWidget = ConfirmationDialogBuilder(
key: key,
imagePath: imagePaths,
listTextSpan: listTextSpan,
isArrangeActionButtonsVertical: isArrangeActionButtonsVertical,
useIconAsBasicLogo: useIconAsBasicLogo,
isScrollContentEnabled: isScrollContentEnabled,
title: title ?? '',
textContent: message,
confirmText: actionName,
cancelText: hasCancelButton ? cancelTitle ?? AppLocalizations.of(context).cancel : '',
iconWidget: icon,
outsideDialogPadding: outsideDialogPadding ??
(responsiveUtils.isScreenWithShortestSide(context)
? const EdgeInsets.all(16)
: null),
additionalWidgetContent: additionalWidgetContent,
cancelBackgroundButtonColor: cancelButtonColor,
confirmBackgroundButtonColor: actionButtonColor,
cancelLabelButtonColor: cancelLabelButtonColor,
confirmLabelButtonColor: confirmLabelButtonColor,
onConfirmButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onConfirmAction?.call();
},
onCancelButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onCancelAction?.call();
},
onCloseButtonAction: onCloseButtonAction,
);
await Get.dialog(
usePopScope && PlatformInfo.isMobile
? PopScope(onPopInvokedWithResult: onPopInvoked, canPop: false, child: childWidget)
: childWidget,
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
barrierDismissible: outsideDismissible
);
} else {
if (responsiveUtils.isMobile(context)) {
final childWidget = ConfirmationDialogBuilder(
key: key,
imagePath: imagePaths,
showAsBottomSheet: true,
listTextSpan: listTextSpan,
maxWidth: responsiveUtils.getSizeScreenShortestSide(context) - 16,
isArrangeActionButtonsVertical: isArrangeActionButtonsVertical,
useIconAsBasicLogo: useIconAsBasicLogo,
isScrollContentEnabled: isScrollContentEnabled,
title: title ?? '',
textContent: message,
confirmText: actionName,
cancelText: hasCancelButton ? cancelTitle ?? AppLocalizations.of(context).cancel : '',
title: title ?? '',
iconWidget: icon,
outsideDialogPadding: outsideDialogPadding ??
(responsiveUtils.isScreenWithShortestSide(context)
? const EdgeInsets.all(16)
: null),
cancelBackgroundButtonColor: cancelButtonColor,
additionalWidgetContent: additionalWidgetContent,
margin: dialogMargin,
confirmBackgroundButtonColor: actionButtonColor,
cancelBackgroundButtonColor: cancelButtonColor,
cancelLabelButtonColor: cancelLabelButtonColor,
confirmLabelButtonColor: confirmLabelButtonColor,
confirmText: actionName,
cancelText: hasCancelButton ? cancelTitle ?? AppLocalizations.of(context).cancel : '',
useIconAsBasicLogo: useIconAsBasicLogo,
isScrollContentEnabled: isScrollContentEnabled,
onConfirmButtonAction: () {
if (autoPerformPopBack) {
popBack();
@@ -82,55 +142,10 @@ mixin MessageDialogActionMixin {
}
onCancelAction?.call();
},
onCloseButtonAction: onCloseButtonAction,
),
);
return await Get.dialog(
usePopScope && PlatformInfo.isMobile
? PopScope(onPopInvokedWithResult: onPopInvoked, canPop: false, child: childWidget)
: childWidget,
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
barrierDismissible: outsideDismissible
);
} else {
if (responsiveUtils.isMobile(context)) {
final childWidget = PointerInterceptor(
child: ConfirmationDialogBuilder(
key: key,
imagePath: imagePaths,
showAsBottomSheet: true,
listTextSpan: listTextSpan,
maxWidth: responsiveUtils.getSizeScreenShortestSide(context) - 16,
isArrangeActionButtonsVertical: isArrangeActionButtonsVertical,
textContent: message,
title: title ?? '',
iconWidget: icon,
margin: dialogMargin,
confirmBackgroundButtonColor: actionButtonColor,
cancelBackgroundButtonColor: cancelButtonColor,
cancelLabelButtonColor: cancelLabelButtonColor,
confirmLabelButtonColor: confirmLabelButtonColor,
confirmText: actionName,
cancelText: hasCancelButton ? cancelTitle ?? AppLocalizations.of(context).cancel : '',
useIconAsBasicLogo: useIconAsBasicLogo,
isScrollContentEnabled: isScrollContentEnabled,
onConfirmButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onConfirmAction?.call();
},
onCancelButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onCancelAction?.call();
},
onCloseButtonAction: onCloseButtonAction ?? popBack
),
onCloseButtonAction: onCloseButtonAction ?? popBack,
);
if (showAsBottomSheet) {
return await Get.bottomSheet(
await Get.bottomSheet(
usePopScope && PlatformInfo.isMobile
? PopScope(onPopInvokedWithResult: onPopInvoked, canPop: false, child: childWidget)
: childWidget,
@@ -143,7 +158,7 @@ mixin MessageDialogActionMixin {
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(18))),
);
} else {
return (ConfirmationDialogActionSheetBuilder(context, listTextSpan: listTextSpan)
await (ConfirmationDialogActionSheetBuilder(context, listTextSpan: listTextSpan)
..messageText(message)
..onCancelAction(
cancelTitle ?? AppLocalizations.of(context).cancel,
@@ -162,39 +177,39 @@ mixin MessageDialogActionMixin {
})).show();
}
} else {
final childWidget = PointerInterceptor(
child: ConfirmationDialogBuilder(
key: key,
imagePath: imagePaths,
listTextSpan: listTextSpan,
isArrangeActionButtonsVertical: isArrangeActionButtonsVertical,
useIconAsBasicLogo: useIconAsBasicLogo,
isScrollContentEnabled: isScrollContentEnabled,
title: title ?? '',
textContent: message,
iconWidget: icon,
confirmBackgroundButtonColor: actionButtonColor,
cancelBackgroundButtonColor: cancelButtonColor,
cancelLabelButtonColor: cancelLabelButtonColor,
confirmLabelButtonColor: confirmLabelButtonColor,
confirmText: actionName,
cancelText: hasCancelButton ? cancelTitle ?? AppLocalizations.of(context).cancel : '',
onConfirmButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onConfirmAction?.call();
},
onCancelButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onCancelAction?.call();
},
onCloseButtonAction: onCloseButtonAction ?? popBack,
),
final childWidget = ConfirmationDialogBuilder(
key: key,
imagePath: imagePaths,
listTextSpan: listTextSpan,
isArrangeActionButtonsVertical: isArrangeActionButtonsVertical,
useIconAsBasicLogo: useIconAsBasicLogo,
isScrollContentEnabled: isScrollContentEnabled,
title: title ?? '',
textContent: message,
iconWidget: icon,
additionalWidgetContent: additionalWidgetContent,
confirmBackgroundButtonColor: actionButtonColor,
cancelBackgroundButtonColor: cancelButtonColor,
cancelLabelButtonColor: cancelLabelButtonColor,
confirmLabelButtonColor: confirmLabelButtonColor,
confirmText: actionName,
cancelText: hasCancelButton ? cancelTitle ?? AppLocalizations.of(context).cancel : '',
onConfirmButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onConfirmAction?.call();
},
onCancelButtonAction: () {
if (autoPerformPopBack) {
popBack();
}
onCancelAction?.call();
},
onCloseButtonAction: onCloseButtonAction ?? popBack,
outsideDialogPadding: outsideDialogPadding,
);
return await Get.dialog(
await Get.dialog(
usePopScope && PlatformInfo.isMobile
? PopScope(onPopInvokedWithResult: onPopInvoked, canPop: false, child: childWidget)
: childWidget,
@@ -203,9 +218,10 @@ mixin MessageDialogActionMixin {
);
}
}
_isConfirmDialogOpened.value = false;
}
Future<dynamic> showInputDialogAction({
Future<void> showInputDialogAction({
required BuildContext context,
required String title,
required String value,
@@ -216,23 +232,27 @@ mixin MessageDialogActionMixin {
String? closeIcon,
OnInputDialogNegativeButtonAction? onNegativeButtonAction,
OnInputDialogInputErrorChangedAction? onInputErrorChanged,
bool outsideDismissible = false,
}) async {
return await Get.dialog(
PointerInterceptor(
child: EditTextDialogBuilder(
key: key,
title: AppLocalizations.of(context).renameFolder,
value: value,
positiveText: positiveText,
negativeText: negativeText,
closeIcon: closeIcon,
onInputErrorChanged: onInputErrorChanged,
onPositiveButtonAction: onPositiveButtonAction,
onNegativeButtonAction: onNegativeButtonAction,
onCloseButtonAction: closeIcon != null ? popBack : null,
),
_isInputDialogOpened.value = true;
await Get.dialog(
EditTextDialogBuilder(
key: key,
title: AppLocalizations.of(context).renameFolder,
value: value,
positiveText: positiveText,
negativeText: negativeText,
closeIcon: closeIcon,
onInputErrorChanged: onInputErrorChanged,
onPositiveButtonAction: onPositiveButtonAction,
onNegativeButtonAction: onNegativeButtonAction,
onCloseButtonAction: closeIcon != null ? popBack : null,
),
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
barrierDismissible: outsideDismissible,
);
_isInputDialogOpened.value = false;
}
}