TF-4002 Apply new design for folder creation modal on web

This commit is contained in:
dab246
2025-09-04 15:06:28 +07:00
committed by Dat H. Pham
parent c09a2c92b2
commit 36e58fc990
23 changed files with 689 additions and 498 deletions
@@ -1,14 +1,10 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
import 'package:tmail_ui_user/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/expand_mode_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart';
@@ -20,27 +16,24 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_na
import 'package:tmail_ui_user/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class MailboxCreatorController extends BaseController {
class MailboxCreatorController extends BaseController
with ExpandFolderTriggerScrollableMixin {
final VerifyNameInteractor _verifyNameInteractor;
final selectedMailbox = Rxn<PresentationMailbox>();
final newNameMailbox = Rxn<String>();
final isFolderModalEnabled = RxBool(false);
bool _createdMailbox = false;
final personalMailboxTree = MailboxTree(MailboxNode.root()).obs;
final defaultMailboxTree = MailboxTree(MailboxNode.root()).obs;
final FocusNode nameInputFocusNode = FocusNode();
final TextEditingController nameInputController = TextEditingController();
MailboxCreatorArguments? arguments;
AccountId? accountId;
Session? _session;
MailboxTree? defaultMailboxTree;
MailboxTree? personalMailboxTree;
MailboxTree? teamMailboxesTre;
final ScrollController listFolderScrollController = ScrollController();
List<String> listMailboxNameAsStringExist = <String>[];
@@ -51,47 +44,35 @@ class MailboxCreatorController extends BaseController {
@override
void onInit() {
super.onInit();
log('MailboxCreatorController::onInit():arguments: ${Get.arguments}');
arguments = Get.arguments;
}
@override
void onReady() {
super.onReady();
log('MailboxCreatorController::onReady():');
MailboxCreatorArguments? arguments = Get.arguments;
if (arguments != null) {
personalMailboxTree = arguments!.personalMailboxTree;
defaultMailboxTree = arguments!.defaultMailboxTree;
teamMailboxesTre = arguments!.teamMailboxesTree;
accountId = arguments!.accountId;
_session = arguments!.session;
if (arguments!.selectedMailbox != null) {
selectedMailbox.value = arguments!.selectedMailbox;
}
personalMailboxTree.value = arguments.personalMailboxTree;
defaultMailboxTree.value = arguments.defaultMailboxTree;
selectedMailbox.value = arguments.selectedMailbox;
_createListMailboxNameAsStringInMailboxLocation();
}
}
@override
void onClose() {
log('MailboxCreatorController::onClose():');
listMailboxNameAsStringExist = [];
_createdMailbox = false;
nameInputFocusNode.dispose();
nameInputController.dispose();
listFolderScrollController.dispose();
super.onClose();
}
MailboxNode? _findMailboxNodeById(MailboxId mailboxId) {
final mailboxNode = defaultMailboxTree?.findNode((node) => node.item.id == mailboxId)
?? personalMailboxTree?.findNode((node) => node.item.id == mailboxId)
?? teamMailboxesTre?.findNode((node) => node.item.id == mailboxId);
final mailboxNode = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxId)
?? personalMailboxTree.value.findNode((node) => node.item.id == mailboxId);
return mailboxNode;
}
void _createListMailboxNameAsStringInMailboxLocation() {
if (selectedMailbox.value == null) {
final allChildrenAtMailboxLocation = (defaultMailboxTree?.root.childrenItems ?? <MailboxNode>[])
+ (personalMailboxTree?.root.childrenItems ?? <MailboxNode>[])
+ (teamMailboxesTre?.root.childrenItems ?? <MailboxNode>[]);
final allChildrenAtMailboxLocation = (defaultMailboxTree.value.root.childrenItems ?? <MailboxNode>[])
+ (personalMailboxTree.value.root.childrenItems ?? <MailboxNode>[]);
if (allChildrenAtMailboxLocation.isNotEmpty) {
listMailboxNameAsStringExist = allChildrenAtMailboxLocation
.where((mailboxNode) => mailboxNode.nameNotEmpty)
@@ -140,33 +121,22 @@ class MailboxCreatorController extends BaseController {
);
}
void selectMailboxLocation(BuildContext context) async {
KeyboardUtils.hideKeyboard(context);
void openFolderModal(BuildContext context) {
nameInputFocusNode.unfocus();
if (accountId != null) {
final arguments = DestinationPickerArguments(
accountId!,
MailboxActions.create,
_session,
mailboxIdSelected: selectedMailbox.value?.id);
final destinationMailbox = PlatformInfo.isWeb
? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.destinationPicker, arguments: arguments)
: await push(AppRoutes.destinationPicker, arguments: arguments);
if (destinationMailbox is PresentationMailbox) {
final mailboxDestination = destinationMailbox == PresentationMailbox.unifiedMailbox
? null
: destinationMailbox;
selectedMailbox.value = mailboxDestination;
_createListMailboxNameAsStringInMailboxLocation();
}
if (!responsiveUtils.isMobile(context)) {
isFolderModalEnabled.value = true;
}
}
void selectMailboxLocation(MailboxNode? mailboxDestination) {
isFolderModalEnabled.value = false;
selectedMailbox.value = mailboxDestination?.item;
_createListMailboxNameAsStringInMailboxLocation();
}
void createNewMailbox(BuildContext context) {
KeyboardUtils.hideKeyboard(context);
nameInputFocusNode.unfocus();
final nameMailbox = newNameMailbox.value;
final nameValidated = getErrorInputNameString(context);
@@ -180,11 +150,35 @@ class MailboxCreatorController extends BaseController {
MailboxName(nameMailbox),
mailboxLocation: selectedMailbox.value);
popBack(result: newMailboxArguments);
} else {
newNameMailbox.refresh();
}
}
void closeMailboxCreator(BuildContext context) {
KeyboardUtils.hideKeyboard(context);
void toggleFolder(MailboxNode selectedMailboxNode, GlobalKey itemKey) {
final newExpandMode = selectedMailboxNode.expandMode.toggle();
if (defaultMailboxTree.value.updateExpandedNode(selectedMailboxNode, newExpandMode) != null) {
defaultMailboxTree.refresh();
triggerScrollWhenExpandFolder(
selectedMailboxNode.expandMode,
itemKey,
listFolderScrollController,
);
}
if (personalMailboxTree.value.updateExpandedNode(selectedMailboxNode, newExpandMode) != null) {
personalMailboxTree.refresh();
triggerScrollWhenExpandFolder(
selectedMailboxNode.expandMode,
itemKey,
listFolderScrollController,
);
}
}
void closeView() {
nameInputFocusNode.unfocus();
popBack();
}
}
@@ -1,24 +1,20 @@
import 'dart:math';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/presentation/views/text/text_field_builder.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_button_arrow_down_field_widget.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/modal_list_action_button_widget.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_icon_widget.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_controller.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/app_bar_mailbox_creator_builder.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/create_mailbox_name_input_decoration_builder.dart';
import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/modal_folder_tree_list_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class MailboxCreatorView extends GetWidget<MailboxCreatorController> {
final _maxHeight = 656.0;
@override
final controller = Get.find<MailboxCreatorController>();
@@ -26,234 +22,181 @@ class MailboxCreatorView extends GetWidget<MailboxCreatorController> {
@override
Widget build(BuildContext context) {
log('MailboxCreatorView::build():');
return PointerInterceptor(
child: GestureDetector(
onTap: () => controller.closeMailboxCreator(context),
child: Card(
margin: EdgeInsets.zero,
borderOnForeground: false,
color: Colors.transparent,
child: SafeArea(
top: PlatformInfo.isMobile && controller.responsiveUtils.isPortraitMobile(context),
bottom: false,
left: false,
right: false,
child: Center(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
margin: _getMarginView(context),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: _getRadiusView(context),
boxShadow: const [
BoxShadow(
color: AppColor.colorShadowLayerBottom,
blurRadius: 96,
spreadRadius: 96,
offset: Offset.zero),
BoxShadow(
color: AppColor.colorShadowLayerTop,
blurRadius: 2,
spreadRadius: 2,
offset: Offset.zero),
]),
width: _getWidthView(context),
height: _getHeightView(context),
child: ClipRRect(
borderRadius: _getRadiusView(context),
child: SafeArea(
top: false,
bottom: false,
left: PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context),
right: PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context),
child: Column(children: [
_buildAppBar(context),
const Divider(color: AppColor.colorDividerDestinationPicker, height: 1),
_buildCreateMailboxNameInput(context),
_buildMailboxLocation(context),
]),
)
)
),
)
),
final appLocalizations = AppLocalizations.of(context);
final theme = Theme.of(context);
Widget bodyWidget = 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: min(
controller.responsiveUtils.getSizeScreenWidth(context) - 32,
554,
),
constraints: BoxConstraints(
maxHeight: min(
controller.responsiveUtils.getSizeScreenHeight(context) - 100,
671,
),
),
);
}
clipBehavior: Clip.antiAlias,
child: Stack(
children: [
Padding(
padding: EdgeInsetsDirectional.only(
start: 32,
end: 32,
bottom: controller.responsiveUtils.isMobile(context) ? 0 : 16,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 64,
alignment: Alignment.center,
child: Text(
appLocalizations.createANewFolder,
style: theme.textTheme.headlineSmall?.copyWith(
color: AppColor.m3SurfaceBackground,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Obx(() {
final folderName =
controller.selectedMailbox.value?.getDisplayName(context) ??
appLocalizations.personalFolders;
return Center(
child: Text(
appLocalizations
.subtitleDisplayTheFolderNameLocationInFolderCreationModal(
folderName,
),
style: ThemeUtils.textStyleInter400.copyWith(
color: AppColor.steelGrayA540,
fontSize: 13,
height: 20 / 13,
),
textAlign: TextAlign.center,
),
);
}),
const SizedBox(height: 32),
Obx(
() => LabelInputFieldBuilder(
label: AppLocalizations.of(context).folderName,
hintText: AppLocalizations.of(context).enterTheFolderName,
textEditingController: controller.nameInputController,
focusNode: controller.nameInputFocusNode,
errorText: controller.getErrorInputNameString(context),
arrangeHorizontally: false,
isLabelHasColon: false,
labelStyle: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
runSpacing: 16,
inputFieldMaxWidth: double.infinity,
onTextChange: controller.setNewNameMailbox,
),
),
Padding(
padding: const EdgeInsets.only(top: 26, bottom: 16),
child: Text(
appLocalizations.selectTheFolderLocation,
style: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Obx(() {
final selectedMailbox = controller.selectedMailbox.value;
final isFolderModalEnabled =
controller.isFolderModalEnabled.isTrue;
Widget _buildAppBar(BuildContext context) {
return (AppBarMailboxCreatorBuilder(
context,
title: AppLocalizations.of(context).newFolder,
isValidated: true)
..addOnCancelActionClick(() => controller.closeMailboxCreator(context))
..addOnDoneActionClick(() => controller.createNewMailbox(context)))
.build();
}
Widget _buildCreateMailboxNameInput(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Obx(() => TextFieldBuilder(
onTextChange: controller.setNewNameMailbox,
keyboardType: TextInputType.visiblePassword,
cursorColor: AppColor.colorTextButton,
controller: controller.nameInputController,
textDirection: DirectionUtils.getDirectionByLanguage(context),
maxLines: 1,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
color: AppColor.colorNameEmail,
fontSize: 16,
overflow: CommonTextStyle.defaultTextOverFlow),
focusNode: controller.nameInputFocusNode,
decoration: (CreateMailboxNameInputDecorationBuilder()
..setHintText(AppLocalizations.of(context).hintInputCreateNewFolder)
..setErrorText(controller.getErrorInputNameString(context)))
.build(),
))
);
}
Widget _buildMailboxLocation(BuildContext context) {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Text(
AppLocalizations.of(context).selectParentFolder,
textAlign: TextAlign.left,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 13,
color: AppColor.colorHintSearchBar,
fontWeight: FontWeight.normal)),
),
Container(
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: AppColor.colorBgMailbox
return Flexible(
child: AnimatedSize(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
alignment: Alignment.topCenter,
child: isFolderModalEnabled
? Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
border: Border.all(
color: AppColor.m3Neutral90,
width: 1,
),
),
padding: const EdgeInsetsDirectional.only(
start: 14,
top: 13,
bottom: 13,
end: 4,
),
child: Obx(() => ModalFolderTreeListWidget(
defaultTree: controller.defaultMailboxTree.value,
personalTree: controller.personalMailboxTree.value,
imagePaths: controller.imagePaths,
listScrollController:
controller.listFolderScrollController,
mailboxIdSelected: selectedMailbox?.id,
onSelectFolderAction: controller.selectMailboxLocation,
onToggleFolderAction: controller.toggleFolder,
)),
)
: DefaultButtonArrowDownFieldWidget(
text: selectedMailbox?.getDisplayName(context) ??
appLocalizations.personalFolders,
icon: MailboxIconWidget(
icon: selectedMailbox?.getMailboxIcon(controller.imagePaths)
?? controller.imagePaths.icFolderMailbox,
),
iconArrowDown: controller.imagePaths.icDropDown,
onTap: () => controller.openFolderModal(context),
),
),
);
}),
ModalListActionButtonWidget(
positiveLabel: appLocalizations.createFolder,
negativeLabel: appLocalizations.cancel,
padding: const EdgeInsets.symmetric(vertical: 25),
onPositiveAction: () => controller.createNewMailbox(context),
onNegativeAction: controller.closeView,
)
],
),
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () => controller.selectMailboxLocation(context),
customBorder: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
child: Row(children: [
const SizedBox(width: 12),
Obx(() => SvgPicture.asset(
controller.selectedMailbox.value?.getMailboxIcon(controller.imagePaths) ?? controller.imagePaths.icFolderMailbox,
width: PlatformInfo.isWeb ? 20 : 24,
height: PlatformInfo.isWeb ? 20 : 24,
fit: BoxFit.fill)),
const SizedBox(width: 12),
Expanded(child: Obx(() => Text(
controller.selectedMailbox.value?.getDisplayName(context) ?? AppLocalizations.of(context).allFolders,
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 15,
color: controller.selectedMailbox.value == null
? AppColor.colorHintSearchBar
: AppColor.colorNameEmail),
))),
const SizedBox(width: 12),
IconButton(
color: AppColor.primaryColor,
icon: SvgPicture.asset(
DirectionUtils.isDirectionRTLByLanguage(context)
? controller.imagePaths.icBack
: controller.imagePaths.icCollapseFolder,
colorFilter: AppColor.colorCollapseMailbox.asFilter(),
fit: BoxFit.fill),
onPressed: () => controller.selectMailboxLocation(context))
])),
)
)
]);
}
DefaultCloseButtonWidget(
iconClose: controller.imagePaths.icCloseDialog,
onTapActionCallback: controller.closeView,
),
],
),
);
double _getWidthView(BuildContext context) {
if (PlatformInfo.isWeb) {
if (controller.responsiveUtils.isMobile(context)) {
return double.infinity;
} else {
return 556;
}
} else {
if (controller.responsiveUtils.isLandscapeMobile(context) ||
controller.responsiveUtils.isPortraitMobile(context)) {
return double.infinity;
} else {
return 556;
}
}
return Center(child: bodyWidget);
}
double _getHeightView(BuildContext context) {
if (PlatformInfo.isWeb) {
if (controller.responsiveUtils.isMobile(context)) {
return double.infinity;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return _maxHeight;
} else {
return double.infinity;
}
}
} else {
if (controller.responsiveUtils.isLandscapeMobile(context) ||
controller.responsiveUtils.isPortraitMobile(context)) {
return double.infinity;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return _maxHeight;
} else {
return double.infinity;
}
}
}
}
EdgeInsets _getMarginView(BuildContext context) {
if (PlatformInfo.isWeb) {
if (controller.responsiveUtils.isMobile(context)) {
return EdgeInsets.zero;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return const EdgeInsets.symmetric(vertical: 12);
} else {
return const EdgeInsets.symmetric(vertical: 50);
}
}
} else {
if (controller.responsiveUtils.isLandscapeMobile(context) ||
controller.responsiveUtils.isPortraitMobile(context)) {
return EdgeInsets.zero;
} else {
if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) {
return const EdgeInsets.symmetric(vertical: 12);
} else {
return const EdgeInsets.symmetric(vertical: 50);
}
}
}
}
BorderRadius _getRadiusView(BuildContext context) {
if (PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context)) {
return BorderRadius.zero;
} else if (controller.responsiveUtils.isMobile(context)) {
return const BorderRadius.only(
topRight: Radius.circular(16),
topLeft: Radius.circular(16));
} else {
return const BorderRadius.all(Radius.circular(16));
}
}
}
}
@@ -1,34 +1,23 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
class MailboxCreatorArguments with EquatableMixin{
final AccountId accountId;
final MailboxTree personalMailboxTree;
final MailboxTree defaultMailboxTree;
final MailboxTree teamMailboxesTree;
final Session session;
final PresentationMailbox? selectedMailbox;
MailboxCreatorArguments(
this.accountId,
this.defaultMailboxTree,
this.defaultMailboxTree,
this.personalMailboxTree,
this.teamMailboxesTree,
this.session,
this.selectedMailbox
this.selectedMailbox,
);
@override
List<Object?> get props => [
accountId,
defaultMailboxTree,
defaultMailboxTree,
personalMailboxTree,
teamMailboxesTree,
session,
selectedMailbox
selectedMailbox,
];
}
@@ -1,92 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnCancelActionClick = void Function();
typedef OnDoneActionClick = void Function();
class AppBarMailboxCreatorBuilder {
OnCancelActionClick? _cancelActionClick;
OnDoneActionClick? _onDoneActionClick;
final BuildContext _context;
final bool isValidated;
final String? title;
AppBarMailboxCreatorBuilder(
this._context,
{
this.title,
this.isValidated = false
}
);
void addOnCancelActionClick(OnCancelActionClick onCancelActionClick) {
_cancelActionClick = onCancelActionClick;
}
void addOnDoneActionClick(OnDoneActionClick onDoneActionClick) {
_onDoneActionClick = onDoneActionClick;
}
Widget build() {
return Container(
color: Colors.white,
height: 52,
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(children: [
_buildCancelButton(),
Expanded(child: _buildTitle()),
_buildCreateButton()
])
);
}
Widget _buildCancelButton() {
return Material(
borderRadius: BorderRadius.circular(20),
color: Colors.transparent,
child: TextButton(
child: Text(
AppLocalizations.of(_context).cancel,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 17, color: AppColor.colorTextButton),
),
onPressed: () => _cancelActionClick?.call()
)
);
}
Widget _buildCreateButton() {
return Material(
borderRadius: BorderRadius.circular(20),
color: Colors.transparent,
child: TextButton(
child: Text(
AppLocalizations.of(_context).done,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 17,
color: isValidated
? AppColor.colorTextButton
: AppColor.colorDisableMailboxCreateButton),
),
onPressed: () => isValidated ? _onDoneActionClick?.call() : null
)
);
}
Widget _buildTitle() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
title ?? '',
textAlign: TextAlign.center,
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 20,
color: AppColor.colorNameEmail,
fontWeight: FontWeight.bold)));
}
}
@@ -1,36 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/material.dart';
class CreateMailboxNameInputDecorationBuilder 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: AppColor.colorNameEmail, fontSize: 16),
hintText: hintText,
hintStyle: hintStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(color: AppColor.colorHintInputCreateMailbox, fontSize: 16),
contentPadding: contentPadding ?? const EdgeInsets.all(12),
errorText: errorText,
errorStyle: errorTextStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(color: AppColor.colorInputBorderErrorVerifyName, fontSize: 13),
filled: true,
fillColor: errorText?.isNotEmpty == true
? AppColor.colorInputBackgroundErrorVerifyName
: AppColor.colorInputBackgroundCreateMailbox);
}
}
@@ -0,0 +1,114 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/list/tree_view.dart';
import 'package:flutter/material.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/mailbox/expand_mode.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_displayed.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_method_action_define.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/folder_item_widget.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class ModalFolderTreeListWidget extends StatelessWidget {
final MailboxTree defaultTree;
final MailboxTree personalTree;
final ScrollController listScrollController;
final ImagePaths imagePaths;
final OnClickOpenMailboxNodeAction onSelectFolderAction;
final OnClickExpandMailboxNodeAction onToggleFolderAction;
final MailboxId? mailboxIdSelected;
const ModalFolderTreeListWidget({
super.key,
required this.defaultTree,
required this.personalTree,
required this.listScrollController,
required this.imagePaths,
required this.onSelectFolderAction,
required this.onToggleFolderAction,
this.mailboxIdSelected,
});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: const ClampingScrollPhysics(),
controller: listScrollController,
padding: const EdgeInsetsDirectional.only(end: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FolderItemWidget(
folderName: AppLocalizations.of(context).personalFolders,
imagePaths: imagePaths,
isSelected: mailboxIdSelected == null,
onTapAction: () => onSelectFolderAction(null),
),
if (defaultTree.root.hasChildren())
_buildFolderTree(
mailboxTree: defaultTree,
mailboxIdSelected: mailboxIdSelected,
),
if (personalTree.root.hasChildren())
_buildFolderTree(
mailboxTree: personalTree,
mailboxIdSelected: mailboxIdSelected,
),
],
),
);
}
Widget _buildFolderTree({
required MailboxTree mailboxTree,
MailboxId? mailboxIdSelected,
}) {
return TreeView(
children: _buildListChildTileWidget(
parentNode: mailboxTree.root,
mailboxIdSelected: mailboxIdSelected,
),
);
}
List<Widget> _buildListChildTileWidget({
required MailboxNode parentNode,
MailboxId? mailboxIdSelected,
}) {
return parentNode.childrenItems?.map(
(mailboxNode) {
if (mailboxNode.hasChildren()) {
return TreeViewChild(
isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND,
paddingChild: const EdgeInsetsDirectional.only(start: 14),
parent: MailboxItemWidget(
mailboxNode: mailboxNode,
mailboxIdAlreadySelected: mailboxIdSelected,
mailboxDisplayed: MailboxDisplayed.modalFolder,
hoverColor: AppColor.blue100,
onOpenMailboxFolderClick: onSelectFolderAction,
onExpandFolderActionClick: onToggleFolderAction,
),
children: _buildListChildTileWidget(
parentNode: mailboxNode,
mailboxIdSelected: mailboxIdSelected,
),
).build();
} else {
return MailboxItemWidget(
mailboxNode: mailboxNode,
mailboxDisplayed: MailboxDisplayed.modalFolder,
mailboxIdAlreadySelected: mailboxIdSelected,
hoverColor: AppColor.blue100,
onOpenMailboxFolderClick: onSelectFolderAction,
onExpandFolderActionClick: onToggleFolderAction,
);
}
},
).toList() ?? [];
}
}