enable remove suggestion tag in from field
This commit is contained in:
+72
-18
@@ -26,16 +26,16 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
|
||||
child: Column(
|
||||
children: [
|
||||
_buildSuggestionFilterField(
|
||||
listTagSelected: controller.listTagFromSelected,
|
||||
context: context,
|
||||
advancedSearchFilterField: AdvancedSearchFilterField.form,
|
||||
listTagInitial: controller.searchEmailFilter.from.toList()
|
||||
listTagSelected: controller.searchEmailFilter.from,
|
||||
context: context,
|
||||
advancedSearchFilterField: AdvancedSearchFilterField.form,
|
||||
listTagInitial: controller.searchEmailFilter.from,
|
||||
),
|
||||
_buildSuggestionFilterField(
|
||||
listTagSelected: controller.listTagToSelected,
|
||||
context: context,
|
||||
advancedSearchFilterField: AdvancedSearchFilterField.to,
|
||||
listTagInitial: controller.searchEmailFilter.to.toList()
|
||||
listTagSelected: controller.searchEmailFilter.to,
|
||||
context: context,
|
||||
advancedSearchFilterField: AdvancedSearchFilterField.to,
|
||||
listTagInitial: controller.searchEmailFilter.to,
|
||||
),
|
||||
_buildFilterField(
|
||||
textEditingController: controller.subjectFilterInputController,
|
||||
@@ -91,11 +91,15 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
|
||||
fontSize: 15,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w500))),
|
||||
if (e ==
|
||||
controller.dateFilterSelectedFormAdvancedSearch.value) ...[
|
||||
if (e == controller.dateFilterSelectedFormAdvancedSearch.value)
|
||||
...[
|
||||
const SizedBox(width: 12),
|
||||
SvgPicture.asset(_imagePaths.icFilterSelected,
|
||||
width: 16, height: 16, fit: BoxFit.fill),
|
||||
SvgPicture.asset(
|
||||
_imagePaths.icFilterSelected,
|
||||
width: 16,
|
||||
height: 16,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
]
|
||||
]),
|
||||
onTap: () {
|
||||
@@ -182,8 +186,8 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
|
||||
Widget _buildSuggestionFilterField({
|
||||
required AdvancedSearchFilterField advancedSearchFilterField,
|
||||
required BuildContext context,
|
||||
required List<String> listTagSelected,
|
||||
required List<String> listTagInitial,
|
||||
required Set<String> listTagSelected,
|
||||
required Set<String> listTagInitial,
|
||||
}) {
|
||||
final child = [
|
||||
SizedBox(
|
||||
@@ -204,7 +208,32 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
|
||||
},
|
||||
advancedSearchFilterField: advancedSearchFilterField,
|
||||
initialTags: listTagInitial,
|
||||
listTagSelected: listTagSelected,
|
||||
onAddTag: (value) {
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.form) {
|
||||
controller.searchEmailFilter.from.add(value);
|
||||
}
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.to) {
|
||||
controller.searchEmailFilter.to.add(value);
|
||||
}
|
||||
},
|
||||
onDeleteTag: (tag) {
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.form) {
|
||||
controller.searchEmailFilter.from.remove(tag);
|
||||
controller.lastTextForm.value = '';
|
||||
}
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.to) {
|
||||
controller.searchEmailFilter.to.remove(tag);
|
||||
controller.lastTextTo.value = '';
|
||||
}
|
||||
},
|
||||
onChange: (value) {
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.form) {
|
||||
controller.lastTextForm.value = value;
|
||||
}
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.to) {
|
||||
controller.lastTextTo.value = value;
|
||||
}
|
||||
},
|
||||
)
|
||||
: Expanded(
|
||||
child: TextFieldAutoCompleteEmailAddress(
|
||||
@@ -213,7 +242,33 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
|
||||
},
|
||||
advancedSearchFilterField: advancedSearchFilterField,
|
||||
initialTags: listTagInitial,
|
||||
listTagSelected: listTagSelected,
|
||||
onAddTag: (value) {
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.form) {
|
||||
controller.searchEmailFilter.from.add(value.trim());
|
||||
}
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.to) {
|
||||
controller.searchEmailFilter.to.add(value.trim());
|
||||
}
|
||||
},
|
||||
onChange: (value) {
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.form) {
|
||||
controller.lastTextForm.value = value.trim();
|
||||
}
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.to) {
|
||||
controller.lastTextTo.value = value.trim();
|
||||
}
|
||||
},
|
||||
onDeleteTag: (tag) {
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.form) {
|
||||
controller.searchEmailFilter.from.remove(tag);
|
||||
controller.lastTextForm.value = '';
|
||||
}
|
||||
if (advancedSearchFilterField == AdvancedSearchFilterField.to) {
|
||||
controller.searchEmailFilter.to.remove(tag);
|
||||
controller.lastTextTo.value = '';
|
||||
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
];
|
||||
@@ -267,8 +322,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
|
||||
fontSize: 14,
|
||||
color: AppColor.colorHintSearchBar,
|
||||
),
|
||||
suffixIconConstraints:
|
||||
const BoxConstraints(minHeight: 24, minWidth: 24),
|
||||
suffixIconConstraints: const BoxConstraints(minHeight: 24, minWidth: 24),
|
||||
suffixIcon: isSelectFormList
|
||||
? buildIconWeb(
|
||||
icon: SvgPicture.asset(
|
||||
|
||||
+88
-37
@@ -7,15 +7,19 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||
|
||||
class AdvancedSearchFilterFormBottomView
|
||||
extends GetWidget<AdvancedFilterController> {
|
||||
const AdvancedSearchFilterFormBottomView({Key? key}) : super(key: key);
|
||||
const AdvancedSearchFilterFormBottomView({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ResponsiveUtils _responsiveUtils = Get.find<ResponsiveUtils>();
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: _responsiveUtils.isMobile(context) ? 8 : 20),
|
||||
padding:
|
||||
EdgeInsets.only(top: _responsiveUtils.isMobile(context) ? 8 : 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (_responsiveUtils.isMobile(context))
|
||||
_buildCheckboxHasAttachment(context),
|
||||
@@ -26,31 +30,11 @@ class AdvancedSearchFilterFormBottomView
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
if (!_responsiveUtils.isMobile(context))
|
||||
Expanded(child: _buildCheckboxHasAttachment(context)),
|
||||
_buildButton(
|
||||
onAction: () {
|
||||
controller.cleanSearchFilter(context);
|
||||
popBack();
|
||||
},
|
||||
colorButton: Colors.transparent,
|
||||
colorText: AppColor.colorMessageConfirmDialog,
|
||||
text: AppLocalizations.of(context).clearFilter,
|
||||
context: context,
|
||||
responsiveUtils: _responsiveUtils,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_buildButton(
|
||||
onAction: () {
|
||||
controller.applyAdvancedSearchFilter(context);
|
||||
popBack();
|
||||
},
|
||||
colorButton: AppColor.primaryColor,
|
||||
colorText: AppColor.primaryLightColor,
|
||||
text: AppLocalizations.of(context).search,
|
||||
context: context,
|
||||
responsiveUtils: _responsiveUtils,
|
||||
),
|
||||
if (!_responsiveUtils.isMobile(context))...[
|
||||
_buildCheckboxHasAttachment(context),
|
||||
const Spacer(),
|
||||
],
|
||||
..._buildListButton(context, _responsiveUtils),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -58,16 +42,80 @@ class AdvancedSearchFilterFormBottomView
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildListButton(
|
||||
BuildContext context, ResponsiveUtils responsiveUtils) {
|
||||
if (responsiveUtils.isMobile(context)) {
|
||||
return [
|
||||
Expanded(
|
||||
child: _buildButton(
|
||||
onAction: () {
|
||||
controller.cleanSearchFilter(context);
|
||||
popBack();
|
||||
},
|
||||
colorButton: AppColor.primaryColor.withOpacity(0.06),
|
||||
colorText: AppColor.primaryColor,
|
||||
text: AppLocalizations.of(context).clearFilter,
|
||||
context: context,
|
||||
responsiveUtils: responsiveUtils,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: _buildButton(
|
||||
onAction: () {
|
||||
controller.applyAdvancedSearchFilter(context);
|
||||
popBack();
|
||||
},
|
||||
colorButton: AppColor.primaryColor,
|
||||
colorText: AppColor.primaryLightColor,
|
||||
text: AppLocalizations.of(context).search,
|
||||
context: context,
|
||||
responsiveUtils: responsiveUtils,
|
||||
),
|
||||
),
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
_buildButton(
|
||||
onAction: () {
|
||||
controller.cleanSearchFilter(context);
|
||||
popBack();
|
||||
},
|
||||
colorButton: AppColor.primaryColor.withOpacity(0.06),
|
||||
colorText: AppColor.primaryColor,
|
||||
text: AppLocalizations.of(context).clearFilter,
|
||||
context: context,
|
||||
responsiveUtils: responsiveUtils,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
_buildButton(
|
||||
onAction: () {
|
||||
controller.applyAdvancedSearchFilter(context);
|
||||
popBack();
|
||||
},
|
||||
colorButton: AppColor.primaryColor,
|
||||
colorText: AppColor.primaryLightColor,
|
||||
text: AppLocalizations.of(context).search,
|
||||
context: context,
|
||||
responsiveUtils: responsiveUtils,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildCheckboxHasAttachment(BuildContext context) {
|
||||
return Obx(
|
||||
() => CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
value: controller.hasAttachment.value,
|
||||
onChanged: (value) {
|
||||
controller.hasAttachment.value = value ?? false;
|
||||
},
|
||||
title: Text(AppLocalizations.of(context).hasAttachment),
|
||||
() => SizedBox(
|
||||
width: 220,
|
||||
child: CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
value: controller.hasAttachment.value,
|
||||
onChanged: (value) {
|
||||
controller.hasAttachment.value = value ?? false;
|
||||
},
|
||||
title: Text(AppLocalizations.of(context).hasAttachment),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -87,8 +135,11 @@ class AdvancedSearchFilterFormBottomView
|
||||
},
|
||||
child: Container(
|
||||
height: 44,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 26),
|
||||
constraints: BoxConstraints(maxWidth: responsiveUtils.isMobile(context) ? double.infinity : 144),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: responsiveUtils.isMobile(context) ? 0 : 26),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth:
|
||||
responsiveUtils.isMobile(context) ? double.infinity : 144),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10), color: colorButton),
|
||||
|
||||
+13
-8
@@ -10,18 +10,23 @@ class AdvancedSearchFilterOverlay extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 660,
|
||||
maxHeight: 568,
|
||||
),
|
||||
width: 660,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
padding: const EdgeInsets.all(32),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColor.colorShadowBgContentEmail,
|
||||
spreadRadius: 1, blurRadius: 1, offset: Offset(0, 0.5)),
|
||||
]
|
||||
),
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColor.colorShadowBgContentEmail,
|
||||
spreadRadius: 1,
|
||||
blurRadius: 1,
|
||||
offset: Offset(0, 0.5)),
|
||||
]),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 12),
|
||||
|
||||
+36
-31
@@ -8,20 +8,25 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||
import 'package:model/extensions/email_address_extension.dart';
|
||||
import 'package:textfield_tags/textfield_tags.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/custom_tf_tag_controller.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
|
||||
class TextFieldAutoCompleteEmailAddress extends StatefulWidget {
|
||||
const TextFieldAutoCompleteEmailAddress({
|
||||
const TextFieldAutoCompleteEmailAddress({
|
||||
Key? key,
|
||||
required this.advancedSearchFilterField,
|
||||
required this.listTagSelected,
|
||||
required this.initialTags,
|
||||
required this.optionsBuilder,
|
||||
required this.onChange,
|
||||
required this.onDeleteTag,
|
||||
required this.onAddTag,
|
||||
}) : super(key: key);
|
||||
final AdvancedSearchFilterField advancedSearchFilterField;
|
||||
final List<String> listTagSelected;
|
||||
final List<String> initialTags;
|
||||
final Set<String> initialTags;
|
||||
final Future<List<EmailAddress>> Function(String) optionsBuilder;
|
||||
final Function(String) onChange;
|
||||
final Function(String) onDeleteTag;
|
||||
final Function(String) onAddTag;
|
||||
|
||||
@override
|
||||
State<TextFieldAutoCompleteEmailAddress> createState() =>
|
||||
@@ -32,20 +37,23 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
extends State<TextFieldAutoCompleteEmailAddress> {
|
||||
final double _distanceToField = 380;
|
||||
final ImagePaths _imagePaths = Get.find<ImagePaths>();
|
||||
late TextfieldTagsController _controller;
|
||||
late CustomController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_controller = TextfieldTagsController();
|
||||
_controller.addListener(() {
|
||||
if(_controller.hasTags){
|
||||
widget.listTagSelected.addAll(_controller.getTags!);
|
||||
}
|
||||
_controller = CustomController();
|
||||
_controller.setActionRemoveTag((tag) {
|
||||
widget.onDeleteTag.call(tag);
|
||||
});
|
||||
_controller.setActionAddTag((tag) {
|
||||
widget.onAddTag.call(tag);
|
||||
});
|
||||
_controller.setActionChangeText((tag) {
|
||||
widget.onChange.call(tag);
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Autocomplete<EmailAddress>(
|
||||
@@ -85,7 +93,7 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
},
|
||||
fieldViewBuilder: (context, ttec, tfn, onFieldSubmitted) {
|
||||
return TextFieldTags(
|
||||
initialTags: widget.initialTags,
|
||||
initialTags: widget.initialTags.toList(),
|
||||
textEditingController: ttec,
|
||||
focusNode: tfn,
|
||||
textfieldTagsController: _controller,
|
||||
@@ -123,28 +131,27 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
Radius.circular(10),
|
||||
),
|
||||
),
|
||||
hintText:
|
||||
widget.advancedSearchFilterField.getHintText(context),
|
||||
hintText: widget.advancedSearchFilterField.getHintText(context),
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColor.colorHintSearchBar,
|
||||
),
|
||||
prefixIconConstraints:
|
||||
BoxConstraints(maxWidth: _distanceToField * 0.74),
|
||||
prefixIcon: tags.isNotEmpty
|
||||
? SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
controller: sc,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: tags.map((String tag) {
|
||||
return _buildTagItem(context, tag, onTagDelete);
|
||||
}).toList()),
|
||||
)
|
||||
prefixIconConstraints: BoxConstraints(maxWidth: _distanceToField * 0.74),
|
||||
prefixIcon: tags.isNotEmpty ? SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
controller: sc,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: tags.map((String tag) {
|
||||
return _buildTagItem(context, tag, onTagDelete);
|
||||
}).toList()),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onChanged: onChanged,
|
||||
onSubmitted: (tag){
|
||||
onChanged: (value) {
|
||||
onChanged?.call(value);
|
||||
},
|
||||
onSubmitted: (tag) {
|
||||
onSubmitted?.call(tag);
|
||||
},
|
||||
);
|
||||
@@ -250,9 +257,8 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
fontWeight: FontWeight.normal)),
|
||||
const SizedBox(width: 4),
|
||||
GestureDetector(
|
||||
onTap: (){
|
||||
onTap: () {
|
||||
onTagDelete.call(tag);
|
||||
widget.listTagSelected.remove(tag);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
_imagePaths.icClose,
|
||||
@@ -281,6 +287,5 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_controller.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user