edit UI rich text for design

This commit is contained in:
ManhNTX
2022-07-26 06:53:50 +07:00
committed by Dat H. Pham
parent b43d00d00e
commit f77fd7b709
3 changed files with 153 additions and 103 deletions
@@ -3,7 +3,7 @@ import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/utils/style_utils.dart';
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:enough_html_editor/enough_html_editor.dart'; import 'package:enough_html_editor/enough_html_editor.dart' as enough_html_editor;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -40,7 +40,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
this.radiusButton = 10, this.radiusButton = 10,
this.opacity = 1.0, this.opacity = 1.0,
this.iconArrowDown, this.iconArrowDown,
this.colorButton, this.colorButton = Colors.white,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -157,7 +157,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
if (item is FontNameType) { if (item is FontNameType) {
return item.fontFamily; return item.fontFamily;
} }
if (item is SafeFont) { if (item is enough_html_editor.SafeFont) {
return item.name; return item.name;
} }
return ''; return '';
@@ -11,12 +11,14 @@ import 'package:jmap_dart_client/jmap/identities/identity.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/composer/domain/state/download_image_as_base64_state.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/mixin/rich_text_button_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.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/rich_text_style_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/email_address_input_builder.dart';
import 'package:tmail_ui_user/features/upload/domain/state/attachment_upload_state.dart';
import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart'; import 'package:tmail_ui_user/features/upload/presentation/extensions/list_upload_file_state_extension.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
@@ -401,28 +403,8 @@ class ComposerView extends GetWidget<ComposerController> with AppLoaderMixin, Ri
const Divider(color: AppColor.colorDividerComposer, height: 1), const Divider(color: AppColor.colorDividerComposer, height: 1),
Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)), Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: _buildListButton(context)),
const Divider(color: AppColor.colorDividerComposer, height: 1), const Divider(color: AppColor.colorDividerComposer, height: 1),
Obx(() { _buildAttachmentsWidget(context),
final uploadAttachments = controller.uploadController.listUploadAttachments; _buildComposerEditor(context),
if (uploadAttachments.isEmpty) {
return const SizedBox.shrink();
} else {
return Column(children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: _buildAttachmentsTitle(context,
uploadAttachments,
controller.expandModeAttachments.value)),
Padding(
padding: const EdgeInsets.only(bottom: 8, left: 16, right: 16),
child: _buildAttachmentsList(context,
uploadAttachments,
controller.expandModeAttachments.value))
]);
}
}),
Padding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20),
child: _buildComposerEditor(context)),
]) ])
); );
} }
@@ -463,8 +445,17 @@ class ComposerView extends GetWidget<ComposerController> with AppLoaderMixin, Ri
padding: const EdgeInsets.only(left: 60, right: 25), padding: const EdgeInsets.only(left: 60, right: 25),
child: Column(children: [ child: Column(children: [
_buildToolbarRichTextWidget(context), _buildToolbarRichTextWidget(context),
const SizedBox(height: 8), _buildAttachmentsWidget(context),
Obx(() { _buildInlineLoadingView(),
_buildComposerEditor(context),
])
)
])
);
}
Widget _buildAttachmentsWidget(BuildContext context) {
return Obx(() {
final uploadAttachments = controller.uploadController.listUploadAttachments; final uploadAttachments = controller.uploadController.listUploadAttachments;
if (uploadAttachments.isEmpty) { if (uploadAttachments.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -482,41 +473,33 @@ class ComposerView extends GetWidget<ComposerController> with AppLoaderMixin, Ri
controller.expandModeAttachments.value)) controller.expandModeAttachments.value))
]); ]);
} }
}), });
Padding( }
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20, top: 10),
child: _buildComposerEditor(context)), Widget _buildInlineLoadingView() {
]) return Obx(() => controller.uploadController.uploadInlineViewState.value.fold(
) (failure) => const SizedBox.shrink(),
]) (success) {
); if (success is UploadingAttachmentUploadState || success is DownloadingImageAsBase64) {
return Padding(
padding: const EdgeInsets.all(5),
child: loadingWidgetWithSizeColor(
size: 30,
color: AppColor.primaryColor));
}
return const SizedBox.shrink();
}));
} }
Widget _buildToolbarRichTextWidget(BuildContext context) { Widget _buildToolbarRichTextWidget(BuildContext context) {
return Container( return Obx(() => Container(
padding: const EdgeInsets.only(left: 20, top: 15, bottom: 8), padding: const EdgeInsets.only(left: 20, top: 8, bottom: 8),
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Wrap( child: Wrap(
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
children: RichTextStyleType.values.map((textType) => Obx(() { children: [
switch(textType) { Container(
case RichTextStyleType.textColor:
return buildIconColorText(
iconData: textType.getIconData(),
colorSelected: controller.richTextMobileTabletController.selectedTextColor.value,
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, textType));
case RichTextStyleType.textBackgroundColor:
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2),
child: buildIconColorBackgroundText(
iconData: textType.getIconData(),
colorSelected: controller.richTextMobileTabletController.selectedTextBackgroundColor.value,
tooltip: textType.getTooltipButton(context),
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, textType)),
);
case RichTextStyleType.fontName:
return Container(
width: 200, width: 200,
padding: const EdgeInsets.only(right: 2), padding: const EdgeInsets.only(right: 2),
child: DropDownButtonWidget<SafeFont>( child: DropDownButtonWidget<SafeFont>(
@@ -529,17 +512,54 @@ class ComposerView extends GetWidget<ComposerController> with AppLoaderMixin, Ri
: DropdownMenuFontStatus.closed; : DropdownMenuFontStatus.closed;
controller.richTextMobileTabletController.menuFontStatus = newStatus; controller.richTextMobileTabletController.menuFontStatus = newStatus;
}, },
heightItem: 35, heightItem: 38,
sizeIconChecked: 16, sizeIconChecked: 16,
radiusButton: 5, radiusButton: 5,
supportSelectionIcon: true)); colorButton: Colors.white,
default: supportSelectionIcon: true)),
return buildIconStyleText( Padding(
path: textType.getIcon(imagePaths), padding: const EdgeInsets.only(left: 8),
isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(textType), child: buildWrapIconStyleText(
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, textType)); padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5),
} icon: buildIconColorBackgroundTextWithoutTooltip(
})).toList() iconData: RichTextStyleType.textColor.getIconData(),
colorSelected: controller.richTextMobileTabletController.selectedTextColor.value,
),
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.textColor)),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5),
icon: buildIconColorBackgroundTextWithoutTooltip(
iconData: RichTextStyleType.textBackgroundColor.getIconData(),
colorSelected: controller.richTextMobileTabletController.selectedTextBackgroundColor.value,
),
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.textBackgroundColor)),
),
buildWrapIconStyleText(
hasDropdown: false,
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
icon: Wrap(children: [
buildIconStyleText(
path: RichTextStyleType.bold.getIcon(imagePaths),
isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.bold),
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.bold)),
buildIconStyleText(
path: RichTextStyleType.italic.getIcon(imagePaths),
isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.italic),
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.italic)),
buildIconStyleText(
path: RichTextStyleType.underline.getIcon(imagePaths),
isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.underline),
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.underline)),
buildIconStyleText(
path: RichTextStyleType.strikeThrough.getIcon(imagePaths),
isSelected: controller.richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.strikeThrough),
onTap: () => controller.richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.strikeThrough))
])),
],
),
), ),
); );
} }
@@ -592,14 +612,17 @@ class ComposerView extends GetWidget<ComposerController> with AppLoaderMixin, Ri
} }
Widget _buildHtmlEditor(String initialContent) { Widget _buildHtmlEditor(String initialContent) {
return HtmlEditor( return Padding(
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 20, top: 10),
child: HtmlEditor(
key: const Key('composer_editor'), key: const Key('composer_editor'),
minHeight: 550, minHeight: 550,
initialContent: initialContent, initialContent: initialContent,
onCreated: (editorApi) { onCreated: (editorApi) {
controller.richTextMobileTabletController.htmlEditorApi = editorApi; controller.richTextMobileTabletController.htmlEditorApi = editorApi;
controller.richTextMobileTabletController.listenHtmlEditorApi(); controller.richTextMobileTabletController.listenHtmlEditorApi();
}); }),
);
} }
Widget _buildAttachmentsTitle( Widget _buildAttachmentsTitle(
@@ -80,6 +80,33 @@ mixin RichTextButtonMixin {
); );
} }
Widget buildIcon({
required String path,
Color? color,
double opacity = 1.0,
}){
final newColor = color == Colors.white
? AppColor.colorDefaultRichTextButton
: color;
return SvgPicture.asset(path,
color: newColor?.withOpacity(opacity),
fit: BoxFit.fill);
}
Widget buildIconColorBackgroundTextWithoutTooltip({
required IconData? iconData,
required Color? colorSelected,
double opacity = 1.0,
}){
final newColor = colorSelected == Colors.white
? AppColor.colorDefaultRichTextButton
: colorSelected;
return Icon(iconData,
color: (newColor ?? AppColor.colorDefaultRichTextButton).withOpacity(opacity),
size: 20);
}
Widget buildIconColorBackgroundText({ Widget buildIconColorBackgroundText({
required IconData? iconData, required IconData? iconData,
required Color? colorSelected, required Color? colorSelected,