diff --git a/assets/images/ic_delete_email_address.svg b/assets/images/ic_delete_email_address.svg
new file mode 100644
index 000000000..1e1f7b061
--- /dev/null
+++ b/assets/images/ic_delete_email_address.svg
@@ -0,0 +1,4 @@
+
diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart
index 36ced66b9..2a742715d 100644
--- a/core/lib/presentation/extensions/color_extension.dart
+++ b/core/lib/presentation/extensions/color_extension.dart
@@ -49,9 +49,9 @@ extension AppColor on Color {
static const subjectEmailTextColorUnRead = Color(0xFF3840F7);
static const dividerColor = Color(0xFFEAEAEA);
static const bgComposer = Color(0xFFFBFBFF);
- static const emailAddressChipColor = Color(0xFFF5F5F7);
- static const enableSendEmailButtonColor = Color(0xFF3840F7);
- static const disableSendEmailButtonColor = Color(0xFFACAFFF);
+ static const emailAddressChipColor = Color(0x0D001C3D);
+ static const enableSendEmailButtonColor = Color(0xFF007AFF);
+ static const disableSendEmailButtonColor = Color(0xFFA9B4C2);
static const borderLeftEmailContentColor = Color(0xFFEFEFEF);
static const toastBackgroundColor = Color(0xFFACAFFF);
static const toastSuccessBackgroundColor = Color(0xFF04AA11);
@@ -85,6 +85,9 @@ extension AppColor on Color {
static const colorActionCancelDialog= Color(0xFF007AFF);
static const colorMessageDialog= Color(0xFF222222);
static const colorConfirmActionDialog= Color(0xFFF2F2F2);
+ static const colorEmailAddress = Color(0xFF333333);
+ static const colorHintEmailAddressInput = Color(0x993C3C43);
+ static const coloDividerComposer = Color(0xFFC6C6C8);
static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)],
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index d859b7d2f..9ec7684b1 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -84,6 +84,7 @@ class ImagePaths {
String get icRenameMailbox => _getImagePath('ic_rename_mailbox.svg');
String get icDeleteToast => _getImagePath('ic_delete_toast.svg');
String get icRemoveDialog => _getImagePath('ic_remove_dialog.svg');
+ String get icDeleteEmailAddress => _getImagePath('ic_delete_email_address.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart
index 00c7c0956..876f5db22 100644
--- a/lib/features/composer/presentation/composer_controller.dart
+++ b/lib/features/composer/presentation/composer_controller.dart
@@ -9,7 +9,6 @@ import 'package:file_picker/file_picker.dart';
import 'package:fk_user_agent/fk_user_agent.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter_chips_input/flutter_chips_input.dart';
import 'package:get/get.dart';
import 'package:html_editor_enhanced/html_editor.dart' as HtmlEditorBrowser;
import 'package:http_parser/http_parser.dart';
@@ -54,6 +53,7 @@ class ComposerController extends BaseController {
final expandMode = ExpandMode.COLLAPSE.obs;
final composerArguments = Rxn();
final isEnableEmailSendButton = false.obs;
+ final isInitialRecipient = false.obs;
final attachments = [].obs;
final emailContents = [].obs;
@@ -79,9 +79,10 @@ class ComposerController extends BaseController {
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.localContact;
HtmlEditorApi? htmlEditorApi;
final HtmlEditorBrowser.HtmlEditorController htmlControllerBrowser = HtmlEditorBrowser.HtmlEditorController();
- final keyToEmailAddress = GlobalKey();
- final keyCcEmailAddress = GlobalKey();
- final keyBccEmailAddress = GlobalKey();
+
+ final toEmailAddressController = TextEditingController();
+ final ccEmailAddressController = TextEditingController();
+ final bccEmailAddressController = TextEditingController();
List initialAttachments = [];
@@ -132,6 +133,18 @@ class ComposerController extends BaseController {
Future.delayed(Duration(milliseconds: 500), () => _checkContactPermission());
}
+ @override
+ void onClose() {
+ subjectEmailInputController.dispose();
+
+ toEmailAddressController.dispose();
+ ccEmailAddressController.dispose();
+ bccEmailAddressController.dispose();
+
+ htmlControllerBrowser.clearFocus();
+ super.onClose();
+ }
+
@override
void onDone() {
viewState.value.fold(
@@ -238,16 +251,8 @@ class ComposerController extends BaseController {
expandMode.value = ExpandMode.COLLAPSE;
}
- if (listToEmailAddress.isNotEmpty) {
- keyToEmailAddress.currentState?.addMultipleValue(listToEmailAddress);
- }
-
- if (listCcEmailAddress.isNotEmpty) {
- keyCcEmailAddress.currentState?.addMultipleValue(listCcEmailAddress);
- }
-
- if (listBccEmailAddress.isNotEmpty) {
- keyBccEmailAddress.currentState?.addMultipleValue(listBccEmailAddress);
+ if (listToEmailAddress.isNotEmpty || listCcEmailAddress.isNotEmpty || listBccEmailAddress.isNotEmpty) {
+ isInitialRecipient.value = true;
}
}
_updateStatusEmailSendButton();
diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart
index ee24991c6..a03647bc6 100644
--- a/lib/features/composer/presentation/composer_view.dart
+++ b/lib/features/composer/presentation/composer_view.dart
@@ -18,6 +18,7 @@ class ComposerView extends GetWidget {
final responsiveUtils = Get.find();
final imagePaths = Get.find();
+ final _appToast = Get.find();
final keyboardUtils = Get.find();
@override
@@ -39,6 +40,7 @@ class ComposerView extends GetWidget {
left: responsiveUtils.isMobileDevice(context) && responsiveUtils.isLandscape(context),
child: Container(
margin: EdgeInsets.zero,
+ padding: EdgeInsets.zero,
alignment: Alignment.topCenter,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -106,19 +108,26 @@ class ComposerView extends GetWidget {
return Container(
margin: EdgeInsets.zero,
padding: EdgeInsets.only(top: 20),
- color: AppColor.bgComposer,
+ color: Colors.white,
alignment: Alignment.topCenter,
child: Column(
children: [
Padding(
- padding: EdgeInsets.symmetric(horizontal: 24),
+ padding: EdgeInsets.only(left: 16),
child: _buildEmailAddress(context)),
+ _buildDivider(),
_buildSubjectEmail(context),
- Divider(color: AppColor.dividerColor, height: 1)
+ _buildDivider(),
],
),
);
}
+
+ Widget _buildDivider(){
+ return Padding(
+ padding: EdgeInsets.only(left: 16),
+ child: Divider(color: AppColor.coloDividerComposer, height: 1));
+ }
Widget _buildEmailAddress(BuildContext context) {
return Column(
@@ -126,7 +135,7 @@ class ComposerView extends GetWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
- padding: EdgeInsets.only(bottom: 8),
+ padding: EdgeInsets.only(bottom: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -136,7 +145,7 @@ class ComposerView extends GetWidget {
'${AppLocalizations.of(context).from_email_address_prefix}:',
overflow: TextOverflow.ellipsis,
maxLines: 1,
- style: TextStyle(fontSize: 14, color: AppColor.nameUserColor, fontWeight: FontWeight.w500))),
+ style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -145,7 +154,7 @@ class ComposerView extends GetWidget {
Obx(() => controller.composerArguments.value != null
? Text(
'<${controller.getEmailAddressSender()}>',
- style: TextStyle(fontSize: 14, color: AppColor.nameUserColor))
+ style: TextStyle(fontSize: 14, color: AppColor.colorEmailAddress, fontWeight: FontWeight.w500))
: SizedBox.shrink()
)
],
@@ -154,37 +163,50 @@ class ComposerView extends GetWidget {
]
)
),
+ Divider(color: AppColor.coloDividerComposer, height: 1),
Obx(() => (EmailAddressInputBuilder(
context,
imagePaths,
+ _appToast,
PrefixEmailAddress.to,
controller.listToEmailAddress,
- keyInput: controller.keyToEmailAddress,
+ controller: controller.toEmailAddressController,
+ isInitial: controller.isInitialRecipient.value,
expandMode: controller.expandMode.value)
..addExpandAddressActionClick(() => controller.expandEmailAddressAction())
..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
.build()
),
+ Obx(() => controller.expandMode.value == ExpandMode.EXPAND
+ ? Divider(color: AppColor.coloDividerComposer, height: 1)
+ : SizedBox.shrink()),
Obx(() => controller.expandMode.value == ExpandMode.EXPAND
? (EmailAddressInputBuilder(
context,
imagePaths,
+ _appToast,
PrefixEmailAddress.cc,
controller.listCcEmailAddress,
- keyInput: controller.keyCcEmailAddress)
+ controller: controller.ccEmailAddressController,
+ isInitial: controller.isInitialRecipient.value,)
..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
.build()
: SizedBox.shrink()
),
+ Obx(() => controller.expandMode.value == ExpandMode.EXPAND
+ ? Divider(color: AppColor.coloDividerComposer, height: 1)
+ : SizedBox.shrink()),
Obx(() => controller.expandMode.value == ExpandMode.EXPAND
? (EmailAddressInputBuilder(
context,
imagePaths,
+ _appToast,
PrefixEmailAddress.bcc,
controller.listBccEmailAddress,
- keyInput: controller.keyBccEmailAddress)
+ controller: controller.bccEmailAddressController,
+ isInitial: controller.isInitialRecipient.value,)
..addOnUpdateListEmailAddressAction((prefixEmailAddress, listEmailAddress) => controller.updateListEmailAddress(prefixEmailAddress, listEmailAddress))
..addOnSuggestionEmailAddress((word) => controller.getAutoCompleteSuggestion(word)))
.build()
@@ -196,22 +218,34 @@ class ComposerView extends GetWidget {
Widget _buildSubjectEmail(BuildContext context) {
return Padding(
- padding: EdgeInsets.only(left: 24, right: 24, bottom: 10, top: 8),
- child: (TextFieldBuilder()
- ..key(Key('subject_email_input'))
- ..textInputAction(TextInputAction.newline)
- ..maxLines(null)
- ..onChange((value) => controller.setSubjectEmail(value))
- ..textStyle(TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500))
- ..textDecoration(InputDecoration(
- hintText: AppLocalizations.of(context).subject_email,
- hintStyle: TextStyle(color: AppColor.baseTextColor, fontSize: 14, fontWeight: FontWeight.w500),
- contentPadding: EdgeInsets.zero,
- filled: true,
- border: InputBorder.none,
- fillColor: AppColor.bgComposer))
- ..addController(controller.subjectEmailInputController))
- .build()
+ padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(right: 8),
+ child: Text(
+ '${AppLocalizations.of(context).subject_email}:',
+ overflow: TextOverflow.ellipsis,
+ maxLines: 1,
+ style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))),
+ Expanded(
+ child: (TextFieldBuilder()
+ ..key(Key('subject_email_input'))
+ ..textInputAction(TextInputAction.newline)
+ ..maxLines(null)
+ ..cursorColor(AppColor.colorTextButton)
+ ..onChange((value) => controller.setSubjectEmail(value))
+ ..textStyle(TextStyle(color: AppColor.colorEmailAddress, fontSize: 15))
+ ..textDecoration(InputDecoration(
+ contentPadding: EdgeInsets.zero,
+ border: InputBorder.none))
+ ..addController(controller.subjectEmailInputController))
+ .build()
+ )
+ ]
+ )
);
}
@@ -222,7 +256,7 @@ class ComposerView extends GetWidget {
children: [
_buildEmailHeader(context),
Container(
- color: AppColor.primaryLightColor,
+ color: Colors.white,
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
alignment: Alignment.topCenter,
diff --git a/lib/features/composer/presentation/widgets/email_address_input_builder.dart b/lib/features/composer/presentation/widgets/email_address_input_builder.dart
index 2a08e01fd..b05fb4dba 100644
--- a/lib/features/composer/presentation/widgets/email_address_input_builder.dart
+++ b/lib/features/composer/presentation/widgets/email_address_input_builder.dart
@@ -1,29 +1,31 @@
+import 'dart:async';
+
import 'package:core/core.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
-import 'package:flutter_chips_input/flutter_chips_input.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
+import 'package:material_tag_editor/tag_editor.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnOpenExpandAddressActionClick = void Function();
typedef OnSuggestionEmailAddress = Future> Function(String word);
-typedef OnUpdateListEmailAddressAction = void Function(
- PrefixEmailAddress prefixEmailAddress,
- List listEmailAddress
-);
+typedef OnUpdateListEmailAddressAction = void Function(PrefixEmailAddress, List);
class EmailAddressInputBuilder {
- final Key? keyInput;
final BuildContext _context;
final ImagePaths _imagePaths;
+ final AppToast _appToast;
final ExpandMode expandMode;
final PrefixEmailAddress _prefixEmailAddress;
final List _listEmailAddress;
+ final TextEditingController? controller;
+ final bool? isInitial;
OnOpenExpandAddressActionClick? _onOpenExpandAddressActionClick;
OnUpdateListEmailAddressAction? _onUpdateListEmailAddressAction;
@@ -44,119 +46,135 @@ class EmailAddressInputBuilder {
EmailAddressInputBuilder(
this._context,
this._imagePaths,
+ this._appToast,
this._prefixEmailAddress,
this._listEmailAddress,
{
- this.keyInput,
+ this.isInitial,
+ this.controller,
this.expandMode = ExpandMode.COLLAPSE,
}
);
Widget build() {
return Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(right: 8, top: 15),
+ child: Text(
+ '${_prefixEmailAddress.asName(_context)}:',
+ style: TextStyle(fontSize: 15, color: AppColor.colorHintEmailAddressInput))),
+ Expanded(child: _buildTagEditor()),
+ if (_prefixEmailAddress == PrefixEmailAddress.to)
Padding(
- padding: EdgeInsets.only(right: 8, top: _listEmailAddress.isNotEmpty ? 20 : 18),
- child: Text(
- '${_prefixEmailAddress.asName(_context)}:',
- style: TextStyle(fontSize: 14, color: AppColor.nameUserColor, fontWeight: FontWeight.w500))),
- Expanded(child: ChipsInput(
- key: keyInput,
- initialValue: _listEmailAddress,
- textStyle: TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500),
- cursorColor: AppColor.primaryColor,
- inputType: TextInputType.emailAddress,
- decoration: InputDecoration(
- border: InputBorder.none,
- hintText: AppLocalizations.of(_context).hint_text_email_address,
- hintMaxLines: 1,
- hintStyle: TextStyle(color: AppColor.baseTextColor, fontSize: 14, fontWeight: FontWeight.w500)),
- findSuggestions: (query) {
- if (query.isNotEmpty && _onSuggestionEmailAddress != null) {
- return _onSuggestionEmailAddress!(query);
- }
- return [];
- },
- onChanged: (data) {
- if (_onUpdateListEmailAddressAction != null) {
- _onUpdateListEmailAddressAction!(_prefixEmailAddress, data);
- }
- },
- onChipInputActionDone: (state, value) {
- if (GetUtils.isEmail(value)) {
- state.selectSuggestion(EmailAddress(value, value));
- }
- },
- onChipInputChangeFocusAction: (state, value) {
- if (GetUtils.isEmail(value)) {
- state.selectSuggestion(EmailAddress(value, value));
- }
- },
- chipBuilder: (context, state, emailAddress) {
- return InputChip(
- key: ObjectKey(emailAddress),
- label: Text(emailAddress.asString()),
- labelStyle: TextStyle(color: AppColor.nameUserColor, fontSize: 12, fontWeight: FontWeight.w500),
- backgroundColor: AppColor.emailAddressChipColor,
- padding: EdgeInsets.all(3),
- deleteIconColor: AppColor.baseTextColor,
- avatar: CircleAvatar(
- backgroundColor: AppColor.avatarColor,
- child: Text(
- emailAddress.asString().isNotEmpty ? emailAddress.asString()[0].toUpperCase() : '',
- style: TextStyle(color: AppColor.appColor, fontSize: 12, fontWeight: FontWeight.w500))),
- onDeleted: () => state.deleteChip(emailAddress),
- materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
- );
- },
- suggestionBuilder: (context, state, emailAddress) {
- return Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(8),
- border: Border.all(color: Colors.white),
- color: Colors.white),
- child: ListTile(
- key: ObjectKey(emailAddress),
- leading: CircleAvatar(
- backgroundColor: AppColor.avatarColor,
- child: Text(
- emailAddress.asString().isNotEmpty ? emailAddress.asString()[0].toUpperCase() : '',
- style: TextStyle(color: AppColor.appColor, fontSize: 16, fontWeight: FontWeight.w500))),
- title: Text(
- emailAddress.asString(),
- style: TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500)),
- subtitle: emailAddress.displayName.isNotEmpty && emailAddress.emailAddress.isNotEmpty
- ? Text(
- emailAddress.emailAddress,
- style: TextStyle(color: AppColor.baseTextColor, fontSize: 12, fontWeight: FontWeight.w500))
- : null,
- onTap: () => state.selectSuggestion(emailAddress),
- ),
- );
- }
- )),
- if (_prefixEmailAddress == PrefixEmailAddress.to)
- Padding(
- padding: EdgeInsets.only(top: _listEmailAddress.isNotEmpty ? 6 : 3),
- child: _buildButtonExpandAddress())
- ]
+ padding: EdgeInsets.only(top: 4),
+ child: _buildButtonExpandAddress())
+ ]
);
}
Widget _buildButtonExpandAddress() {
- return IconButton(
- color: AppColor.baseTextColor,
- icon: SvgPicture.asset(
- expandMode == ExpandMode.EXPAND ? _imagePaths.icExpandAddress : _imagePaths.icMoreReceiver,
- width: 20,
- height: 20,
- fit: BoxFit.fill),
- onPressed: () {
- if (_onOpenExpandAddressActionClick != null) {
- _onOpenExpandAddressActionClick!();
- }
- }
+ return Material(
+ type: MaterialType.circle,
+ color: Colors.transparent,
+ child: IconButton(
+ icon: SvgPicture.asset(
+ expandMode == ExpandMode.EXPAND ? _imagePaths.icExpandAddress : _imagePaths.icMoreReceiver,
+ width: 20,
+ height: 20,
+ fit: BoxFit.fill),
+ onPressed: () => _onOpenExpandAddressActionClick?.call()
+ )
+ );
+ }
+
+ Widget _buildTagEditor() {
+ return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
+ return TagEditor(
+ length: _listEmail.length,
+ controller: controller,
+ keyboardType: TextInputType.emailAddress,
+ textInputAction: TextInputAction.done,
+ hasAddButton: false,
+ resetTextOnSubmitted: true,
+ textStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500),
+ onSubmitted: (value) {
+ if (GetUtils.isEmail(value)) {
+ setState(() => _listEmailAddress.add(EmailAddress(value, value)));
+ _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
+ } else {
+ _appToast.showErrorToast(AppLocalizations.of(context).email_address_is_not_in_the_correct_format);
+ }
+ },
+ inputDecoration: InputDecoration(
+ border: InputBorder.none,
+ hintText: AppLocalizations.of(_context).hint_text_email_address,
+ hintMaxLines: 1,
+ hintStyle: TextStyle(color: AppColor.colorHintEmailAddressInput, fontSize: 14, fontWeight: FontWeight.w500)
+ ),
+ tagBuilder: (context, index) => Padding(
+ padding: EdgeInsets.only(top: kIsWeb ? 8 : 0),
+ child: Chip(
+ labelPadding: EdgeInsets.only(left: 8, right: 8, bottom: 2),
+ label: Text(_listEmail[index], maxLines: 1, overflow: TextOverflow.ellipsis),
+ deleteIcon: SvgPicture.asset(_imagePaths.icDeleteEmailAddress, fit: BoxFit.fill),
+ labelStyle: TextStyle(color: AppColor.colorEmailAddress, fontSize: 14, fontWeight: FontWeight.w500),
+ backgroundColor: AppColor.emailAddressChipColor,
+ avatar: CircleAvatar(
+ backgroundColor: AppColor.colorTextButton,
+ child: Text(
+ _listEmail[index].isNotEmpty ? _listEmail[index][0].toUpperCase() : '',
+ style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500))),
+ onDeleted: () {
+ setState(() => _listEmailAddress.removeAt(index));
+ _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
+ },
+ )),
+ onTagChanged: (String value) {},
+ findSuggestions: (String query) {
+ if (query.isNotEmpty && _onSuggestionEmailAddress != null) {
+ return _onSuggestionEmailAddress!(query);
+ }
+ return [];
+ },
+ suggestionBuilder: (context, tagEditorState, emailAddress) {
+ return _buildSuggestionItem(setState, context, tagEditorState, emailAddress);
+ },
+ );
+ });
+ }
+
+ List get _listEmail => _listEmailAddress.map((emailAddress) => emailAddress.asString()).toList();
+
+ Widget _buildSuggestionItem(StateSetter setState, BuildContext context, TagsEditorState tagEditorState, EmailAddress emailAddress) {
+ return Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: Colors.white),
+ color: Colors.white),
+ child: ListTile(
+ key: ObjectKey(emailAddress),
+ leading: CircleAvatar(
+ backgroundColor: AppColor.avatarColor,
+ child: Text(
+ emailAddress.asString().isNotEmpty ? emailAddress.asString()[0].toUpperCase() : '',
+ style: TextStyle(color: AppColor.appColor, fontSize: 16, fontWeight: FontWeight.w500))),
+ title: Text(
+ emailAddress.asString(),
+ style: TextStyle(color: AppColor.nameUserColor, fontSize: 14, fontWeight: FontWeight.w500)),
+ subtitle: emailAddress.displayName.isNotEmpty && emailAddress.emailAddress.isNotEmpty
+ ? Text(
+ emailAddress.emailAddress,
+ style: TextStyle(color: AppColor.baseTextColor, fontSize: 12, fontWeight: FontWeight.w500))
+ : null,
+ onTap: () {
+ setState(() => _listEmailAddress.add(emailAddress));
+ tagEditorState.selectSuggestion(emailAddress);
+ _onUpdateListEmailAddressAction?.call(_prefixEmailAddress, _listEmailAddress);
+ },
+ ),
);
}
}
\ No newline at end of file
diff --git a/lib/features/composer/presentation/widgets/suggestions_box_controller.dart b/lib/features/composer/presentation/widgets/suggestions_box_controller.dart
new file mode 100644
index 000000000..d16c83918
--- /dev/null
+++ b/lib/features/composer/presentation/widgets/suggestions_box_controller.dart
@@ -0,0 +1,35 @@
+import 'package:flutter/material.dart';
+
+class SuggestionsBoxController {
+ final BuildContext context;
+
+ OverlayEntry? overlayEntry;
+
+ bool _isOpened = false;
+
+ bool get isOpened => _isOpened;
+
+ SuggestionsBoxController(this.context);
+
+ void open() {
+ if (_isOpened) return;
+ assert(overlayEntry != null);
+ Overlay.of(context)?.insert(overlayEntry!);
+ _isOpened = true;
+ }
+
+ void close() {
+ if (!_isOpened) return;
+ assert(overlayEntry != null);
+ overlayEntry!.remove();
+ _isOpened = false;
+ }
+
+ void toggle() {
+ if (_isOpened) {
+ close();
+ } else {
+ open();
+ }
+ }
+}
diff --git a/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart b/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart
index 325f6cd4c..a6dde007d 100644
--- a/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart
+++ b/lib/features/composer/presentation/widgets/top_bar_composer_widget_builder.dart
@@ -68,16 +68,9 @@ class TopBarComposerWidgetBuilder {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- // IconButton(
- // key: Key('button_file_share'),
- // color: AppColor.baseTextColor,
- // icon: SvgPicture.asset(_imagePaths.icComposerFileShare, color: AppColor.baseTextColor, fit: BoxFit.fill),
- // onPressed: () {}
- // ),
IconButton(
key: Key('button_attachment'),
- color: AppColor.baseTextColor,
- icon: SvgPicture.asset(_imagePaths.icShare, color: AppColor.baseTextColor, fit: BoxFit.fill),
+ icon: SvgPicture.asset(_imagePaths.icShare, fit: BoxFit.fill),
onPressed: () {
if (_onAttachFileActionClick != null) {
_onAttachFileActionClick!();
@@ -98,12 +91,6 @@ class TopBarComposerWidgetBuilder {
}
),
SizedBox(width: 8)
- // IconButton(
- // key: Key('button_menu_composer'),
- // color: AppColor.baseTextColor,
- // icon: SvgPicture.asset(_imagePaths.icComposerMenu, color: AppColor.baseTextColor, fit: BoxFit.fill),
- // onPressed: () {}
- // )
]
);
}
diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart
index 90a805f0d..c961c6448 100644
--- a/lib/main/localizations/app_localizations.dart
+++ b/lib/main/localizations/app_localizations.dart
@@ -722,4 +722,11 @@ class AppLocalizations {
name: 'there_is_already_folder_with_the_same_name',
);
}
+
+ String get email_address_is_not_in_the_correct_format {
+ return Intl.message(
+ 'Email address is not in the correct format',
+ name: 'email_address_is_not_in_the_correct_format',
+ );
+ }
}
\ No newline at end of file
diff --git a/pubspec.yaml b/pubspec.yaml
index e5f15eb10..0eb28f40a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -80,11 +80,11 @@ dependencies:
# file_size
filesize: 2.0.1
- # flutter_chips_input
- flutter_chips_input:
+ # material_tag_editor
+ material_tag_editor:
git:
- url: https://github.com/dab246/flutter_chips_input.git
- ref: master
+ url: https://github.com/dab246/material_tag_editor.git
+ ref: feature/support_suggestion_box
# uuid
uuid: 3.0.4