Set padding for email address tag in input field

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-07-11 10:43:32 +07:00
committed by Dat H. Pham
parent 58a8d93ed3
commit 3457601d14
8 changed files with 166 additions and 105 deletions
@@ -130,18 +130,10 @@ class _DefaultAutocompleteInputFieldWidgetState
color: AppColor.m3Tertiary, color: AppColor.m3Tertiary,
), ),
isDense: true, isDense: true,
contentPadding: _currentListEmailAddress.isNotEmpty contentPadding: _getTextFieldContentPadding(),
? const EdgeInsetsDirectional.symmetric(vertical: 14)
: const EdgeInsetsDirectional.only(
top: 14,
bottom: 14,
start: 12,
end: 8,
),
), ),
padding: _currentListEmailAddress.isNotEmpty padding: _getTagEditorPadding(),
? const EdgeInsets.symmetric(horizontal: 12) tagSpacing: PlatformInfo.isWeb ? 4.0 : 8.0,
: EdgeInsets.zero,
borderRadius: 10, borderRadius: 10,
borderSize: 1, borderSize: 1,
focusedBorderColor: AppColor.primaryColor, focusedBorderColor: AppColor.primaryColor,
@@ -434,4 +426,40 @@ class _DefaultAutocompleteInputFieldWidgetState
setState(onAction); setState(onAction);
} }
} }
EdgeInsetsGeometry? _getTextFieldContentPadding() {
if (_currentListEmailAddress.isNotEmpty) {
if (PlatformInfo.isWeb) {
return const EdgeInsetsDirectional.symmetric(
vertical: 14,
horizontal: 4,
);
} else {
return const EdgeInsetsDirectional.symmetric(vertical: 12);
}
} else {
if (PlatformInfo.isWeb) {
return const EdgeInsetsDirectional.only(
top: 14,
bottom: 14,
start: 12,
end: 8,
);
} else {
return const EdgeInsetsDirectional.all(12);
}
}
}
EdgeInsets? _getTagEditorPadding() {
if (_currentListEmailAddress.isNotEmpty) {
if (PlatformInfo.isWeb) {
return const EdgeInsets.symmetric(horizontal: 12);
} else {
return const EdgeInsets.only(left: 12, right: 12, bottom: 9);
}
} else {
return null;
}
}
} }
@@ -1,7 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/utils/direction_utils.dart'; import 'package:core/utils/direction_utils.dart';
import 'package:core/utils/platform_info.dart';
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:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
@@ -42,78 +42,102 @@ class DefaultAutocompleteTagItemWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Stack( Widget bodyWidget = Chip(
alignment: AlignmentDirectional.centerEnd, labelPadding: EdgeInsetsDirectional.symmetric(
children: [ horizontal: AutoCompleteTagItemWebStyle.labelPaddingHorizontal,
Padding( vertical: DirectionUtils.isDirectionRTLByHasAnyRtl(currentEmailAddress.asString()) ? 0 : 2,
padding: EdgeInsetsDirectional.only( ),
top: AutoCompleteTagItemWebStyle.paddingTop, label: Text(
end: isCollapsed ? AutoCompleteTagItemWebStyle.paddingEnd : 0, currentEmailAddress.asString(),
), maxLines: 1,
child: Draggable<DraggableEmailAddress>( overflow: TextOverflow.ellipsis,
data: DraggableEmailAddress( style: AutoCompleteTagItemWebStyle.labelTextStyle,
emailAddress: currentEmailAddress, ),
filterField: field, deleteIcon: SvgPicture.asset(iconClose, fit: BoxFit.fill),
), padding: EdgeInsets.zero,
feedback: DraggableRecipientTagWidget( labelStyle: AutoCompleteTagItemWebStyle.labelTextStyle,
emailAddress: currentEmailAddress, backgroundColor: _getTagBackgroundColor(),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), shape: RoundedRectangleBorder(
), borderRadius: AutoCompleteTagItemWebStyle.shapeBorderRadius,
childWhenDragging: DraggableRecipientTagWidget( side: _getTagBorderSide(),
emailAddress: currentEmailAddress, ),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), avatar: currentEmailAddress.emailAddress.isNotEmpty
), ? AvatarTagItemWidget(tagName: currentEmailAddress.emailAddress)
child: TextFieldTapRegion( : null,
child: InkWell( onDeleted: () => onDeleteTagAction?.call(currentEmailAddress),
onTap: () => isCollapsed );
? onShowFullAction?.call(field)
: null, if (PlatformInfo.isWeb) {
child: MouseRegion( bodyWidget = Draggable<DraggableEmailAddress>(
cursor: SystemMouseCursors.grab, data: DraggableEmailAddress(
child: Chip( emailAddress: currentEmailAddress,
labelPadding: EdgeInsetsDirectional.symmetric( filterField: field,
horizontal: AutoCompleteTagItemWebStyle.labelPaddingHorizontal, ),
vertical: DirectionUtils.isDirectionRTLByHasAnyRtl(currentEmailAddress.asString()) ? 0 : 2 feedback: DraggableRecipientTagWidget(
), emailAddress: currentEmailAddress,
label: Text( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
currentEmailAddress.asString(), ),
maxLines: 1, childWhenDragging: DraggableRecipientTagWidget(
overflow: CommonTextStyle.defaultTextOverFlow, emailAddress: currentEmailAddress,
softWrap: CommonTextStyle.defaultSoftWrap, padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
), ),
deleteIcon: SvgPicture.asset( child: TextFieldTapRegion(
iconClose, child: InkWell(
fit: BoxFit.fill, onTap: () => isCollapsed ? onShowFullAction?.call(field) : null,
), child: MouseRegion(
padding: EdgeInsets.zero, cursor: SystemMouseCursors.grab,
labelStyle: AutoCompleteTagItemWebStyle.labelTextStyle, child: bodyWidget,
backgroundColor: _getTagBackgroundColor(),
shape: RoundedRectangleBorder(
borderRadius: AutoCompleteTagItemWebStyle.shapeBorderRadius,
side: _getTagBorderSide(),
),
avatar: currentEmailAddress.emailAddress.isNotEmpty
? AvatarTagItemWidget(tagName: currentEmailAddress.emailAddress)
: null,
onDeleted: () => onDeleteTagAction?.call(currentEmailAddress),
),
),
),
), ),
), ),
), ),
if (isCollapsed) );
TMailButtonWidget.fromText( } else {
margin: AutoCompleteTagItemWebStyle.marginCollapsed, bodyWidget = TextFieldTapRegion(
text: '+${currentListEmailAddress.length - collapsedListEmailAddress.length}', child: InkWell(
onTapActionCallback: () => onShowFullAction?.call(field), onTap: () => isCollapsed ? onShowFullAction?.call(field) : null,
borderRadius: 10, child: bodyWidget,
textStyle: AutoCompleteTagItemWebStyle.collapsedTextStyle, ),
padding: AutoCompleteTagItemWebStyle.collapsedPadding, );
backgroundColor: AutoCompleteTagItemWebStyle.collapsedBackgroundColor, }
)
], final listChildrenWidget = [
); if (isCollapsed)
Flexible(child: bodyWidget)
else
bodyWidget,
if (isCollapsed)
TMailButtonWidget.fromText(
margin: const EdgeInsetsDirectional.only(start: 8),
text: '+${currentListEmailAddress.length - collapsedListEmailAddress.length}',
onTapActionCallback: () => onShowFullAction?.call(field),
borderRadius: 10,
alignment: Alignment.center,
textStyle: AutoCompleteTagItemWebStyle.collapsedTextStyle,
padding: PlatformInfo.isWeb
? const EdgeInsetsDirectional.symmetric(vertical: 4, horizontal: 8)
: const EdgeInsetsDirectional.symmetric(vertical: 6, horizontal: 10),
backgroundColor: AutoCompleteTagItemWebStyle.collapsedBackgroundColor,
)
];
Widget tagWidget;
if (listChildrenWidget.length == 1) {
tagWidget = listChildrenWidget.first;
} else {
tagWidget = Row(
mainAxisSize: MainAxisSize.min,
children: listChildrenWidget,
);
}
if (PlatformInfo.isWeb) {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 3),
child: tagWidget,
);
} else {
return tagWidget;
}
} }
Color _getTagBackgroundColor() { Color _getTagBackgroundColor() {
@@ -37,10 +37,10 @@ class DraggableRecipientTagWidget extends StatelessWidget {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (emailAddress.displayName.isNotEmpty) if (emailAddress.emailAddress.isNotEmpty)
GradientCircleAvatarIcon( GradientCircleAvatarIcon(
colors: emailAddress.avatarColors, colors: emailAddress.avatarColors,
label: emailAddress.displayName.firstLetterToUpperCase, label: emailAddress.emailAddress.firstCharacterToUpperCase,
labelFontSize: DraggableRecipientTagWidgetStyle.avatarLabelFontSize, labelFontSize: DraggableRecipientTagWidgetStyle.avatarLabelFontSize,
iconSize: DraggableRecipientTagWidgetStyle.avatarIconSize, iconSize: DraggableRecipientTagWidgetStyle.avatarIconSize,
), ),
@@ -161,6 +161,7 @@ class EmailRecoveryFormMobileBuilder extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 17), padding: const EdgeInsets.symmetric(horizontal: 17),
onChanged: controller.onChangeHasAttachment, onChanged: controller.onChangeHasAttachment,
)), )),
const SizedBox(height: 32.0),
], ],
), ),
), ),
@@ -170,9 +171,11 @@ class EmailRecoveryFormMobileBuilder extends StatelessWidget {
onRestore: () => controller.onRestore(context), onRestore: () => controller.onRestore(context),
nextFocusNode: controller.focusManager.subjectFieldFocusNode, nextFocusNode: controller.focusManager.subjectFieldFocusNode,
isMobile: true, isMobile: true,
padding: const EdgeInsetsDirectional.symmetric( padding: const EdgeInsetsDirectional.only(
horizontal: 16, start: 16,
vertical: 44, end: 16,
top: 12,
bottom: 44,
), ),
) )
], ],
@@ -3,12 +3,7 @@ import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AutoCompleteTagItemWebStyle { class AutoCompleteTagItemWebStyle {
static const double paddingTop = 3.0;
static const double paddingEnd = 40.0;
static const double labelPaddingHorizontal = 4.0; static const double labelPaddingHorizontal = 4.0;
static const EdgeInsetsGeometry collapsedPadding = EdgeInsetsDirectional.symmetric(vertical: 4, horizontal: 8);
static const EdgeInsetsGeometry marginCollapsed = EdgeInsets.only(top: 3);
static const BorderRadius shapeBorderRadius = BorderRadius.all(Radius.circular(10.0)); static const BorderRadius shapeBorderRadius = BorderRadius.all(Radius.circular(10.0));
@@ -7,29 +7,37 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class AdvancedSearchFieldWidget extends StatelessWidget { class AdvancedSearchFieldWidget extends StatelessWidget {
final FilterField filterField; final FilterField filterField;
final Widget child; final Widget child;
final bool useHeight;
const AdvancedSearchFieldWidget({ const AdvancedSearchFieldWidget({
super.key, super.key,
required this.filterField, required this.filterField,
required this.child, required this.child,
this.useHeight = true,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( final bodyWidget = Row(
height: AdvancedSearchInputFormStyle.inputFieldHeight, children: [
child: Row( SizedBox(
children: [ width: AdvancedSearchInputFormStyle.labelMaxWidth,
SizedBox( child: LabelAdvancedSearchFieldWidget(
width: AdvancedSearchInputFormStyle.labelMaxWidth, name: filterField.getTitle(AppLocalizations.of(context)),
child: LabelAdvancedSearchFieldWidget(
name: filterField.getTitle(AppLocalizations.of(context)),
),
), ),
const SizedBox(width: AdvancedSearchInputFormStyle.labelSpace), ),
Expanded(child: child), const SizedBox(width: AdvancedSearchInputFormStyle.labelSpace),
], Expanded(child: child),
), ],
); );
if (useHeight) {
return SizedBox(
height: AdvancedSearchInputFormStyle.inputFieldHeight,
child: bodyWidget,
);
} else {
return bodyWidget;
}
} }
} }
@@ -23,6 +23,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController> {
children: [ children: [
AdvancedSearchFieldWidget( AdvancedSearchFieldWidget(
filterField: FilterField.from, filterField: FilterField.from,
useHeight: false,
child: Obx(() => DefaultAutocompleteInputFieldWidget( child: Obx(() => DefaultAutocompleteInputFieldWidget(
field: FilterField.from, field: FilterField.from,
listEmailAddress: controller.listFromEmailAddress, listEmailAddress: controller.listFromEmailAddress,
@@ -44,6 +45,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController> {
const SizedBox(height: 12), const SizedBox(height: 12),
AdvancedSearchFieldWidget( AdvancedSearchFieldWidget(
filterField: FilterField.to, filterField: FilterField.to,
useHeight: false,
child: Obx(() => DefaultAutocompleteInputFieldWidget( child: Obx(() => DefaultAutocompleteInputFieldWidget(
field: FilterField.to, field: FilterField.to,
listEmailAddress: controller.listToEmailAddress, listEmailAddress: controller.listToEmailAddress,
@@ -1,5 +1,6 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/extensions/string_extension.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/avatar_tag_item_style.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/avatar_tag_item_style.dart';
@@ -27,7 +28,7 @@ class AvatarTagItemWidget extends StatelessWidget {
) )
), ),
child: Text( child: Text(
tagName.isNotEmpty ? tagName[0].toUpperCase() : '', tagName.firstCharacterToUpperCase,
style: AvatarTagItemStyle.labelTextStyle style: AvatarTagItemStyle.labelTextStyle
) )
); );