Close color picker when click outside in composer

This commit is contained in:
dab246
2025-08-26 17:39:00 +07:00
committed by Dat H. Pham
parent ff0ac57c1d
commit a4b0885477
7 changed files with 166 additions and 150 deletions
@@ -5,7 +5,6 @@ import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
typedef SelectColorActionCallback = Function(Color? colorSelected); typedef SelectColorActionCallback = Function(Color? colorSelected);
@@ -41,117 +40,115 @@ class ColorPickerDialogBuilder {
Future<dynamic> show() async { Future<dynamic> show() async {
return Get.dialog( return Get.dialog(
PointerInterceptor( AlertDialog(
child: AlertDialog( title: Text(
title: Text( title ?? '',
title ?? '', textAlign: TextAlign.center,
textAlign: TextAlign.center, style: ThemeUtils.defaultTextStyleInterFont.copyWith(
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black)),
titleTextStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 20, fontSize: 20,
color: Colors.black), color: Colors.black)),
titlePadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), titleTextStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), fontWeight: FontWeight.bold,
actionsPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), fontSize: 20,
actionsAlignment: MainAxisAlignment.center, color: Colors.black),
actionsOverflowButtonSpacing: 8, titlePadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
backgroundColor: Colors.white, contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), actionsPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
scrollable: true, actionsAlignment: MainAxisAlignment.center,
elevation: 10, actionsOverflowButtonSpacing: 8,
content: ValueListenableBuilder( backgroundColor: Colors.white,
valueListenable: _currentColor, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
builder: (context, _, __) { scrollable: true,
return Column( elevation: 10,
mainAxisAlignment: MainAxisAlignment.center, content: ValueListenableBuilder(
children: [ valueListenable: _currentColor,
Container( builder: (context, _, __) {
width: 500, return Column(
decoration: const BoxDecoration( mainAxisAlignment: MainAxisAlignment.center,
borderRadius: BorderRadius.all(Radius.circular(4)), children: [
color: Colors.white Container(
), width: 500,
child: Center( decoration: const BoxDecoration(
child: Wrap(children: AppColor.listColorsPicker borderRadius: BorderRadius.all(Radius.circular(4)),
.map((color) => _itemColorWidget(context, color)) color: Colors.white
.toList(), ),
), child: Center(
child: Wrap(children: AppColor.listColorsPicker
.map((color) => _itemColorWidget(context, color))
.toList(),
), ),
), ),
Padding( ),
padding: const EdgeInsets.symmetric(vertical: 16), Padding(
child: ColorCodeField( padding: const EdgeInsets.symmetric(vertical: 16),
color: _currentColor.value, child: ColorCodeField(
colorCodeHasColor: true, color: _currentColor.value,
shouldUpdate: _shouldUpdate, colorCodeHasColor: true,
onColorChanged: (Color color) { shouldUpdate: _shouldUpdate,
if (AppColor.listColorsPicker.any((element) => element.toInt() == color.toInt())) { onColorChanged: (Color color) {
_shouldUpdate = true; if (AppColor.listColorsPicker.any((element) => element.toInt() == color.toInt())) {
_currentColor.value = color; _shouldUpdate = true;
} else { _currentColor.value = color;
_shouldUpdate = false; } else {
_currentColor.value = Colors.black; _shouldUpdate = false;
_colorCode = color; _currentColor.value = Colors.black;
} _colorCode = color;
}, }
onEditFocused: (bool editInFocus) { },
_shouldUpdate = editInFocus ? true : false; onEditFocused: (bool editInFocus) {
}, _shouldUpdate = editInFocus ? true : false;
copyPasteBehavior: const ColorPickerCopyPasteBehavior( },
parseShortHexCode: true, copyPasteBehavior: const ColorPickerCopyPasteBehavior(
), parseShortHexCode: true,
toolIcons: const ColorPickerActionButtons( ),
dialogActionButtons: true, toolIcons: const ColorPickerActionButtons(
), dialogActionButtons: true,
), ),
), ),
], ),
); ],
}, );
), },
actions: <Widget>[
buildButtonWrapText(
textActionCancel ?? '',
radius: 5,
height: 30,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.normal),
bgColor: AppColor.colorShadowComposer,
onTap: () => cancelActionCallback?.call()),
buildButtonWrapText(
textActionResetDefault ?? '',
radius: 5,
height: 30,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.normal),
bgColor: Colors.white,
borderColor: Colors.black26,
onTap: () => resetToDefaultActionCallback?.call()),
buildButtonWrapText(
textActionSetColor ?? '',
radius: 5,
height: 30,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500),
onTap: () {
if (!_shouldUpdate) {
setColorActionCallback?.call(_colorCode);
} else {
setColorActionCallback?.call(_currentColor.value);
}
})
],
), ),
actions: <Widget>[
buildButtonWrapText(
textActionCancel ?? '',
radius: 5,
height: 30,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.normal),
bgColor: AppColor.colorShadowComposer,
onTap: () => cancelActionCallback?.call()),
buildButtonWrapText(
textActionResetDefault ?? '',
radius: 5,
height: 30,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.normal),
bgColor: Colors.white,
borderColor: Colors.black26,
onTap: () => resetToDefaultActionCallback?.call()),
buildButtonWrapText(
textActionSetColor ?? '',
radius: 5,
height: 30,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500),
onTap: () {
if (!_shouldUpdate) {
setColorActionCallback?.call(_colorCode);
} else {
setColorActionCallback?.call(_currentColor.value);
}
})
],
) )
); );
} }
@@ -0,0 +1,46 @@
import 'package:core/presentation/views/dialog/color_picker_dialog_builder.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class ColorDialogPicker {
static final _instance = ColorDialogPicker._();
factory ColorDialogPicker() => _instance;
ColorDialogPicker._();
final _isOpened = RxBool(false);
RxBool get isOpened => _isOpened;
Future<void> showTwakeColorPicker(
BuildContext context,
Color currentColor,
{
Function(Color?)? onSelectColor,
VoidCallback? onResetToDefault,
}
) async {
_isOpened.value = true;
final appLocalizations = AppLocalizations.of(context);
return ColorPickerDialogBuilder(
ValueNotifier<Color>(currentColor),
title: appLocalizations.chooseAColor,
textActionSetColor: appLocalizations.setColor,
textActionResetDefault: appLocalizations.resetToDefault,
textActionCancel: appLocalizations.cancel,
cancelActionCallback: popBack,
resetToDefaultActionCallback: () {
onResetToDefault?.call();
popBack();
},
setColorActionCallback: (selectedColor) {
onSelectColor?.call(selectedColor);
popBack();
},
).show().whenComplete(() => _isOpened.value = false);
}
}
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
import 'package:model/email/prefix_email_address.dart'; import 'package:model/email/prefix_email_address.dart';
import 'package:pointer_interceptor/pointer_interceptor.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/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/features/base/widget/dialog_picker/color_dialog_picker.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/composer_print_draft_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/composer_print_draft_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_edit_recipient_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_edit_recipient_extension.dart';
@@ -42,8 +43,11 @@ class ComposerView extends GetWidget<ComposerController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final iframeOverlay = Obx(() { final iframeOverlay = Obx(() {
if (controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame || bool isOverlayEnabled = controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame ||
MessageDialogActionManager().isDialogOpened) { MessageDialogActionManager().isDialogOpened ||
ColorDialogPicker().isOpened.isTrue;
if (isOverlayEnabled) {
return Positioned.fill( return Positioned.fill(
key: const ValueKey('tap-to-close'), key: const ValueKey('tap-to-close'),
child: PointerInterceptor( child: PointerInterceptor(
@@ -1,35 +0,0 @@
import 'package:core/presentation/views/dialog/color_picker_dialog_builder.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
abstract class BaseRichTextController extends GetxController {
void openMenuSelectColor(
BuildContext context,
Color currentColor,
{
Function(Color?)? onSelectColor,
VoidCallback? onResetToDefault,
}
) async {
await ColorPickerDialogBuilder(
ValueNotifier<Color>(currentColor),
title: AppLocalizations.of(context).chooseAColor,
textActionSetColor: AppLocalizations.of(context).setColor,
textActionResetDefault: AppLocalizations.of(context).resetToDefault,
textActionCancel: AppLocalizations.of(context).cancel,
cancelActionCallback: () => popBack(),
resetToDefaultActionCallback: () {
onResetToDefault?.call();
popBack();
},
setColorActionCallback: (selectedColor) {
onSelectColor?.call(selectedColor);
popBack();
}
).show();
}
}
@@ -5,13 +5,13 @@ import 'package:core/utils/html/html_utils.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:rich_text_composer/rich_text_composer.dart'; import 'package:rich_text_composer/rich_text_composer.dart';
import 'package:tmail_ui_user/features/composer/presentation/controller/base_rich_text_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RichTextMobileTabletController extends BaseRichTextController { class RichTextMobileTabletController extends GetxController {
HtmlEditorApi? htmlEditorApi; HtmlEditorApi? htmlEditorApi;
final RichTextController richTextController = RichTextController(); final RichTextController richTextController = RichTextController();
@@ -9,7 +9,7 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:html_editor_enhanced/html_editor.dart'; import 'package:html_editor_enhanced/html_editor.dart';
import 'package:tmail_ui_user/features/composer/presentation/controller/base_rich_text_controller.dart'; import 'package:tmail_ui_user/features/base/widget/dialog_picker/color_dialog_picker.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/code_view_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/code_view_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu_font_status.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu_font_status.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/font_name_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/font_name_type.dart';
@@ -21,7 +21,7 @@ import 'package:tmail_ui_user/features/composer/presentation/model/paragraph_typ
import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class RichTextWebController extends BaseRichTextController { class RichTextWebController extends GetxController {
static const List<int> fontSizeList = [10, 12, 14, 15, 16, 18, 24, 36, 48, 64]; static const List<int> fontSizeList = [10, 12, 14, 15, 16, 18, 24, 36, 48, 64];
static const int fontSizeDefault = 16; static const int fontSizeDefault = 16;
@@ -140,7 +140,7 @@ class RichTextWebController extends BaseRichTextController {
void applyRichTextStyle(BuildContext context, RichTextStyleType textStyleType) { void applyRichTextStyle(BuildContext context, RichTextStyleType textStyleType) {
switch(textStyleType) { switch(textStyleType) {
case RichTextStyleType.textColor: case RichTextStyleType.textColor:
openMenuSelectColor( ColorDialogPicker().showTwakeColorPicker(
context, context,
selectedTextColor.value, selectedTextColor.value,
onResetToDefault: () => _applyForegroundColor(Colors.black), onResetToDefault: () => _applyForegroundColor(Colors.black),
@@ -148,7 +148,7 @@ class RichTextWebController extends BaseRichTextController {
); );
break; break;
case RichTextStyleType.textBackgroundColor: case RichTextStyleType.textBackgroundColor:
openMenuSelectColor( ColorDialogPicker().showTwakeColorPicker(
context, context,
selectedTextBackgroundColor.value, selectedTextBackgroundColor.value,
onResetToDefault: () => applyBackgroundColor(Colors.white), onResetToDefault: () => applyBackgroundColor(Colors.white),
@@ -14,6 +14,7 @@ import 'package:model/extensions/presentation_email_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:pointer_interceptor/pointer_interceptor.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/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/features/base/widget/dialog_picker/color_dialog_picker.dart';
import 'package:tmail_ui_user/features/base/widget/optional_expanded.dart'; import 'package:tmail_ui_user/features/base/widget/optional_expanded.dart';
import 'package:tmail_ui_user/features/base/widget/optional_scroll.dart'; import 'package:tmail_ui_user/features/base/widget/optional_scroll.dart';
import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart';
@@ -529,9 +530,12 @@ class EmailView extends GetWidget<SingleEmailController> {
), ),
), ),
Obx(() { Obx(() {
if (controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame || bool isOverlayEnabled = controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame ||
MessageDialogActionManager().isDialogOpened || MessageDialogActionManager().isDialogOpened ||
EmailActionReactor.isDialogOpened) { EmailActionReactor.isDialogOpened ||
ColorDialogPicker().isOpened.isTrue;
if (isOverlayEnabled) {
return Positioned.fill( return Positioned.fill(
child: PointerInterceptor( child: PointerInterceptor(
child: const SizedBox.expand(), child: const SizedBox.expand(),