TF-1477 Improve UX for advanced and quick search

This commit is contained in:
dab246
2023-03-03 14:20:48 +07:00
committed by Dat Vu
parent a0b51ae22d
commit b3ec90b507
20 changed files with 764 additions and 503 deletions
@@ -4,11 +4,12 @@ import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
import 'package:tmail_ui_user/features/base/widget/popup_item_no_icon_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.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/model/search/email_receive_time_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_filter_form_bottom_view.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/drop_down_button_filter_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/date_drop_down_button.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_auto_complete_email_adress.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -73,7 +74,6 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
onTap: () => controller.selectedMailBox(context)),
Row(children: [
Expanded(child: _buildFilterField(
textEditingController: controller.dateFilterInputController,
context: context,
advancedSearchFilterField: AdvancedSearchFilterField.date,
isSelectFormList: true,
@@ -102,52 +102,22 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
List<Widget> _buildEmailReceiveTimeTypeActionTiles(BuildContext context) {
return EmailReceiveTimeType.values
.map(
(e) => Material(
child: PopupMenuItem(
child: Row(children: [
const SizedBox(width: 12),
Expanded(
child: Text(
e.getTitle(
context,
startDate: controller.startDate,
endDate: controller.endDate),
style: const TextStyle(
fontSize: 15,
color: Colors.black,
fontWeight: FontWeight.w500))),
if (e == controller.dateFilterSelectedFormAdvancedSearch.value)
...[
const SizedBox(width: 12),
SvgPicture.asset(
_imagePaths.icFilterSelected,
width: 16,
height: 16,
fit: BoxFit.fill,
),
]
]),
onTap: () {
if (e != EmailReceiveTimeType.customRange) {
controller.clearDateRangeOfFilter();
}
controller.dateFilterSelectedFormAdvancedSearch.value = e;
controller.dateFilterInputController.text = e.getTitle(
context,
startDate: controller.startDate,
endDate: controller.endDate);
},
),
),
)
.toList();
.map((receiveTime) => PopupMenuItem(
padding: EdgeInsets.zero,
child: PopupItemNoIconWidget(
receiveTime.getTitle(context),
svgIconSelected: _imagePaths.icFilterSelected,
maxWidth: 320,
isSelected: controller.dateFilterSelectedFormAdvancedSearch.value == receiveTime,
onCallbackAction: () => controller.updateReceiveDateSearchFilter(context, receiveTime),
)))
.toList();
}
Widget _buildFilterField({
required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField,
required TextEditingController textEditingController,
TextEditingController? textEditingController,
VoidCallback? onTap,
bool isSelectFormList = false,
MouseCursor? mouseCursor,
@@ -180,7 +150,13 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
)
else if (_responsiveUtils.landscapeTabletSupported(context))
if (advancedSearchFilterField == AdvancedSearchFilterField.date)
const DateDropDownButton()
Obx(() => DateDropDownButton(
_imagePaths,
startDate: controller.startDate.value,
endDate: controller.endDate.value,
receiveTimeSelected: controller.dateFilterSelectedFormAdvancedSearch.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
))
else
_buildTextField(
isSelectFormList: isSelectFormList,
@@ -222,7 +198,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Widget _buildTextFieldFilterForWeb({
required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField,
required TextEditingController textEditingController,
TextEditingController? textEditingController,
VoidCallback? onTap,
bool isSelectFormList = false,
MouseCursor? mouseCursor,
@@ -231,7 +207,13 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
}) {
switch (advancedSearchFilterField) {
case AdvancedSearchFilterField.date:
return const DateDropDownButton();
return Obx(() => DateDropDownButton(
_imagePaths,
startDate: controller.startDate.value,
endDate: controller.endDate.value,
receiveTimeSelected: controller.dateFilterSelectedFormAdvancedSearch.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
));
default:
return _buildTextField(
isSelectFormList: isSelectFormList,
@@ -357,7 +339,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Widget _buildTextField({
required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField,
required TextEditingController textEditingController,
TextEditingController? textEditingController,
VoidCallback? onTap,
bool isSelectFormList = false,
MouseCursor? mouseCursor,
@@ -0,0 +1,130 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
class DateDropDownButton extends StatelessWidget {
final ImagePaths imagePaths;
final DateTime? startDate;
final DateTime? endDate;
final EmailReceiveTimeType? receiveTimeSelected;
final Function(EmailReceiveTimeType)? onReceiveTimeSelected;
const DateDropDownButton(
this.imagePaths, {
Key? key,
this.startDate,
this.endDate,
this.receiveTimeSelected,
this.onReceiveTimeSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: PointerInterceptor(
child: DropdownButton2<EmailReceiveTimeType>(
isExpanded: true,
items: EmailReceiveTimeType.values
.map((item) => _buildItemMenu(context, item))
.toList(),
value: receiveTimeSelected,
customButton: Container(
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: AppColor.colorInputBorderCreateMailbox,
width: 1,
),
color: AppColor.colorInputBackgroundCreateMailbox
),
padding: const EdgeInsets.only(left: 12, right: 10),
child: Row(children: [
Expanded(child: Text(
receiveTimeSelected?.getTitle(context, startDate: startDate, endDate: endDate) ?? '',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
),
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
)),
SvgPicture.asset(imagePaths.icDropDown)
]),
),
onChanged: (value) {
if (value != null) {
onReceiveTimeSelected?.call(value);
}
},
icon: SvgPicture.asset(imagePaths.icDropDown),
buttonPadding: const EdgeInsets.symmetric(horizontal: 12),
buttonDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: AppColor.colorInputBorderCreateMailbox,
width: 1,
),
color: AppColor.colorInputBackgroundCreateMailbox,
),
itemHeight: 44,
buttonHeight: 44,
selectedItemHighlightColor: Colors.white,
itemPadding: const EdgeInsets.symmetric(horizontal: 12),
dropdownMaxHeight: 200,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
offset: const Offset(0.0, -8.0),
dropdownElevation: 4,
scrollbarRadius: const Radius.circular(40),
scrollbarThickness: 6
),
),
);
}
DropdownMenuItem<EmailReceiveTimeType> _buildItemMenu(
BuildContext context,
EmailReceiveTimeType receiveTime
) {
return DropdownMenuItem<EmailReceiveTimeType>(
value: receiveTime,
child: PointerInterceptor(
child: Container(
color: Colors.transparent,
height: 44,
child: Row(children: [
Expanded(child: Text(
receiveTime.getTitle(context),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
),
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
)),
if (receiveTime == receiveTimeSelected)
SvgPicture.asset(
imagePaths.icChecked,
width: 20,
height: 20,
fit: BoxFit.fill
)
]),
),
),
);
}
}
@@ -1,6 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
@@ -34,9 +35,8 @@ class IconOpenAdvancedSearchWidget extends StatelessWidget {
width: 16,
height: 16),
onTap: () {
if(searchController.isAdvancedSearchViewOpen.isFalse && searchController.advancedSearchIsActivated.isFalse){
advancedFilterController.initSearchFilterField(context);
}
log('IconOpenAdvancedSearchWidget::build(): clicked');
advancedFilterController.initSearchFilterField(context);
searchController.showAdvancedFilterView(_parentContext);
}),
),
@@ -128,10 +128,7 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
},
buttonActionCallback: (filterAction) {
if (filterAction is QuickSearchFilter) {
dashBoardController.selectQuickSearchFilter(
quickSearchFilter: filterAction,
fromSuggestionBox: true,
);
dashBoardController.addFilterToSuggestionForm(filterAction);
}
},
listActionPadding: const EdgeInsets.only(
@@ -210,43 +207,31 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
}
Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter filter) {
final controller = dashBoardController.searchController;
return Obx(() {
final isFilterSelected = dashBoardController.checkQuickSearchFilterSelected(
quickSearchFilter: filter);
final isFilterSelected = filter.isApplied(dashBoardController.searchController.listFilterOnSuggestionForm);
return Chip(
labelPadding: const EdgeInsets.only(
top: 2,
bottom: 2,
right: 10),
top: 2,
bottom: 2,
right: 10),
label: Text(
filter.getTitle(
context,
receiveTimeType: controller.emailReceiveTimeType.value),
filter.getName(context),
maxLines: 1,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: filter.getTextStyle(
quickSearchFilterSelected: isFilterSelected),
style: filter.getTextStyle(isFilterSelected: isFilterSelected),
),
avatar: SvgPicture.asset(
filter.getIcon(
imagePaths,
quickSearchFilterSelected: isFilterSelected),
width: 16,
height: 16,
fit: BoxFit.fill),
labelStyle: filter.getTextStyle(
quickSearchFilterSelected: isFilterSelected),
backgroundColor: filter.getBackgroundColor(
quickSearchFilterSelected: isFilterSelected),
filter.getIcon(imagePaths, isFilterSelected: isFilterSelected),
width: 16,
height: 16,
fit: BoxFit.fill),
labelStyle: filter.getTextStyle(isFilterSelected: isFilterSelected),
backgroundColor: filter.getBackgroundColor(isFilterSelected: isFilterSelected),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: filter.getBackgroundColor(
quickSearchFilterSelected: isFilterSelected)),
side: BorderSide(color: filter.getBackgroundColor(isFilterSelected: isFilterSelected)),
),
);
});