TF-4004 Apply new design for create rule view on mobile

This commit is contained in:
dab246
2025-09-08 16:35:06 +07:00
committed by Dat H. Pham
parent 8c3c7b1feb
commit a0123c41b0
19 changed files with 620 additions and 1105 deletions
@@ -6,7 +6,6 @@ import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/c
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/context_menu/context_item_rule_condition_field_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/context_menu/context_item_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
@@ -37,7 +36,7 @@ extension SelectRuleActionFieldExtension on RulesFilterCreatorController {
itemActions: contextMenuActions,
onContextMenuActionClick: (menuAction) {
popBack();
selectConditionCombiner(menuAction.action);
updateConditionCombiner(menuAction.action);
},
);
}
@@ -46,10 +45,10 @@ extension SelectRuleActionFieldExtension on RulesFilterCreatorController {
BuildContext context,
Field? currentField,
Field selectedField,
RuleFilterConditionScreenType screenType,
bool isMobile,
int index,
) {
if (screenType == RuleFilterConditionScreenType.mobile) {
if (isMobile) {
unFocusAllInputField();
final contextMenuActions = Field.values.map((field) {
@@ -78,10 +77,10 @@ extension SelectRuleActionFieldExtension on RulesFilterCreatorController {
BuildContext context,
Comparator? currentComparator,
Comparator selectedComparator,
RuleFilterConditionScreenType screenType,
bool isMobile,
int index,
) {
if (screenType == RuleFilterConditionScreenType.mobile) {
if (isMobile) {
unFocusAllInputField();
final contextMenuActions = Comparator.values.map((comparator) {
@@ -1,5 +1,4 @@
enum RuleFilterConditionScreenType {
mobile,
tablet,
desktop
}
@@ -34,6 +34,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/model/edit_email_ru
import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_rules_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_rules_interactor.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/list_rule_filter_action_argument_extension.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/select_rule_action_field_extension.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/creator_action_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_action_arguments.dart';
@@ -618,12 +619,27 @@ class RulesFilterCreatorController extends BaseMailboxController {
listRuleConditionValueArguments.removeAt(ruleConditionIndex);
}
void selectConditionCombiner(ConditionCombiner? combinerType) {
if (combinerType != null) {
conditionCombinerType.value = combinerType;
void selectConditionCombiner({
required BuildContext context,
ConditionCombiner? combinerType,
bool isMobile = false,
}) {
FocusScope.of(context).unfocus();
if (isMobile) {
selectRuleConditionCombinerAction(
context,
conditionCombinerType.value ?? ConditionCombiner.AND,
);
} else if (combinerType != null) {
updateConditionCombiner(combinerType);
}
}
void updateConditionCombiner(ConditionCombiner? newConditionCombiner) {
conditionCombinerType.value = newConditionCombiner;
}
void tapAddAction() {
listEmailRuleFilterActionSelected.add(RuleFilterActionArguments.emptyAction());
if (listEmailRuleFilterActionSelected.length >= maxCountAction) {
@@ -3,22 +3,17 @@ import 'dart:math' as math;
import 'package:core/core.dart';
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_close_button_widget.dart';
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
import 'package:tmail_ui_user/features/base/widget/pop_back_barrier_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/select_rule_action_field_extension.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_action_arguments.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_list_action_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
@@ -32,17 +27,18 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
Widget build(BuildContext context) {
final responsiveUtil = controller.responsiveUtils;
final isMobile = responsiveUtil.isMobile(context);
final focusScope = FocusScope.of(context);
final currentScreenHeight = responsiveUtil.getSizeScreenHeight(context);
final currentScreenWidth = responsiveUtil.getSizeScreenWidth(context);
if (!isMobile) {
return Center(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(16)),
boxShadow: [
Widget bodyWidget = Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: isMobile
? null
: const BorderRadius.all(Radius.circular(16)),
boxShadow: isMobile
? null
: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 24,
@@ -53,400 +49,250 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
blurRadius: 2,
),
],
),
width: math.min(
),
width: isMobile
? double.infinity :
math.min(
currentScreenWidth,
612,
),
constraints: BoxConstraints(
maxHeight: math.min(
currentScreenHeight - 100,
674,
),
constraints: BoxConstraints(
maxHeight: isMobile
? double.infinity
: math.min(
currentScreenHeight - 100,
674,
),
clipBehavior: Clip.antiAlias,
child: _buildRulesFilterFormOnDesktop(context),
),
clipBehavior: isMobile ? Clip.none : Clip.antiAlias,
child: _buildRulesFilterForm(context, isMobile),
);
if (isMobile) {
bodyWidget = Scaffold(
backgroundColor: Colors.white,
body: GestureDetector(
onTap: FocusScope.of(context).unfocus,
child: SafeArea(child: bodyWidget),
),
);
} else {
bodyWidget = Center(child: bodyWidget);
}
return ResponsiveWidget(
responsiveUtils: responsiveUtil,
mobile: Scaffold(
backgroundColor:
PlatformInfo.isWeb ? Colors.black.withAlpha(24) : Colors.black38,
body: PopBackBarrierWidget(
child: SafeArea(
bottom: false,
left: false,
right: false,
child: GestureDetector(
onTap: focusScope.unfocus,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.vertical(
top: Radius.circular(16),
),
color: Colors.white,
),
margin: EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0),
child: SafeArea(
child: _buildRulesFilterFormOnMobile(context),
),
),
),
),
),
),
tablet: Scaffold(
backgroundColor: Colors.black.withAlpha(24),
body: Center(
child: GestureDetector(
onTap: focusScope.unfocus,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(16)),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 24,
offset: const Offset(0, 2),
),
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 2,
),
],
),
width: math.min(
currentScreenWidth,
612,
),
constraints: BoxConstraints(
maxHeight: math.min(
currentScreenHeight - 100,
674,
),
),
clipBehavior: Clip.antiAlias,
child: _buildRulesFilterFormOnDesktop(context),
),
),
),
),
);
return bodyWidget;
}
Widget _buildRulesFilterFormOnDesktop(BuildContext context) {
Widget _buildRulesFilterForm(BuildContext context, bool isMobile) {
final appLocalizations = AppLocalizations.of(context);
return Stack(
Widget formWidget = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(top: 24, bottom: 12),
alignment: Alignment.center,
child: Obx(
() => Text(
controller.actionType.value.getTitle(appLocalizations),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: AppColor.m3SurfaceBackground,
),
if (!isMobile)
Container(
padding: const EdgeInsets.only(top: 24, bottom: 12),
alignment: Alignment.center,
child: Obx(
() => Text(
controller.actionType.value.getTitle(appLocalizations),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: AppColor.m3SurfaceBackground,
),
),
),
Expanded(
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(
)
else
Container(
height: 64,
padding: _getPadding(context),
child: Stack(
alignment: Alignment.center,
children: [
PositionedDirectional(
start: 0,
child: TMailButtonWidget.fromIcon(
icon: controller.imagePaths.icArrowBack,
tooltipMessage: appLocalizations.back,
backgroundColor: Colors.transparent,
onTapActionCallback: () => controller.closeView(context),
),
),
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50),
child: Obx(
() => Text(
controller.actionType.value.getTitle(appLocalizations),
textAlign: TextAlign.center,
style: ThemeUtils.textStyleM3BodyLarge.copyWith(
color: AppColor.m3SurfaceBackground,
),
),
),
),
),
],
),
),
Expanded(
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: isMobile ? 16 : 32,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(
() => LabelInputFieldBuilder(
label: appLocalizations.ruleName,
hintText: appLocalizations.rulesNameHintTextInput,
textEditingController:
controller.inputRuleNameController,
focusNode: controller.inputRuleNameFocusNode,
errorText: controller.errorRuleName.value,
arrangeHorizontally: false,
isLabelHasColon: false,
labelStyle: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
runSpacing: 16,
inputFieldMaxWidth: double.infinity,
onTextChange: (value) =>
controller.updateRuleName(context, value),
),
label: appLocalizations.ruleName,
hintText: appLocalizations.rulesNameHintTextInput,
textEditingController:
controller.inputRuleNameController,
focusNode: controller.inputRuleNameFocusNode,
errorText: controller.errorRuleName.value,
arrangeHorizontally: false,
isLabelHasColon: false,
labelStyle: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 24,
),
child: Text(
appLocalizations.condition,
style: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
),
runSpacing: 16,
inputFieldMaxWidth: double.infinity,
onTextChange: (value) =>
controller.updateRuleName(context, value),
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 24,
),
child: Text(
appLocalizations.condition,
style: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
Obx(
),
),
Obx(
() => RuleFilterTitle(
conditionCombinerType:
controller.conditionCombinerType.value,
responsiveUtils: controller.responsiveUtils,
tapActionCallback: (value) =>
controller.selectConditionCombiner(value),
ruleFilterConditionScreenType:
RuleFilterConditionScreenType.desktop,
),
conditionCombinerType: controller.conditionCombinerType.value,
imagePaths: controller.imagePaths,
isMobile: isMobile,
onTapActionCallback: (value) =>
controller.selectConditionCombiner(
context: context,
combinerType: value,
isMobile: isMobile,
),
),
),
const SizedBox(height: 8),
_buildListRuleFilterConditionList(
context: context,
isMobile: isMobile,
),
Container(
constraints: const BoxConstraints(minWidth: 161),
height: 36,
margin: const EdgeInsetsDirectional.only(top: 12),
child: ConfirmDialogButton(
label: AppLocalizations.of(context).addACondition,
backgroundColor: Colors.white,
textColor: AppColor.primaryMain,
borderColor: AppColor.primaryMain,
icon: controller.imagePaths.icAddIdentity,
onTapAction: controller.tapAddCondition,
),
),
Padding(
padding: const EdgeInsetsDirectional.only(
top: 24,
bottom: 8,
),
child: Text(
appLocalizations.actionsToPerform,
style: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
const SizedBox(height: 8),
_buildListRuleFilterConditionList(
context,
RuleFilterConditionScreenType.desktop,
),
Container(
),
),
_buildListRuleFilterActionList(context),
Obx(() {
if (controller.isShowAddAction.value == true) {
return Container(
constraints: const BoxConstraints(minWidth: 161),
height: 36,
margin: const EdgeInsetsDirectional.only(top: 12),
margin: const EdgeInsetsDirectional.only(top: 4),
child: ConfirmDialogButton(
label: AppLocalizations.of(context).addACondition,
label: AppLocalizations.of(context).addAnAction,
backgroundColor: Colors.white,
textColor: AppColor.primaryMain,
borderColor: AppColor.primaryMain,
icon: controller.imagePaths.icAddIdentity,
onTapAction: controller.tapAddCondition,
onTapAction: controller.tapAddAction,
),
),
Padding(
padding: const EdgeInsetsDirectional.only(
top: 24,
bottom: 8,
),
child: Text(
appLocalizations.actionsToPerform,
style: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
),
),
_buildListRuleFilterActionList(context),
Obx(() {
if (controller.isShowAddAction.value == true) {
return Container(
constraints: const BoxConstraints(minWidth: 161),
height: 36,
margin: const EdgeInsetsDirectional.only(top: 4),
child: ConfirmDialogButton(
label: AppLocalizations.of(context).addAnAction,
backgroundColor: Colors.white,
textColor: AppColor.primaryMain,
borderColor: AppColor.primaryMain,
icon: controller.imagePaths.icAddIdentity,
onTapAction: controller.tapAddAction,
),
);
} else {
return const SizedBox.shrink();
}
}),
],
),
),
);
} else {
return const SizedBox.shrink();
}
}),
],
),
),
RuleFilterListActionWidget(
positiveLabel: appLocalizations.createRule,
negativeLabel: appLocalizations.cancel,
padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 32),
onPositiveAction: () => controller.createNewRuleFilter(context),
onNegativeAction: () => controller.closeView(context),
),
],
),
),
DefaultCloseButtonWidget(
iconClose: controller.imagePaths.icCloseDialog,
onTapActionCallback: () => controller.closeView(context),
RuleFilterListActionWidget(
positiveLabel: appLocalizations.createRule,
negativeLabel: appLocalizations.cancel,
padding: EdgeInsets.symmetric(
vertical: 25,
horizontal: isMobile ? 16 : 32,
),
onPositiveAction: () => controller.createNewRuleFilter(context),
onNegativeAction: () => controller.closeView(context),
),
],
);
}
Widget _buildRulesFilterFormOnMobile(BuildContext context) {
return Stack(
if (isMobile) {
return formWidget;
} else {
return Stack(
children: [
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Container(
padding: const EdgeInsets.only(top: 16),
alignment: Alignment.center,
child: Obx(() => Text(
controller.actionType.value.getTitle(AppLocalizations.of(context)),
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black)))),
Expanded(child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() => RulesFilterInputField(
hintText: AppLocalizations.of(context).rulesNameHintTextInput,
errorText: controller.errorRuleName.value,
editingController: controller.inputRuleNameController,
focusNode: controller.inputRuleNameFocusNode,
onChangeAction: (value) => controller.updateRuleName(context, value),)),
const Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: Divider(),
),
Obx(() => RuleFilterTitle(
conditionCombinerType: controller.conditionCombinerType.value,
responsiveUtils: controller.responsiveUtils,
ruleFilterConditionScreenType: RuleFilterConditionScreenType.mobile,
tapActionCallback: (_) {
controller.selectRuleConditionCombinerAction(
context,
controller.conditionCombinerType.value
?? ConditionCombiner.AND,
);
},
)),
const SizedBox(height: 24),
_buildListRuleFilterConditionList(context, RuleFilterConditionScreenType.mobile),
Container(
padding: const EdgeInsets.only(top: 12),
child: InkWell(
onTap: controller.tapAddCondition,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
controller.imagePaths.icAddNewFolder,
fit: BoxFit.fill,
),
const SizedBox(width: 15,),
Text(
AppLocalizations.of(context).addCondition,
maxLines: 1,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: 17,
color: AppColor.primaryColor
)
)
],
),
),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 12),
child: Divider(),
),
Text(AppLocalizations.of(context).actionTitleRulesFilter,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
color: Colors.black)),
const SizedBox(height: 24),
_buildListRuleFilterActionList(context),
Obx(() {
if (controller.isShowAddAction.value == true) {
return Container(
constraints: const BoxConstraints(minWidth: 161),
height: 36,
margin: const EdgeInsetsDirectional.only(top: 4),
child: ConfirmDialogButton(
label: AppLocalizations.of(context).addAnAction,
backgroundColor: Colors.white,
textColor: AppColor.primaryMain,
borderColor: AppColor.primaryMain,
icon: controller.imagePaths.icAddIdentity,
onTapAction: controller.tapAddAction,
),
);
} else {
return const SizedBox.shrink();
}
}),
]
),
),
)),
Container(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
alignment: Alignment.center,
color: Colors.white,
child: Row(
children: [
Expanded(child: buildTextButton(
AppLocalizations.of(context).cancel,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: 17,
color: AppColor.colorTextButton),
backgroundColor: AppColor.emailAddressChipColor,
width: 128,
height: 44,
radius: 10,
onTap: () => controller.closeView(context))),
const SizedBox(width: 12),
Expanded(child: Obx(() => buildTextButton(
controller.actionType.value.getActionName(context),
width: 128,
height: 44,
backgroundColor: AppColor.colorTextButton,
radius: 10,
onTap: () => controller.createNewRuleFilter(context)))),
]
),
)
]),
Positioned(top: 8, right: 8,
child: buildIconWeb(
icon: SvgPicture.asset(
controller.imagePaths.icCircleClose,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).close,
onTap: () => controller.closeView(context)))
]
);
formWidget,
DefaultCloseButtonWidget(
iconClose: controller.imagePaths.icCloseDialog,
onTapActionCallback: () => controller.closeView(context),
),
],
);
}
}
Widget _buildListRuleFilterConditionList(
BuildContext context,
RuleFilterConditionScreenType ruleFilterConditionScreenType,
) {
Widget _buildListRuleFilterConditionList({
required BuildContext context,
required bool isMobile,
}) {
return Obx(() {
return ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: controller.listRuleCondition.length,
itemBuilder: (context, index) {
final conditionItem =
controller.listRuleConditionValueArguments[index];
return RuleFilterConditionWidget(
key: ValueKey(conditionItem.focusNode),
ruleFilterConditionScreenType: ruleFilterConditionScreenType,
isMobile: isMobile,
ruleCondition: controller.listRuleCondition[index],
imagePaths: controller.imagePaths,
conditionValueErrorText: conditionItem.errorText,
@@ -457,7 +303,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
context,
value,
controller.listRuleCondition[index].field,
ruleFilterConditionScreenType,
isMobile,
index,
),
tapRuleConditionComparatorCallback: (value) =>
@@ -465,7 +311,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
context,
value,
controller.listRuleCondition[index].comparator,
ruleFilterConditionScreenType,
isMobile,
index,
),
conditionValueOnChangeAction: (value) =>
@@ -482,6 +328,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
return Obx(() {
return ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: controller.listEmailRuleFilterActionSelected.length,
padding: const EdgeInsetsDirectional.only(bottom: 8),
itemBuilder: (context, index) {
@@ -534,4 +381,14 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
);
});
}
EdgeInsetsGeometry _getPadding(BuildContext context) {
if (controller.responsiveUtils.isPortraitMobile(context)) {
return const EdgeInsetsDirectional.only(start: 8, end: 16);
} else if (controller.responsiveUtils.isLandscapeMobile(context)) {
return const EdgeInsetsDirectional.symmetric(horizontal: 24);
} else {
return const EdgeInsetsDirectional.symmetric(horizontal: 32);
}
}
}
@@ -1,7 +0,0 @@
class RuleFilterActionStyles {
static const double extentRatio = 0.1;
static const double removeButtonRadius = 110.0;
static const double mainPadding = 12.0;
static const double mainBorderRadius = 12.0;
static const double itemDistance = 12.0;
}
@@ -1,67 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
class RuleActionSheetActionTileBuilder
extends CupertinoActionSheetNoIconBuilder<EmailRuleFilterAction> {
final EmailRuleFilterAction ruleAction;
final EmailRuleFilterAction? ruleActionCurrent;
final SvgPicture? actionSelected;
final Color? bgColor;
final EdgeInsets? iconLeftPadding;
final EdgeInsets? iconRightPadding;
RuleActionSheetActionTileBuilder(
String actionName,
this.ruleActionCurrent,
this.ruleAction,
{
Key? key,
this.actionSelected,
this.bgColor,
this.iconLeftPadding,
this.iconRightPadding,
}
) : super(actionName, key: key);
@override
Widget build() {
return Container(
color: bgColor ?? Colors.white,
child: MouseRegion(
cursor: PlatformInfo.isWeb
? WidgetStateMouseCursor.clickable
: MouseCursor.defer,
child: CupertinoActionSheetAction(
key: key,
child: Stack(
children: [
Center(
child: Text(actionName,
textAlign: TextAlign.center,
style: actionTextStyle()),
),
if (ruleActionCurrent == ruleAction && actionSelected != null)
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: iconRightPadding ??
const EdgeInsets.only(right: 12),
child: actionSelected!),
),
],
),
onPressed: () {
if (onCupertinoActionSheetActionClick != null) {
onCupertinoActionSheetActionClick!(ruleAction);
}
},
),
),
);
}
}
@@ -1,68 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_svg/svg.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_condition_combiner_bottom_sheet_styles.dart';
class RuleConditionCombinerSheetActionTileBuilder
extends CupertinoActionSheetNoIconBuilder<ConditionCombiner> {
final ConditionCombiner combiner;
final ConditionCombiner? combinerCurrent;
final SvgPicture? actionSelected;
final Color? bgColor;
final EdgeInsets? iconLeftPadding;
final EdgeInsets? iconRightPadding;
RuleConditionCombinerSheetActionTileBuilder(
String actionName,
this.combiner,
this.combinerCurrent,
{
Key? key,
this.actionSelected,
this.bgColor,
this.iconLeftPadding,
this.iconRightPadding,
}
) : super(actionName, key: key);
@override
Widget build() {
return Container(
color: bgColor ?? RuleConditionCombinerBottomSheetStyles.defaultBgColor,
child: MouseRegion(
cursor: PlatformInfo.isWeb
? WidgetStateMouseCursor.clickable
: MouseCursor.defer,
child: CupertinoActionSheetAction(
key: key,
child: Stack(
children: [
Center(
child: Text(
actionName,
textAlign: TextAlign.center,
style: actionTextStyle()
),
),
if (combinerCurrent == combiner && actionSelected != null)
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: iconRightPadding ?? const EdgeInsets.only(right: RuleConditionCombinerBottomSheetStyles.defaultIconRightPadding),
child: actionSelected!
),
),
],
),
onPressed: () {
if (onCupertinoActionSheetActionClick != null) {
onCupertinoActionSheetActionClick!(combiner);
}
},
),
),
);
}
}
@@ -1,67 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition;
class RuleConditionComparatorSheetActionTileBuilder
extends CupertinoActionSheetNoIconBuilder<rule_condition.Comparator> {
final rule_condition.Comparator comparator;
final rule_condition.Comparator? comparatorCurrent;
final SvgPicture? actionSelected;
final Color? bgColor;
final EdgeInsets? iconLeftPadding;
final EdgeInsets? iconRightPadding;
RuleConditionComparatorSheetActionTileBuilder(
String actionName,
this.comparatorCurrent,
this.comparator,
{
Key? key,
this.actionSelected,
this.bgColor,
this.iconLeftPadding,
this.iconRightPadding,
}
) : super(actionName, key: key);
@override
Widget build() {
return Container(
color: bgColor ?? Colors.white,
child: MouseRegion(
cursor: PlatformInfo.isWeb
? WidgetStateMouseCursor.clickable
: MouseCursor.defer,
child: CupertinoActionSheetAction(
key: key,
child: Stack(
children: [
Center(
child: Text(actionName,
textAlign: TextAlign.center,
style: actionTextStyle()),
),
if (comparatorCurrent == comparator && actionSelected != null)
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: iconRightPadding ??
const EdgeInsets.only(right: 12),
child: actionSelected!),
),
],
),
onPressed: () {
if (onCupertinoActionSheetActionClick != null) {
onCupertinoActionSheetActionClick!(comparator);
}
},
),
),
);
}
}
@@ -1,67 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition;
class RuleConditionFieldSheetActionTileBuilder
extends CupertinoActionSheetNoIconBuilder<rule_condition.Field> {
final rule_condition.Field field;
final rule_condition.Field? fieldCurrent;
final SvgPicture? actionSelected;
final Color? bgColor;
final EdgeInsets? iconLeftPadding;
final EdgeInsets? iconRightPadding;
RuleConditionFieldSheetActionTileBuilder(
String actionName,
this.fieldCurrent,
this.field,
{
Key? key,
this.actionSelected,
this.bgColor,
this.iconLeftPadding,
this.iconRightPadding,
}
) : super(actionName, key: key);
@override
Widget build() {
return Container(
color: bgColor ?? Colors.white,
child: MouseRegion(
cursor: PlatformInfo.isWeb
? WidgetStateMouseCursor.clickable
: MouseCursor.defer,
child: CupertinoActionSheetAction(
key: key,
child: Stack(
children: [
Center(
child: Text(actionName,
textAlign: TextAlign.center,
style: actionTextStyle()),
),
if (fieldCurrent == field && actionSelected != null)
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: iconRightPadding ??
const EdgeInsets.only(right: 12),
child: actionSelected!),
),
],
),
onPressed: () {
if (onCupertinoActionSheetActionClick != null) {
onCupertinoActionSheetActionClick!(field);
}
},
),
),
);
}
}
@@ -4,10 +4,11 @@ import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RuleFilterActionDetailed extends StatelessWidget {
final ImagePaths imagePaths;
final EmailRuleFilterAction? actionType;
final PresentationMailbox? mailboxSelected;
final String? errorValue;
@@ -18,6 +19,7 @@ class RuleFilterActionDetailed extends StatelessWidget {
const RuleFilterActionDetailed({
Key? key,
required this.imagePaths,
this.actionType,
this.mailboxSelected,
this.errorValue,
@@ -34,8 +36,9 @@ class RuleFilterActionDetailed extends StatelessWidget {
case EmailRuleFilterAction.moveMessage:
return RuleFilterButtonField<PresentationMailbox>(
value: mailboxSelected,
imagePaths: imagePaths,
borderColor: borderColor,
tapActionCallback: (value) {
onTapActionCallback: (value) {
tapActionDetailedCallback!();
},
);
@@ -7,8 +7,8 @@ import 'package:tmail_ui_user/features/base/widget/default_field/default_input_f
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RuleFilterActionRow extends StatelessWidget {
@@ -81,10 +81,11 @@ class RuleFilterActionRow extends StatelessWidget {
Expanded(
child: RuleFilterButtonField<PresentationMailbox>(
value: mailboxSelected,
imagePaths: imagePaths,
borderColor: errorValue?.isNotEmpty == true
? AppColor.redFF3347
: AppColor.m3Neutral90,
tapActionCallback: (_) => tapActionDetailedCallback?.call(),
onTapActionCallback: (_) => tapActionDetailedCallback?.call(),
),
),
]
@@ -102,7 +103,7 @@ class RuleFilterActionRow extends StatelessWidget {
RuleFilterDeleteButtonWidget(
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
)
),
],
);
}
@@ -2,13 +2,13 @@ import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_filter_action_styles.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_detailed_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RuleFilterActionRowMobile extends StatelessWidget {
final ImagePaths imagePaths;
final EmailRuleFilterAction? actionSelected;
final Function()? tapActionCallback;
final PresentationMailbox? mailboxSelected;
@@ -20,6 +20,7 @@ class RuleFilterActionRowMobile extends StatelessWidget {
const RuleFilterActionRowMobile({
Key? key,
required this.imagePaths,
this.actionSelected,
this.tapActionCallback,
this.mailboxSelected,
@@ -37,7 +38,8 @@ class RuleFilterActionRowMobile extends StatelessWidget {
children: [
RuleFilterButtonField<EmailRuleFilterAction>(
value: actionSelected,
tapActionCallback: (value) {
imagePaths: imagePaths,
onTapActionCallback: (value) {
tapActionCallback!();
},
hintText: AppLocalizations.of(context).selectAction,
@@ -45,7 +47,7 @@ class RuleFilterActionRowMobile extends StatelessWidget {
actionSelected == EmailRuleFilterAction.moveMessage
? Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(vertical: RuleFilterActionStyles.mainPadding),
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(
AppLocalizations.of(context).toFolder,
overflow: TextOverflow.ellipsis,
@@ -58,9 +60,10 @@ class RuleFilterActionRowMobile extends StatelessWidget {
),
)
: SizedBox(
height: actionSelected == EmailRuleFilterAction.forwardTo ? RuleFilterActionStyles.itemDistance : 0,
height: actionSelected == EmailRuleFilterAction.forwardTo ? 12 : 0,
),
RuleFilterActionDetailed(
imagePaths: imagePaths,
actionType: actionSelected,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
@@ -3,14 +3,12 @@ import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_filter_action_styles.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
class RuleFilterActionWidget extends StatelessWidget {
final ResponsiveUtils responsiveUtils;
@@ -46,118 +44,90 @@ class RuleFilterActionWidget extends StatelessWidget {
Widget build(BuildContext context) {
final isMobile = responsiveUtils.isMobile(context);
Widget bodyWidget = Container(
padding: const EdgeInsetsDirectional.only(start: 12),
margin: const EdgeInsetsDirectional.only(top: 8),
decoration: const BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
height: 72,
alignment: Alignment.center,
child: isMobile
? RuleFilterActionRowMobile(
actionSelected: actionSelected,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
tapActionCallback: onActionChangeMobile,
)
: RuleFilterActionRow(
actionList: EmailRuleFilterAction.values,
actionSelected: actionSelected,
onActionChanged: onActionChanged,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
),
);
if (!isMobile) {
return bodyWidget;
}
return Slidable(
enabled: responsiveUtils.isMobile(context) ? true : false,
endActionPane: ActionPane(
extentRatio: RuleFilterActionStyles.extentRatio,
motion: const BehindMotion(),
children: [
CustomSlidableAction(
padding: const EdgeInsets.only(right: RuleFilterActionStyles.mainPadding),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(RuleFilterActionStyles.mainBorderRadius),
bottomRight: Radius.circular(RuleFilterActionStyles.mainBorderRadius),
),
onPressed: (_) => onDeleteRuleConditionAction(),
backgroundColor: AppColor.colorBackgroundFieldConditionRulesFilter,
child: CircleAvatar(
backgroundColor: AppColor.colorRemoveRuleFilterConditionButton,
radius: RuleFilterActionStyles.removeButtonRadius,
child: SvgPicture.asset(
imagePaths.icMinimize,
fit: BoxFit.fill,
colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(),
),
),
)
],
),
child: Builder(
builder: (context) {
SlidableController? slideController = Slidable.of(context);
return ValueListenableBuilder<int>(
valueListenable: slideController?.direction ?? ValueNotifier<int>(0),
builder: (context, value, _) {
var borderRadius = value != -1
? BorderRadius.circular(RuleFilterActionStyles.mainBorderRadius)
: const BorderRadius.only(
bottomLeft: Radius.circular(RuleFilterActionStyles.mainBorderRadius),
topLeft: Radius.circular(RuleFilterActionStyles.mainBorderRadius),
);
return Container(
padding: const EdgeInsets.all(RuleFilterActionStyles.mainPadding),
decoration: BoxDecoration(
color: AppColor.colorBackgroundFieldConditionRulesFilter,
borderRadius: borderRadius,
return Container(
padding: const EdgeInsetsDirectional.only(start: 12),
margin: const EdgeInsetsDirectional.only(top: 8),
decoration: const BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
height: 72,
alignment: Alignment.center,
child: RuleFilterActionRow(
actionList: EmailRuleFilterAction.values,
actionSelected: actionSelected,
onActionChanged: onActionChanged,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
),
);
} else {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 8),
child: Slidable(
endActionPane: ActionPane(
extentRatio: 0.15,
motion: const BehindMotion(),
children: [
CustomSlidableAction(
padding: const EdgeInsets.only(right: 12),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
),
child: responsiveUtils.isMobile(context)
? RuleFilterActionRowMobile(
actionSelected: actionSelected,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
tapActionCallback: onActionChangeMobile,
)
: RuleFilterActionRow(
actionList: EmailRuleFilterAction.values,
actionSelected: actionSelected,
onActionChanged: onActionChanged,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
)
);
},
);
}
),
);
onPressed: (_) => onDeleteRuleConditionAction(),
backgroundColor: AppColor.lightGrayF9FAFB,
child: RuleFilterDeleteButtonWidget(
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
),
)
],
),
child: Builder(
builder: (context) {
SlidableController? slideController = Slidable.of(context);
return ValueListenableBuilder<int>(
valueListenable: slideController?.direction ?? ValueNotifier<int>(0),
builder: (context, value, _) {
final borderRadius = value != -1
? BorderRadius.circular(10)
: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topLeft: Radius.circular(10),
);
return Container(
padding: const EdgeInsetsDirectional.all(8),
decoration: BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: borderRadius,
),
alignment: Alignment.center,
child: RuleFilterActionRowMobile(
imagePaths: imagePaths,
actionSelected: actionSelected,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
tapActionCallback: onActionChangeMobile,
),
);
},
);
}
),
),
);
}
}
}
@@ -1,11 +1,9 @@
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:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:model/model.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition;
import 'package:rule_filter/rule_filter/rule_condition_group.dart';
@@ -14,49 +12,57 @@ import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensi
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnTapActionCallback<T> = Function(T? value);
typedef OnRuleTapActionCallback<T> = Function(T? value);
class RuleFilterButtonField<T> extends StatelessWidget {
final T? value;
final OnTapActionCallback? tapActionCallback;
final OnRuleTapActionCallback onTapActionCallback;
final ImagePaths imagePaths;
final Color? borderColor;
final String? hintText;
const RuleFilterButtonField({
super.key,
this.value,
this.tapActionCallback,
required this.value,
required this.imagePaths,
required this.onTapActionCallback,
this.borderColor,
this.hintText,
});
@override
Widget build(BuildContext context) {
final imagePaths = Get.find<ImagePaths>();
final Color textColor = value != null ? Colors.black : AppColor.textFieldHintColor;
return InkWell(
onTap: () => tapActionCallback?.call(value),
child: Container(
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
return Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => onTapActionCallback(value),
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: Container(
height: 40,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(10)),
border: Border.all(
color: borderColor ?? AppColor.colorInputBorderCreateMailbox,
width: 1),
color: Colors.white),
padding: const EdgeInsets.only(left: 12, right: 10),
child: Row(children: [
Expanded(child: Text(
_getName(context, value),
style: ThemeUtils.textStyleBodyBody3(color: textColor),
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
)),
SvgPicture.asset(imagePaths.icDropDown)
]),
color: borderColor ?? AppColor.m3Neutral90,
width: 1,
),
color: Colors.white
),
padding: const EdgeInsetsDirectional.only(start: 12, end: 10),
child: Row(children: [
Expanded(child: Text(
_getName(context, value),
style: ThemeUtils.textStyleBodyBody3(
color: value != null
? Colors.black
: AppColor.textFieldHintColor,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)),
SvgPicture.asset(imagePaths.icDropDown)
]),
),
),
);
}
@@ -1,25 +1,23 @@
import 'package:core/core.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/keyboard_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition;
import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart';
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RuleFilterConditionRow extends StatelessWidget {
final RuleFilterConditionScreenType? ruleFilterConditionScreenType;
final bool isMobile;
final RuleCondition ruleCondition;
final ImagePaths imagePaths;
final OnDeleteRuleConditionAction onDeleteRuleConditionAction;
final Function(Field?)? tapRuleConditionFieldCallback;
final Function(Comparator?)? tapRuleConditionComparatorCallback;
final OnRuleTapActionCallback tapRuleConditionFieldCallback;
final OnRuleTapActionCallback tapRuleConditionComparatorCallback;
final String? conditionValueErrorText;
final TextEditingController textEditingController;
final FocusNode? conditionValueFocusNode;
@@ -27,13 +25,13 @@ class RuleFilterConditionRow extends StatelessWidget {
const RuleFilterConditionRow({
Key? key,
required this.ruleFilterConditionScreenType,
required this.isMobile,
required this.ruleCondition,
required this.imagePaths,
required this.textEditingController,
required this.onDeleteRuleConditionAction,
this.tapRuleConditionFieldCallback,
this.tapRuleConditionComparatorCallback,
required this.tapRuleConditionFieldCallback,
required this.tapRuleConditionComparatorCallback,
this.conditionValueErrorText,
this.conditionValueFocusNode,
this.conditionValueOnChangeAction,
@@ -41,99 +39,88 @@ class RuleFilterConditionRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
switch (ruleFilterConditionScreenType) {
case RuleFilterConditionScreenType.mobile:
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RuleFilterButtonField<rule_condition.Field>(
value: ruleCondition.field,
tapActionCallback: (value) {
KeyboardUtils.hideKeyboard(context);
tapRuleConditionFieldCallback!(ruleCondition.field);
},
if (isMobile) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RuleFilterButtonField<rule_condition.Field>(
value: ruleCondition.field,
imagePaths: imagePaths,
onTapActionCallback: tapRuleConditionFieldCallback,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: RuleFilterButtonField<rule_condition.Comparator>(
value: ruleCondition.comparator,
imagePaths: imagePaths,
onTapActionCallback: tapRuleConditionComparatorCallback,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: RuleFilterButtonField<rule_condition.Comparator>(
value: ruleCondition.comparator,
tapActionCallback: (value) {
KeyboardUtils.hideKeyboard(context);
tapRuleConditionComparatorCallback!(ruleCondition.comparator);
},
)
),
DefaultInputFieldWidget(
hintText: AppLocalizations.of(context).conditionValueHintTextInput,
errorText: conditionValueErrorText,
focusNode: conditionValueFocusNode,
inputColor: Colors.black,
onTextChange: conditionValueOnChangeAction,
textEditingController: textEditingController,
),
],
);
} else {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 12),
Expanded(
child: DropDownButtonWidget<rule_condition.Field>(
items: rule_condition.Field.values,
itemSelected: ruleCondition.field,
dropdownMaxHeight: 250,
heightItem: 40,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: tapRuleConditionFieldCallback,
supportSelectionIcon: true,
),
RulesFilterInputField(
hintText: AppLocalizations.of(context).conditionValueHintTextInput,
),
const SizedBox(width: 8),
Expanded(
child: DropDownButtonWidget<rule_condition.Comparator>(
items: rule_condition.Comparator.values,
itemSelected: ruleCondition.comparator,
heightItem: 40,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: tapRuleConditionComparatorCallback,
supportSelectionIcon: true,
)),
const SizedBox(width: 8),
Expanded(
child: DefaultInputFieldWidget(
hintText:
AppLocalizations.of(context).conditionValueHintTextInput,
errorText: conditionValueErrorText,
focusNode: conditionValueFocusNode,
onChangeAction: conditionValueOnChangeAction,
editingController: textEditingController,
inputColor: Colors.black,
onTextChange: conditionValueOnChangeAction,
textEditingController: textEditingController,
),
],
);
case RuleFilterConditionScreenType.tablet:
case RuleFilterConditionScreenType.desktop:
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 12),
Expanded(
child: DropDownButtonWidget<rule_condition.Field>(
items: rule_condition.Field.values,
itemSelected: ruleCondition.field,
dropdownMaxHeight: 250,
heightItem: 40,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: (newField) => {
tapRuleConditionFieldCallback!(newField)
},
supportSelectionIcon: true,
)
),
const SizedBox(width: 8),
Expanded(
child: DropDownButtonWidget<rule_condition.Comparator>(
items: rule_condition.Comparator.values,
itemSelected: ruleCondition.comparator,
heightItem: 40,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: (newComparator) => {
tapRuleConditionComparatorCallback!(newComparator)
},
supportSelectionIcon: true,
)
),
const SizedBox(width: 8),
Expanded(
child: DefaultInputFieldWidget(
hintText: AppLocalizations.of(context).conditionValueHintTextInput,
errorText: conditionValueErrorText,
focusNode: conditionValueFocusNode,
inputColor: Colors.black,
onTextChange: conditionValueOnChangeAction,
textEditingController: textEditingController,
),
),
RuleFilterDeleteButtonWidget(
imagePaths: imagePaths,
margin: const EdgeInsetsDirectional.only(top: 2),
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
),
],
);
default:
return const SizedBox.shrink();
),
RuleFilterDeleteButtonWidget(
imagePaths: imagePaths,
margin: const EdgeInsetsDirectional.only(top: 2),
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
),
],
);
}
}
}
@@ -1,20 +1,20 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
typedef OnChangeFilterInputAction = Function(String? value);
class RuleFilterConditionWidget extends StatelessWidget {
final RuleFilterConditionScreenType? ruleFilterConditionScreenType;
final bool isMobile;
final RuleCondition ruleCondition;
final TextEditingController textEditingController;
final Function(Field?)? tapRuleConditionFieldCallback;
final Function(Comparator?)? tapRuleConditionComparatorCallback;
final OnRuleTapActionCallback tapRuleConditionFieldCallback;
final OnRuleTapActionCallback tapRuleConditionComparatorCallback;
final String? conditionValueErrorText;
final FocusNode? conditionValueFocusNode;
final OnChangeFilterInputAction? conditionValueOnChangeAction;
@@ -23,13 +23,13 @@ class RuleFilterConditionWidget extends StatelessWidget {
const RuleFilterConditionWidget({
super.key,
this.ruleFilterConditionScreenType,
required this.isMobile,
required this.ruleCondition,
required this.imagePaths,
required this.textEditingController,
required this.onDeleteRuleConditionAction,
this.tapRuleConditionFieldCallback,
this.tapRuleConditionComparatorCallback,
required this.tapRuleConditionFieldCallback,
required this.tapRuleConditionComparatorCallback,
this.conditionValueErrorText,
this.conditionValueFocusNode,
this.conditionValueOnChangeAction,
@@ -37,59 +37,89 @@ class RuleFilterConditionWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget bodyWidget = Container(
padding: const EdgeInsetsDirectional.symmetric(vertical: 12),
margin: const EdgeInsetsDirectional.only(top: 8),
decoration: const BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
alignment: Alignment.center,
child: RuleFilterConditionRow(
ruleFilterConditionScreenType: ruleFilterConditionScreenType,
ruleCondition: ruleCondition,
tapRuleConditionFieldCallback: tapRuleConditionFieldCallback,
tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback,
conditionValueErrorText: conditionValueErrorText,
textEditingController: textEditingController,
conditionValueFocusNode: conditionValueFocusNode,
conditionValueOnChangeAction: conditionValueOnChangeAction,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
imagePaths: imagePaths,
),
);
if (ruleFilterConditionScreenType != RuleFilterConditionScreenType.mobile) {
return bodyWidget;
}
return Slidable(
enabled: ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile ? true : false,
endActionPane: ActionPane(
extentRatio: 0.1,
motion: const BehindMotion(),
children: [
CustomSlidableAction(
padding: const EdgeInsets.only(right: 12),
borderRadius: const BorderRadius.only(topRight: Radius.circular(12), bottomRight: Radius.circular(12)),
onPressed: (_) => onDeleteRuleConditionAction(),
backgroundColor: AppColor.colorBackgroundFieldConditionRulesFilter,
child: CircleAvatar(
backgroundColor: AppColor.colorRemoveRuleFilterConditionButton,
radius: 110,
child: SvgPicture.asset(
imagePaths.icMinimize,
fit: BoxFit.fill,
colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(),
if (isMobile) {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 8),
child: Slidable(
endActionPane: ActionPane(
extentRatio: 0.15,
motion: const BehindMotion(),
children: [
CustomSlidableAction(
padding: const EdgeInsets.only(right: 12),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
),
onPressed: (_) => onDeleteRuleConditionAction(),
backgroundColor: AppColor.lightGrayF9FAFB,
child: RuleFilterDeleteButtonWidget(
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
),
),
)
)
]
),
child: ValueListenableBuilder<int>(
valueListenable: Slidable.of(context)?.direction ?? ValueNotifier<int>(0),
builder: (_, __, ___) => bodyWidget,
),
);
],
),
child: Builder(
builder: (context) {
SlidableController? slideController = Slidable.of(context);
return ValueListenableBuilder<int>(
valueListenable: slideController?.direction ?? ValueNotifier<int>(0),
builder: (context, value, _) {
final borderRadius = value != -1
? BorderRadius.circular(10)
: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topLeft: Radius.circular(10),
);
return Container(
padding: const EdgeInsetsDirectional.all(8),
decoration: BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: borderRadius,
),
alignment: Alignment.center,
child: RuleFilterConditionRow(
isMobile: isMobile,
ruleCondition: ruleCondition,
tapRuleConditionFieldCallback: tapRuleConditionFieldCallback,
tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback,
conditionValueErrorText: conditionValueErrorText,
textEditingController: textEditingController,
conditionValueFocusNode: conditionValueFocusNode,
conditionValueOnChangeAction: conditionValueOnChangeAction,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
imagePaths: imagePaths,
),
);
}
);
}
),
),
);
} else {
return Container(
padding: const EdgeInsetsDirectional.symmetric(vertical: 12),
margin: const EdgeInsetsDirectional.only(top: 8),
decoration: const BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
alignment: Alignment.center,
child: RuleFilterConditionRow(
isMobile: isMobile,
ruleCondition: ruleCondition,
tapRuleConditionFieldCallback: tapRuleConditionFieldCallback,
tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback,
conditionValueErrorText: conditionValueErrorText,
textEditingController: textEditingController,
conditionValueFocusNode: conditionValueFocusNode,
conditionValueOnChangeAction: conditionValueOnChangeAction,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
imagePaths: imagePaths,
),
);
}
}
}
@@ -1,36 +1,70 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/keyboard_utils.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnTapActionCallback<RuleConditionCombiner> = Function(
ConditionCombiner? value,
);
class RuleFilterTitle extends StatelessWidget {
final ImagePaths imagePaths;
final ConditionCombiner? conditionCombinerType;
final OnTapActionCallback? tapActionCallback;
final RuleFilterConditionScreenType ruleFilterConditionScreenType;
final ResponsiveUtils responsiveUtils;
final OnRuleTapActionCallback onTapActionCallback;
final bool isMobile;
const RuleFilterTitle({
Key? key,
required this.ruleFilterConditionScreenType,
required this.responsiveUtils,
this.conditionCombinerType,
this.tapActionCallback,
required this.imagePaths,
required this.isMobile,
required this.conditionCombinerType,
required this.onTapActionCallback,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final appLocalizations = AppLocalizations.of(context);
final isMobile = responsiveUtils.isMobile(context);
if (isMobile) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Text(
appLocalizations.conditionTitleRulesFilterBeforeCombiner,
style: ThemeUtils.textStyleInter400.copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
constraints: const BoxConstraints(minWidth: 100),
child: RuleFilterButtonField<ConditionCombiner>(
value: conditionCombinerType,
imagePaths: imagePaths,
onTapActionCallback: onTapActionCallback,
),
),
)
],
),
const SizedBox(height: 8),
Text(
appLocalizations.conditionTitleRulesFilterAfterCombiner,
style: ThemeUtils.textStyleInter400.copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
)
],
);
}
return Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
@@ -47,31 +81,19 @@ class RuleFilterTitle extends StatelessWidget {
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
width: isMobile ? double.infinity : 158,
child: ruleFilterConditionScreenType ==
RuleFilterConditionScreenType.mobile
? RuleFilterButtonField<ConditionCombiner>(
value: conditionCombinerType,
tapActionCallback: (value) {
KeyboardUtils.hideKeyboard(context);
tapActionCallback?.call(value);
},
)
: DropDownButtonWidget<ConditionCombiner>(
items: ConditionCombiner.values,
itemSelected: conditionCombinerType,
supportSelectionIcon: true,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: (value) {
KeyboardUtils.hideKeyboard(context);
tapActionCallback?.call(value);
},
),
width: 158,
child: DropDownButtonWidget<ConditionCombiner>(
items: ConditionCombiner.values,
itemSelected: conditionCombinerType,
supportSelectionIcon: true,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: onTapActionCallback,
),
),
Text(
appLocalizations.conditionTitleRulesFilterAfterCombiner,
@@ -1,53 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
class RulesFilterInputDecorationBuilder extends InputDecorationBuilder {
@override
InputDecoration build() {
return InputDecoration(
enabledBorder: enabledBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(
width: 1,
color: AppColor.colorInputBorderCreateMailbox)),
focusedBorder: enabledBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(
width: 1,
color: AppColor.colorTextButton)),
errorBorder: errorBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(
width: 1,
color: AppColor.colorInputBorderErrorVerifyName)),
focusedErrorBorder: errorBorder ?? const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(
width: 1,
color: AppColor.colorInputBorderErrorVerifyName)),
prefixText: prefixText,
labelText: labelText,
floatingLabelBehavior: FloatingLabelBehavior.never,
labelStyle: labelStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.black,
fontSize: 16),
hintText: hintText,
isDense: true,
hintStyle: hintStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(
color: AppColor.colorHintInputCreateMailbox,
fontSize: 16),
contentPadding: contentPadding ?? const EdgeInsets.symmetric(
horizontal: 12,
vertical: 12),
errorText: errorText != '' ? errorText : null,
errorStyle: errorTextStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(
color: AppColor.colorInputBorderErrorVerifyName,
fontSize: 13),
filled: true,
fillColor: errorText?.isNotEmpty == true
? AppColor.colorInputBackgroundErrorVerifyName
: Colors.white);
}
}
@@ -1,49 +0,0 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/text/text_field_builder.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_decoration_builder.dart';
typedef OnChangeFilterInputAction = Function(String? value);
class RulesFilterInputField extends StatelessWidget {
final String? errorText;
final String? hintText;
final TextEditingController? editingController;
final FocusNode? focusNode;
final OnChangeFilterInputAction? onChangeAction;
const RulesFilterInputField({
Key? key,
this.hintText,
this.errorText,
this.editingController,
this.focusNode,
this.onChangeAction
}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFieldBuilder(
onTextChange: onChangeAction,
textInputAction: TextInputAction.next,
controller: editingController,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: Colors.black,
fontSize: 16,
),
textDirection: DirectionUtils.getDirectionByLanguage(context),
keyboardType: TextInputType.text,
focusNode: focusNode,
maxLines: 1,
decoration: (RulesFilterInputDecorationBuilder()
..setContentPadding(EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 14 : 12, horizontal: 12))
..setHintText(hintText)
..setErrorText(errorText))
.build(),
);
}
}