TF-1884 Support RTL mode for TextField
(cherry picked from commit 3f8445141bea9dbf276a6a336114e4e2b549936f)
This commit is contained in:
@@ -47,7 +47,6 @@ export 'utils/platform_info.dart';
|
||||
export 'presentation/views/text/slogan_builder.dart';
|
||||
export 'presentation/views/text/text_field_builder.dart';
|
||||
export 'presentation/views/text/input_decoration_builder.dart';
|
||||
export 'presentation/views/text/text_builder.dart';
|
||||
export 'presentation/views/text/rich_text_builder.dart';
|
||||
export 'presentation/views/responsive/responsive_widget.dart';
|
||||
export 'presentation/views/list/tree_view.dart';
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -576,6 +577,7 @@ class _TypeAheadFieldQuickSearchState<T, R> extends State<TypeAheadFieldQuickSea
|
||||
FocusNode? _focusNode;
|
||||
TextEditingController? _textEditingController;
|
||||
_SuggestionsBox? _suggestionsBox;
|
||||
late TextDirection _textDirection;
|
||||
|
||||
TextEditingController? get _effectiveController =>
|
||||
widget.textFieldConfiguration.controller ?? _textEditingController;
|
||||
@@ -622,6 +624,8 @@ class _TypeAheadFieldQuickSearchState<T, R> extends State<TypeAheadFieldQuickSea
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
_textDirection = widget.textFieldConfiguration.textDirection ?? TextDirection.ltr;
|
||||
|
||||
if (widget.textFieldConfiguration.controller == null) {
|
||||
_textEditingController = TextEditingController();
|
||||
}
|
||||
@@ -840,7 +844,17 @@ class _TypeAheadFieldQuickSearchState<T, R> extends State<TypeAheadFieldQuickSea
|
||||
maxLength: widget.textFieldConfiguration.maxLength,
|
||||
maxLengthEnforcement: widget.textFieldConfiguration.maxLengthEnforcement,
|
||||
obscureText: widget.textFieldConfiguration.obscureText,
|
||||
onChanged: widget.textFieldConfiguration.onChanged,
|
||||
onChanged: (input) {
|
||||
widget.textFieldConfiguration.onChanged?.call(input);
|
||||
if (input.isNotEmpty) {
|
||||
final directionByText = DirectionUtils.getDirectionByEndsText(input);
|
||||
if (directionByText != _textDirection) {
|
||||
setState(() {
|
||||
_textDirection = directionByText;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
onSubmitted: widget.textFieldConfiguration.onSubmitted,
|
||||
onEditingComplete: widget.textFieldConfiguration.onEditingComplete,
|
||||
onTap: widget.textFieldConfiguration.onTap,
|
||||
@@ -851,9 +865,8 @@ class _TypeAheadFieldQuickSearchState<T, R> extends State<TypeAheadFieldQuickSea
|
||||
cursorWidth: widget.textFieldConfiguration.cursorWidth,
|
||||
cursorRadius: widget.textFieldConfiguration.cursorRadius,
|
||||
cursorColor: widget.textFieldConfiguration.cursorColor,
|
||||
textDirection: widget.textFieldConfiguration.textDirection,
|
||||
enableInteractiveSelection:
|
||||
widget.textFieldConfiguration.enableInteractiveSelection,
|
||||
textDirection: _textDirection,
|
||||
enableInteractiveSelection: widget.textFieldConfiguration.enableInteractiveSelection,
|
||||
readOnly: widget.hideKeyboard,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import 'package:core/presentation/utils/style_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TextBuilder {
|
||||
String? _text;
|
||||
TextStyle? _textStyle;
|
||||
TextAlign? _textAlign;
|
||||
Key? _key;
|
||||
|
||||
TextBuilder key(Key key) {
|
||||
_key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
TextBuilder text(String text) {
|
||||
_text = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
TextBuilder textStyle(TextStyle textStyle) {
|
||||
_textStyle = textStyle;
|
||||
return this;
|
||||
}
|
||||
|
||||
TextBuilder textAlign(TextAlign textAlign) {
|
||||
_textAlign = textAlign;
|
||||
return this;
|
||||
}
|
||||
|
||||
Text build() {
|
||||
return Text(_text ?? '', key: _key ?? const Key('TextBuilder'), style: _textStyle ?? CommonTextStyle.textStyleNormal, textAlign: _textAlign ?? TextAlign.center);
|
||||
}
|
||||
}
|
||||
|
||||
class CenterTextBuilder extends TextBuilder {
|
||||
@override
|
||||
Text build() {
|
||||
return Text(_text ?? '', key: _key ?? const Key('TextBuilder'), style: _textStyle, textAlign: TextAlign.center);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +1,117 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TextFieldBuilder {
|
||||
Key? _key;
|
||||
ValueChanged<String>? _onTextChange;
|
||||
ValueChanged<String>? _onTextSubmitted;
|
||||
TextStyle? _textStyle;
|
||||
TextInputAction? _textInputAction;
|
||||
InputDecoration? _inputDecoration;
|
||||
bool? _obscureText;
|
||||
int? _maxLines = 1;
|
||||
int? _minLines;
|
||||
TextEditingController? _textController;
|
||||
TextInputType? _keyboardType;
|
||||
Color? _cursorColor;
|
||||
bool? _autoFocus;
|
||||
FocusNode? _focusNode;
|
||||
class TextFieldBuilder extends StatefulWidget {
|
||||
|
||||
void key(Key key) {
|
||||
_key = key;
|
||||
final ValueChanged<String>? onTextChange;
|
||||
final ValueChanged<String>? onTextSubmitted;
|
||||
final VoidCallback? onTap;
|
||||
final TextStyle? textStyle;
|
||||
final TextInputAction? textInputAction;
|
||||
final InputDecoration? decoration;
|
||||
final bool obscureText;
|
||||
final int? maxLines;
|
||||
final int? minLines;
|
||||
final TextEditingController? controller;
|
||||
final TextInputType? keyboardType;
|
||||
final Color cursorColor;
|
||||
final bool autoFocus;
|
||||
final FocusNode? focusNode;
|
||||
final String? fromValue;
|
||||
final Brightness? keyboardAppearance;
|
||||
final bool autocorrect;
|
||||
final TextDirection textDirection;
|
||||
final bool readOnly;
|
||||
final MouseCursor? mouseCursor;
|
||||
|
||||
const TextFieldBuilder({
|
||||
super.key,
|
||||
this.cursorColor = AppColor.primaryColor,
|
||||
this.autocorrect = false,
|
||||
this.obscureText = false,
|
||||
this.autoFocus = false,
|
||||
this.readOnly = false,
|
||||
this.textStyle = const TextStyle(color: AppColor.textFieldTextColor),
|
||||
this.textDirection = TextDirection.ltr,
|
||||
this.textInputAction,
|
||||
this.decoration,
|
||||
this.maxLines,
|
||||
this.minLines,
|
||||
this.controller,
|
||||
this.keyboardType,
|
||||
this.focusNode,
|
||||
this.fromValue,
|
||||
this.keyboardAppearance,
|
||||
this.mouseCursor,
|
||||
this.onTap,
|
||||
this.onTextChange,
|
||||
this.onTextSubmitted,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TextFieldBuilder> createState() => _TextFieldBuilderState();
|
||||
}
|
||||
|
||||
class _TextFieldBuilderState extends State<TextFieldBuilder> {
|
||||
|
||||
late TextEditingController _controller;
|
||||
late TextDirection _textDirection;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.fromValue != null) {
|
||||
_controller = TextEditingController.fromValue(TextEditingValue(text: widget.fromValue!));
|
||||
} else if (widget.controller != null) {
|
||||
_controller = widget.controller!;
|
||||
} else {
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
_textDirection = widget.textDirection;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void onChange(ValueChanged<String> onChange) {
|
||||
_onTextChange = onChange;
|
||||
}
|
||||
|
||||
void onSubmitted(ValueChanged<String> onSubmitted) {
|
||||
_onTextSubmitted = onSubmitted;
|
||||
}
|
||||
|
||||
void textStyle(TextStyle style) {
|
||||
_textStyle = style;
|
||||
}
|
||||
|
||||
void textInputAction(TextInputAction inputAction) {
|
||||
_textInputAction = inputAction;
|
||||
}
|
||||
|
||||
void textDecoration(InputDecoration inputDecoration) {
|
||||
_inputDecoration = inputDecoration;
|
||||
}
|
||||
|
||||
void obscureText(bool obscureText) {
|
||||
_obscureText = obscureText;
|
||||
}
|
||||
|
||||
void setText(String value) {
|
||||
_textController = TextEditingController.fromValue(TextEditingValue(text: value));
|
||||
}
|
||||
|
||||
void addController(TextEditingController? textEditingController) {
|
||||
_textController = textEditingController;
|
||||
}
|
||||
|
||||
void maxLines(int? value) {
|
||||
_maxLines = value;
|
||||
}
|
||||
|
||||
void minLines(int? value) {
|
||||
_minLines = value;
|
||||
}
|
||||
|
||||
void keyboardType(TextInputType? value) {
|
||||
_keyboardType = value;
|
||||
}
|
||||
|
||||
void cursorColor(Color? color) {
|
||||
_cursorColor = color;
|
||||
}
|
||||
|
||||
void autoFocus(bool autoFocus) {
|
||||
_autoFocus = autoFocus;
|
||||
}
|
||||
|
||||
void addFocusNode(FocusNode? focusNode) {
|
||||
_focusNode = focusNode;
|
||||
}
|
||||
|
||||
TextField build() {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
key: _key ?? const Key('TextFieldBuilder'),
|
||||
onChanged: _onTextChange,
|
||||
cursorColor: _cursorColor ?? AppColor.primaryColor,
|
||||
controller: _textController,
|
||||
autocorrect: false,
|
||||
textInputAction: _textInputAction,
|
||||
decoration: _inputDecoration,
|
||||
maxLines: _maxLines,
|
||||
minLines: _minLines,
|
||||
keyboardAppearance: Brightness.light,
|
||||
style: _textStyle ?? const TextStyle(color: AppColor.textFieldTextColor),
|
||||
obscureText: _obscureText ?? false,
|
||||
keyboardType: _keyboardType,
|
||||
onSubmitted: _onTextSubmitted,
|
||||
autofocus: _autoFocus ?? false,
|
||||
focusNode: _focusNode,
|
||||
key: widget.key,
|
||||
controller: _controller,
|
||||
cursorColor: widget.cursorColor,
|
||||
autocorrect: widget.autocorrect,
|
||||
textInputAction: widget.textInputAction,
|
||||
decoration: widget.decoration,
|
||||
maxLines: widget.maxLines,
|
||||
minLines: widget.minLines,
|
||||
keyboardAppearance: widget.keyboardAppearance,
|
||||
style: widget.textStyle,
|
||||
obscureText: widget.obscureText,
|
||||
keyboardType: widget.keyboardType,
|
||||
autofocus: widget.autoFocus,
|
||||
focusNode: widget.focusNode,
|
||||
textDirection: _textDirection,
|
||||
readOnly: widget.readOnly,
|
||||
mouseCursor: widget.mouseCursor,
|
||||
onChanged: (value) {
|
||||
widget.onTextChange?.call(value);
|
||||
if (value.isNotEmpty) {
|
||||
final directionByText = DirectionUtils.getDirectionByEndsText(value);
|
||||
if (directionByText != _textDirection) {
|
||||
setState(() {
|
||||
_textDirection = directionByText;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
onSubmitted: widget.onTextSubmitted,
|
||||
onTap: widget.onTap,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (widget.fromValue == null && widget.controller == null) {
|
||||
_controller.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
abstract class DirectionUtils {
|
||||
|
||||
static bool isDirectionRTLByLanguage(BuildContext context) => intl.Bidi.isRtlLanguage(Localizations.localeOf(context).languageCode);
|
||||
|
||||
static bool isDirectionRTLByEndsText(String text) => intl.Bidi.endsWithRtl(text);
|
||||
|
||||
static TextDirection getDirectionByLanguage(BuildContext context) => isDirectionRTLByLanguage(context) ? TextDirection.rtl : TextDirection.ltr;
|
||||
|
||||
static TextDirection getDirectionByEndsText(String text) => isDirectionRTLByEndsText(text) ? TextDirection.rtl : TextDirection.ltr;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class PlatformInfo {
|
||||
static bool get isWeb => kIsWeb;
|
||||
static const bool isWeb = kIsWeb;
|
||||
static bool get isLinux => !kIsWeb && Platform.isLinux;
|
||||
static bool get isWindows => !kIsWeb && Platform.isWindows;
|
||||
static bool get isMacOS => !kIsWeb && Platform.isMacOS;
|
||||
|
||||
@@ -296,6 +296,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.0"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -70,6 +70,9 @@ dependencies:
|
||||
# flutter_test from sdk depends on collection 1.17.0
|
||||
collection: 1.17.0
|
||||
|
||||
# flutter_localizations depends on intl 0.17.0
|
||||
intl: 0.17.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tmail_ui_user/features/base/widget/text_input_decoration_builder.dart';
|
||||
|
||||
typedef OnChangeInputAction = Function(String? value);
|
||||
typedef OnChangeInputAction = Function(String value);
|
||||
|
||||
class TextInputFieldBuilder extends StatelessWidget {
|
||||
|
||||
@@ -46,24 +47,23 @@ class TextInputFieldBuilder extends StatelessWidget {
|
||||
color: AppColor.colorContentEmail)),
|
||||
const SizedBox(height: 8)
|
||||
],
|
||||
(TextFieldBuilder()
|
||||
..onChange((value) => onChangeInputAction?.call(value))
|
||||
..textInputAction(TextInputAction.next)
|
||||
..addController(editingController ?? TextEditingController())
|
||||
..addFocusNode(focusNode)
|
||||
..textStyle(const TextStyle(color: Colors.black, fontSize: 16))
|
||||
..keyboardType(inputType ?? TextInputType.text)
|
||||
..minLines(minLines)
|
||||
..maxLines(maxLines)
|
||||
..textDecoration((TextInputDecorationBuilder()
|
||||
..setContentPadding(const EdgeInsets.symmetric(
|
||||
vertical: BuildUtils.isWeb ? 16 : 12,
|
||||
horizontal: 12))
|
||||
..setHintText(hint)
|
||||
..setFillColor(backgroundColor)
|
||||
..setErrorText(error))
|
||||
.build()))
|
||||
.build()
|
||||
TextFieldBuilder(
|
||||
onTextChange: onChangeInputAction,
|
||||
textInputAction: TextInputAction.next,
|
||||
controller: editingController,
|
||||
focusNode: focusNode,
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 16),
|
||||
keyboardType: inputType ?? TextInputType.text,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
minLines: minLines,
|
||||
maxLines: maxLines,
|
||||
decoration: (TextInputDecorationBuilder()
|
||||
..setContentPadding(const EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 16 : 12, horizontal: 12))
|
||||
..setHintText(hint)
|
||||
..setFillColor(backgroundColor)
|
||||
..setErrorText(error))
|
||||
.build(),
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.d
|
||||
import 'package:core/presentation/views/responsive/responsive_widget.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:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:filesize/filesize.dart';
|
||||
@@ -490,16 +491,17 @@ class ComposerView extends GetWidget<ComposerController>
|
||||
)
|
||||
)
|
||||
),
|
||||
Expanded(child: (TextFieldBuilder()
|
||||
..key(const Key('subject_email_input'))
|
||||
..cursorColor(AppColor.colorTextButton)
|
||||
..maxLines(responsiveUtils.isMobile(context) ? null : 1)
|
||||
..addFocusNode(controller.subjectEmailInputFocusNode)
|
||||
..onChange((value) => controller.setSubjectEmail(value))
|
||||
..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal))
|
||||
..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none))
|
||||
..addController(controller.subjectEmailInputController))
|
||||
.build())
|
||||
Expanded(child: TextFieldBuilder(
|
||||
key: const Key('subject_email_input'),
|
||||
cursorColor: AppColor.colorTextButton,
|
||||
maxLines: responsiveUtils.isMobile(context) ? null : 1,
|
||||
focusNode: controller.subjectEmailInputFocusNode,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
onTextChange: controller.setSubjectEmail,
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal),
|
||||
decoration: const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none),
|
||||
controller: controller.subjectEmailInputController,
|
||||
))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:core/presentation/views/image/avatar_builder.dart';
|
||||
import 'package:core/presentation/views/responsive/responsive_widget.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:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:filesize/filesize.dart';
|
||||
@@ -690,16 +691,16 @@ class ComposerView extends GetWidget<ComposerController>
|
||||
)
|
||||
)
|
||||
),
|
||||
Expanded(child: (TextFieldBuilder()
|
||||
..key(const Key('subject_email_input'))
|
||||
..cursorColor(AppColor.colorTextButton)
|
||||
..addFocusNode(controller.subjectEmailInputFocusNode)
|
||||
..onChange((value) => controller.setSubjectEmail(value))
|
||||
..textStyle(const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal))
|
||||
..textDecoration(const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none))
|
||||
..addController(controller.subjectEmailInputController))
|
||||
.build()
|
||||
)
|
||||
Expanded(child: TextFieldBuilder(
|
||||
key: const Key('subject_email_input'),
|
||||
cursorColor: AppColor.colorTextButton,
|
||||
focusNode: controller.subjectEmailInputFocusNode,
|
||||
onTextChange: controller.setSubjectEmail,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.normal),
|
||||
decoration: const InputDecoration(contentPadding: EdgeInsets.zero, border: InputBorder.none),
|
||||
controller: controller.subjectEmailInputController,
|
||||
))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/build_utils.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -101,7 +102,7 @@ class InsertImageDialogBuilder {
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w500)),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
TextFieldBuilder(
|
||||
controller: _inputUrlController,
|
||||
textInputAction: TextInputAction.done,
|
||||
decoration: InputDecoration(
|
||||
@@ -110,7 +111,7 @@ class InsertImageDialogBuilder {
|
||||
errorText: validateFailed,
|
||||
errorMaxLines: 2,
|
||||
),
|
||||
),
|
||||
)
|
||||
]),
|
||||
),
|
||||
actionsPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||
|
||||
@@ -91,6 +91,10 @@ class ContactController extends BaseController {
|
||||
_deBouncerTime.value = text;
|
||||
}
|
||||
|
||||
void onSearchTextAction(String text) {
|
||||
_deBouncerTime.value = text;
|
||||
}
|
||||
|
||||
void clearAllTextInputSearchForm() {
|
||||
textInputSearchController?.clear();
|
||||
searchQuery.value = SearchQuery.initial();
|
||||
|
||||
@@ -78,29 +78,29 @@ class ContactView extends GetWidget<ContactController> {
|
||||
onCloseContactView: () => controller.closeContactView(context))
|
||||
),
|
||||
const Divider(color: AppColor.colorDividerComposer, height: 1),
|
||||
(SearchAppBarWidget(
|
||||
_imagePaths,
|
||||
controller.searchQuery.value,
|
||||
controller.textInputSearchFocus,
|
||||
controller.textInputSearchController,
|
||||
hasBackButton: false,
|
||||
hasSearchButton: true)
|
||||
..addPadding(EdgeInsets.zero)
|
||||
..setHeightSearchBar(44)
|
||||
..setMargin(ContactUtils.getPaddingSearchInputForm(context, _responsiveUtils))
|
||||
..addDecoration(BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: AppColor.colorBgSearchBar))
|
||||
..addIconClearText(SvgPicture.asset(
|
||||
_imagePaths.icClearTextSearch,
|
||||
width: 18,
|
||||
height: 18,
|
||||
fit: BoxFit.fill))
|
||||
..setHintText(AppLocalizations.of(context).hintSearchInputContact)
|
||||
..addOnClearTextSearchAction(() => controller.clearAllTextInputSearchForm())
|
||||
..addOnTextChangeSearchAction(controller.onTextSearchChange)
|
||||
..addOnSearchTextAction((query) => {}))
|
||||
.build(),
|
||||
SearchAppBarWidget(
|
||||
imagePaths: _imagePaths,
|
||||
searchQuery: controller.searchQuery.value,
|
||||
searchFocusNode: controller.textInputSearchFocus,
|
||||
searchInputController :controller.textInputSearchController,
|
||||
hasBackButton: false,
|
||||
hasSearchButton: true,
|
||||
padding: EdgeInsets.zero,
|
||||
heightSearchBar: 44,
|
||||
margin: ContactUtils.getPaddingSearchInputForm(context, _responsiveUtils),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
color: AppColor.colorBgSearchBar),
|
||||
iconClearText: SvgPicture.asset(
|
||||
_imagePaths.icClearTextSearch,
|
||||
width: 18,
|
||||
height: 18,
|
||||
fit: BoxFit.fill),
|
||||
hintText: AppLocalizations.of(context).hintSearchInputContact,
|
||||
onClearTextSearchAction: controller.clearAllTextInputSearchForm,
|
||||
onTextChangeSearchAction: controller.onTextSearchChange,
|
||||
onSearchTextAction: controller.onSearchTextAction,
|
||||
),
|
||||
Expanded(child: Obx(() {
|
||||
if (controller.listContactSearched.isNotEmpty) {
|
||||
return Container(
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:core/presentation/views/list/tree_view.dart';
|
||||
import 'package:core/presentation/views/search/search_bar_view.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/build_utils.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -171,23 +172,24 @@ class DestinationPickerView extends GetWidget<DestinationPickerController>
|
||||
Widget _buildCreateMailboxNameInput(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
child: Obx(() => (TextFieldBuilder()
|
||||
..key(const Key('create_mailbox_name_input'))
|
||||
..onChange((value) => controller.setNewNameMailbox(value))
|
||||
..keyboardType(TextInputType.visiblePassword)
|
||||
..cursorColor(AppColor.colorTextButton)
|
||||
..addController(controller.nameInputController)
|
||||
..maxLines(1)
|
||||
..textStyle(const TextStyle(
|
||||
color: AppColor.colorNameEmail,
|
||||
fontSize: 16,
|
||||
overflow: CommonTextStyle.defaultTextOverFlow))
|
||||
..addFocusNode(controller.nameInputFocusNode)
|
||||
..textDecoration((CreateMailboxNameInputDecorationBuilder()
|
||||
..setHintText(AppLocalizations.of(context).hint_input_create_new_mailbox)
|
||||
..setErrorText(controller.getErrorInputNameString(context)))
|
||||
.build()))
|
||||
.build())
|
||||
child: Obx(() => TextFieldBuilder(
|
||||
key: const Key('create_mailbox_name_input'),
|
||||
onTextChange: controller.setNewNameMailbox,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
cursorColor: AppColor.colorTextButton,
|
||||
controller: controller.nameInputController,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
maxLines: 1,
|
||||
textStyle: const TextStyle(
|
||||
color: AppColor.colorNameEmail,
|
||||
fontSize: 16,
|
||||
overflow: CommonTextStyle.defaultTextOverFlow),
|
||||
focusNode: controller.nameInputFocusNode,
|
||||
decoration: (CreateMailboxNameInputDecorationBuilder()
|
||||
..setHintText(AppLocalizations.of(context).hint_input_create_new_mailbox)
|
||||
..setErrorText(controller.getErrorInputNameString(context)))
|
||||
.build(),
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -554,25 +556,30 @@ class DestinationPickerView extends GetWidget<DestinationPickerController>
|
||||
colorFilter: AppColor.colorTextButton.asFilter(),
|
||||
fit: BoxFit.fill),
|
||||
onTap: () => controller.disableSearch(context))),
|
||||
Expanded(child: (SearchAppBarWidget(
|
||||
_imagePaths,
|
||||
controller.searchQuery.value,
|
||||
controller.searchFocus,
|
||||
controller.searchInputController,
|
||||
hasBackButton: false,
|
||||
hasSearchButton: true)
|
||||
..addPadding(EdgeInsets.zero)
|
||||
..setMargin(EdgeInsets.only(
|
||||
right: AppUtils.isDirectionRTL(context) ? 0 : 16,
|
||||
left: AppUtils.isDirectionRTL(context) ? 16 : 0,
|
||||
))
|
||||
..addDecoration(BoxDecoration(borderRadius: BorderRadius.circular(12), color: AppColor.colorBgSearchBar))
|
||||
..addIconClearText(SvgPicture.asset(_imagePaths.icClearTextSearch, width: 18, height: 18, fit: BoxFit.fill))
|
||||
..setHintText(AppLocalizations.of(context).hint_search_mailboxes)
|
||||
..addOnClearTextSearchAction(() => controller.clearSearchText())
|
||||
..addOnTextChangeSearchAction((query) => controller.searchMailbox(query))
|
||||
..addOnSearchTextAction((query) => controller.searchMailbox(query)))
|
||||
.build())
|
||||
Expanded(child: SearchAppBarWidget(
|
||||
imagePaths: _imagePaths,
|
||||
searchQuery: controller.searchQuery.value,
|
||||
searchFocusNode: controller.searchFocus,
|
||||
searchInputController: controller.searchInputController,
|
||||
hasBackButton: false,
|
||||
hasSearchButton: true,
|
||||
padding: EdgeInsets.zero,
|
||||
margin: EdgeInsets.only(
|
||||
right: DirectionUtils.isDirectionRTLByLanguage(context) ? 0 : 16,
|
||||
left: DirectionUtils.isDirectionRTLByLanguage(context) ? 16 : 0),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
color: AppColor.colorBgSearchBar),
|
||||
iconClearText: SvgPicture.asset(
|
||||
_imagePaths.icClearTextSearch,
|
||||
width: 18,
|
||||
height: 18,
|
||||
fit: BoxFit.fill),
|
||||
hintText: AppLocalizations.of(context).hint_search_mailboxes,
|
||||
onClearTextSearchAction: controller.clearSearchText,
|
||||
onTextChangeSearchAction: controller.searchMailbox,
|
||||
onSearchTextAction: controller.searchMailbox,
|
||||
))
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
+16
-16
@@ -1,7 +1,8 @@
|
||||
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/build_utils.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/identity_creator/presentation/widgets/identity_input_decoration_builder.dart';
|
||||
|
||||
@@ -41,21 +42,20 @@ class IdentityInputFieldBuilder extends StatelessWidget {
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColor.colorContentEmail)),
|
||||
const SizedBox(height: 8),
|
||||
(TextFieldBuilder()
|
||||
..onChange((value) => onChangeInputNameAction?.call(value))
|
||||
..textInputAction(TextInputAction.next)
|
||||
..addController(editingController ?? TextEditingController())
|
||||
..autoFocus(true)
|
||||
..addFocusNode(focusNode)
|
||||
..textStyle(const TextStyle(color: Colors.black, fontSize: 16))
|
||||
..keyboardType(inputType ?? TextInputType.text)
|
||||
..textDecoration((IdentityInputDecorationBuilder()
|
||||
..setContentPadding(const EdgeInsets.symmetric(
|
||||
vertical: BuildUtils.isWeb ? 16 : 12,
|
||||
horizontal: 12))
|
||||
..setErrorText(_error))
|
||||
.build()))
|
||||
.build()
|
||||
TextFieldBuilder(
|
||||
onTextChange: onChangeInputNameAction,
|
||||
textInputAction: TextInputAction.next,
|
||||
autoFocus: true,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
controller: editingController,
|
||||
focusNode: focusNode,
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 16),
|
||||
keyboardType: inputType ?? TextInputType.text,
|
||||
decoration: (IdentityInputDecorationBuilder()
|
||||
..setContentPadding(const EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 16 : 12,horizontal: 12))
|
||||
..setErrorText(_error))
|
||||
.build(),
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:core/presentation/views/text/text_builder.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -30,9 +29,8 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
|
||||
padding: const EdgeInsets.only(top: 11, bottom: 36, left: 58, right: 58),
|
||||
child: SizedBox(
|
||||
width: responsiveUtils.getWidthLoginTextField(context),
|
||||
child: CenterTextBuilder()
|
||||
.key(const Key('login_message'))
|
||||
.text(loginState.viewState.fold(
|
||||
child: Text(
|
||||
loginState.viewState.fold(
|
||||
(failure) {
|
||||
if (failure is LoginMissUrlAction) {
|
||||
return AppLocalizations.of(context).requiredUrl;
|
||||
@@ -43,7 +41,7 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
|
||||
} else if (failure is LoginSSONotAvailableAction) {
|
||||
return AppLocalizations.of(context).ssoNotAvailable;
|
||||
} else if (failure is GetOIDCConfigurationFailure
|
||||
|| failure is LoginCanNotVerifySSOConfigurationAction) {
|
||||
|| failure is LoginCanNotVerifySSOConfigurationAction) {
|
||||
return AppLocalizations.of(context).canNotVerifySSOConfiguration;
|
||||
} else if (failure is GetTokenOIDCFailure || failure is LoginCanNotGetTokenAction) {
|
||||
return AppLocalizations.of(context).canNotGetToken;
|
||||
@@ -58,15 +56,16 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
|
||||
return AppLocalizations.of(context).loginInputSSOMessage;
|
||||
}
|
||||
return AppLocalizations.of(context).loginInputUrlMessage;
|
||||
}))
|
||||
.textStyle(TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: loginState.viewState.fold(
|
||||
(failure) => AppColor.textFieldErrorBorderColor,
|
||||
(success) => AppColor.colorNameEmail)))
|
||||
.build()
|
||||
)
|
||||
}),
|
||||
key: const Key('login_message'),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: loginState.viewState.fold(
|
||||
(failure) => AppColor.textFieldErrorBorderColor,
|
||||
(success) => AppColor.colorNameEmail)),
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:core/presentation/utils/responsive_utils.dart';
|
||||
import 'package:core/presentation/utils/style_utils.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/build_utils.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -112,23 +113,24 @@ class MailboxCreatorView extends GetWidget<MailboxCreatorController> {
|
||||
Widget _buildCreateMailboxNameInput(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
child: Obx(() => (TextFieldBuilder()
|
||||
..key(const Key('create_mailbox_name_input'))
|
||||
..onChange((value) => controller.setNewNameMailbox(value))
|
||||
..keyboardType(TextInputType.visiblePassword)
|
||||
..cursorColor(AppColor.colorTextButton)
|
||||
..addController(controller.nameInputController)
|
||||
..maxLines(1)
|
||||
..textStyle(const TextStyle(
|
||||
color: AppColor.colorNameEmail,
|
||||
fontSize: 16,
|
||||
overflow: CommonTextStyle.defaultTextOverFlow))
|
||||
..addFocusNode(controller.nameInputFocusNode)
|
||||
..textDecoration((CreateMailboxNameInputDecorationBuilder()
|
||||
..setHintText(AppLocalizations.of(context).hint_input_create_new_mailbox)
|
||||
..setErrorText(controller.getErrorInputNameString(context)))
|
||||
.build()))
|
||||
.build())
|
||||
child: Obx(() => TextFieldBuilder(
|
||||
key: const Key('create_mailbox_name_input'),
|
||||
onTextChange: controller.setNewNameMailbox,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
cursorColor: AppColor.colorTextButton,
|
||||
controller: controller.nameInputController,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
maxLines: 1,
|
||||
textStyle: const TextStyle(
|
||||
color: AppColor.colorNameEmail,
|
||||
fontSize: 16,
|
||||
overflow: CommonTextStyle.defaultTextOverFlow),
|
||||
focusNode: controller.nameInputFocusNode,
|
||||
decoration: (CreateMailboxNameInputDecorationBuilder()
|
||||
..setHintText(AppLocalizations.of(context).hint_input_create_new_mailbox)
|
||||
..setErrorText(controller.getErrorInputNameString(context)))
|
||||
.build(),
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -356,13 +356,13 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
|
||||
nextFocusNode?.requestFocus();
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
child: TextFieldBuilder(
|
||||
controller: textEditingController,
|
||||
readOnly: isSelectFormList,
|
||||
mouseCursor: mouseCursor,
|
||||
textInputAction: isSelectFormList ? TextInputAction.done : TextInputAction.next,
|
||||
onTap: onTap,
|
||||
onSubmitted: (value) {
|
||||
onTextSubmitted: (value) {
|
||||
if (isSelectFormList) {
|
||||
onTap?.call();
|
||||
} else {
|
||||
|
||||
+14
-8
@@ -1,5 +1,11 @@
|
||||
import 'package:collection/collection.dart';
|
||||
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/responsive_utils.dart';
|
||||
import 'package:core/presentation/utils/style_utils.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/app_logger.dart';
|
||||
import 'package:core/utils/platform_info.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -66,8 +72,8 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
optionsViewBuilder: (context, onSelected, listEmailAddress) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(
|
||||
top: BuildUtils.isWeb ? 5 : 8,
|
||||
bottom: 16),
|
||||
top: PlatformInfo.isWeb ? 5 : 8,
|
||||
bottom: 16),
|
||||
height: _getHeightSuggestionBox(listEmailAddress.length, 65),
|
||||
width: maxWidthSuggestionBox,
|
||||
alignment: Alignment.topLeft,
|
||||
@@ -134,7 +140,7 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
widget.nextFocusNode?.requestFocus();
|
||||
}
|
||||
},
|
||||
child: TextField(
|
||||
child: TextFieldBuilder(
|
||||
controller: tec,
|
||||
focusNode: fn,
|
||||
textInputAction: TextInputAction.next,
|
||||
@@ -180,12 +186,12 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onChanged: (value) {
|
||||
onTextChange: (value) {
|
||||
if (value.trim().isNotEmpty) {
|
||||
onChanged?.call(value);
|
||||
}
|
||||
},
|
||||
onSubmitted: (tag) {
|
||||
onTextSubmitted: (tag) {
|
||||
if (tag.trim().isNotEmpty) {
|
||||
onSubmitted?.call(tag);
|
||||
fn.requestFocus();
|
||||
@@ -334,7 +340,7 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
double _getHeightSuggestionBox(int countItem, double heightItem) {
|
||||
final maxHeightList = countItem * heightItem;
|
||||
|
||||
if (BuildUtils.isWeb) {
|
||||
if (PlatformInfo.isWeb) {
|
||||
return maxHeightList > 250 ? 250 : maxHeightList;
|
||||
} else {
|
||||
if (_responsiveUtils.isLandscapeMobile(context)) {
|
||||
@@ -346,7 +352,7 @@ class _TextFieldAutoCompleteEmailAddressState
|
||||
}
|
||||
|
||||
double get maxWidthSuggestionBox {
|
||||
if (BuildUtils.isWeb) {
|
||||
if (PlatformInfo.isWeb) {
|
||||
if (_responsiveUtils.isTabletLarge(context)) {
|
||||
return 300;
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/style_utils.dart';
|
||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||
import 'package:core/presentation/views/quick_search/quick_search_input_form.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
@@ -186,6 +187,7 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
|
||||
controller: controller.searchInputController,
|
||||
focusNode: controller.searchFocus,
|
||||
textInputAction: TextInputAction.done,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
onSubmitted: (keyword) => _invokeSearchEmailAction(context, keyword.trim()),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -409,22 +410,21 @@ class VacationView extends GetWidget<VacationController> with RichTextButtonMixi
|
||||
}
|
||||
|
||||
Widget _buildMessagePlainTextEditor(BuildContext context) {
|
||||
return (TextFieldBuilder()
|
||||
..onChange((value) => controller.updateMessageBody(context, value))
|
||||
..keyboardType(TextInputType.multiline)
|
||||
..addController(controller.messageTextController)
|
||||
..textStyle(const TextStyle(color: Colors.black, fontSize: 16))
|
||||
..minLines(10)
|
||||
..maxLines(null)
|
||||
..textDecoration((TextInputDecorationBuilder()
|
||||
return TextFieldBuilder(
|
||||
onTextChange: (value) => controller.updateMessageBody(context, value),
|
||||
keyboardType: TextInputType.multiline,
|
||||
controller: controller.messageTextController,
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 16),
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
minLines: 10,
|
||||
maxLines: null,
|
||||
decoration: (TextInputDecorationBuilder()
|
||||
..setContentPadding(const EdgeInsets.all(16))
|
||||
..setHintText(AppLocalizations.of(context).hintMessageBodyVacation)
|
||||
..setFillColor(Colors.white)
|
||||
..setErrorText(controller.isVacationDeactivated
|
||||
? null
|
||||
: controller.errorMessageBody.value))
|
||||
.build()))
|
||||
.build();
|
||||
..setErrorText(controller.isVacationDeactivated ? null : controller.errorMessageBody.value))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageHtmlTextEditor(BuildContext context) {
|
||||
|
||||
+16
-16
@@ -1,6 +1,7 @@
|
||||
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/build_utils.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';
|
||||
|
||||
@@ -25,20 +26,19 @@ class RulesFilterInputField extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return (TextFieldBuilder()
|
||||
..onChange((value) => onChangeAction?.call(value))
|
||||
..textInputAction(TextInputAction.next)
|
||||
..addController(editingController ?? TextEditingController())
|
||||
..textStyle(const TextStyle(color: Colors.black, fontSize: 16))
|
||||
..keyboardType(TextInputType.text)
|
||||
..addFocusNode(focusNode)
|
||||
..textDecoration((RulesFilterInputDecorationBuilder()
|
||||
..setContentPadding(const EdgeInsets.symmetric(
|
||||
vertical: BuildUtils.isWeb ? 16 : 12,
|
||||
horizontal: 12))
|
||||
..setHintText(hintText)
|
||||
..setErrorText(errorText))
|
||||
.build()))
|
||||
.build();
|
||||
return TextFieldBuilder(
|
||||
onTextChange: onChangeAction,
|
||||
textInputAction: TextInputAction.next,
|
||||
controller: editingController,
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 16),
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
keyboardType: TextInputType.text,
|
||||
focusNode: focusNode,
|
||||
decoration: (RulesFilterInputDecorationBuilder()
|
||||
..setContentPadding(const EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 16 : 12, horizontal: 12))
|
||||
..setHintText(hintText)
|
||||
..setErrorText(errorText))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -642,6 +642,14 @@ class SearchEmailController extends BaseController
|
||||
_deBouncerTime.value = text;
|
||||
}
|
||||
|
||||
void onTextSearchSubmitted(BuildContext context, String text) {
|
||||
final query = text.trim();
|
||||
if (query.isNotEmpty) {
|
||||
saveRecentSearch(RecentSearch.now(query));
|
||||
submitSearchAction(context, query);
|
||||
}
|
||||
}
|
||||
|
||||
void setTextInputSearchForm(String value) {
|
||||
textInputSearchController.text = value;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:core/presentation/views/background/background_widget_builder.dar
|
||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/build_utils.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -117,29 +118,21 @@ class SearchEmailView extends GetWidget<SearchEmailController>
|
||||
tooltip: AppLocalizations.of(context).back,
|
||||
onTap: () => controller.closeSearchView(context)
|
||||
),
|
||||
Expanded(child: (TextFieldBuilder()
|
||||
..onChange(controller.onTextSearchChange)
|
||||
..textInputAction(TextInputAction.search)
|
||||
..addController(controller.textInputSearchController)
|
||||
..addFocusNode(controller.textInputSearchFocus)
|
||||
..textStyle(const TextStyle(color: Colors.black, fontSize: 16))
|
||||
..keyboardType(TextInputType.text)
|
||||
..onSubmitted((value) {
|
||||
final query = value.trim();
|
||||
if (query.isNotEmpty) {
|
||||
controller.saveRecentSearch(RecentSearch.now(query));
|
||||
controller.submitSearchAction(context, query);
|
||||
}
|
||||
})
|
||||
..maxLines(1)
|
||||
..textDecoration(InputDecoration(
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
hintText: AppLocalizations.of(context).search_emails,
|
||||
hintStyle: const TextStyle(
|
||||
color: AppColor.loginTextFieldHintColor,
|
||||
fontSize: 16),
|
||||
border: InputBorder.none)))
|
||||
.build()),
|
||||
Expanded(child: TextFieldBuilder(
|
||||
onTextChange: controller.onTextSearchChange,
|
||||
textInputAction: TextInputAction.search,
|
||||
controller: controller.textInputSearchController,
|
||||
focusNode: controller.textInputSearchFocus,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
textStyle: const TextStyle(color: Colors.black, fontSize: 16),
|
||||
keyboardType: TextInputType.text,
|
||||
onTextSubmitted: (text) => controller.onTextSearchSubmitted(context, text),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
hintText: AppLocalizations.of(context).search_emails,
|
||||
hintStyle: const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 16),
|
||||
border: InputBorder.none),
|
||||
)),
|
||||
Obx(() {
|
||||
if (controller.currentSearchText.isNotEmpty) {
|
||||
return
|
||||
|
||||
@@ -207,6 +207,13 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa
|
||||
_deBouncerTime.value = text;
|
||||
}
|
||||
|
||||
void onTextSearchSubmitted(BuildContext context, String text) {
|
||||
final query = text.trim();
|
||||
if (query.isNotEmpty) {
|
||||
submitSearchAction(context, query);
|
||||
}
|
||||
}
|
||||
|
||||
void setTextInputSearchForm(String value) {
|
||||
textInputSearchController.text = value;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/views/button/icon_button_web.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/build_utils.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:focused_menu_custom/modals.dart';
|
||||
@@ -138,33 +139,28 @@ class SearchMailboxView extends GetWidget<SearchMailboxController>
|
||||
}
|
||||
|
||||
Widget _buildTextFieldSearchInput(BuildContext context) {
|
||||
return (TextFieldBuilder()
|
||||
..onChange(controller.onTextSearchChange)
|
||||
..textInputAction(TextInputAction.search)
|
||||
..autoFocus(true)
|
||||
..addController(controller.textInputSearchController)
|
||||
..textStyle(const TextStyle(
|
||||
color: Colors.black,
|
||||
return TextFieldBuilder(
|
||||
onTextChange: controller.onTextSearchChange,
|
||||
textInputAction: TextInputAction.search,
|
||||
autoFocus: true,
|
||||
controller: controller.textInputSearchController,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
textStyle: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.normal),
|
||||
keyboardType: TextInputType.text,
|
||||
onTextSubmitted: (text) => controller.onTextSearchSubmitted(context, text),
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintText: AppLocalizations.of(context).searchForMailboxes,
|
||||
hintStyle: const TextStyle(
|
||||
color: AppColor.loginTextFieldHintColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.normal))
|
||||
..keyboardType(TextInputType.text)
|
||||
..onSubmitted((value) {
|
||||
final query = value.trim();
|
||||
if (query.isNotEmpty) {
|
||||
controller.submitSearchAction(context, query);
|
||||
}
|
||||
})
|
||||
..maxLines(1)
|
||||
..textDecoration(InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintText: AppLocalizations.of(context).searchForMailboxes,
|
||||
hintStyle: const TextStyle(
|
||||
color: AppColor.loginTextFieldHintColor,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.normal),
|
||||
border: InputBorder.none
|
||||
)
|
||||
)).build();
|
||||
fontWeight: FontWeight.normal),
|
||||
border: InputBorder.none
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMailboxListView(BuildContext context) {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
|
||||
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/views/button/icon_button_web.dart';
|
||||
import 'package:core/presentation/views/text/text_field_builder.dart';
|
||||
import 'package:core/utils/direction_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
|
||||
@@ -9,93 +13,61 @@ typedef OnClearTextSearchAction = Function();
|
||||
typedef OnTextChangeSearchAction = Function(String);
|
||||
typedef OnSearchTextAction = Function(String);
|
||||
|
||||
class SearchAppBarWidget {
|
||||
OnCancelSearchPressed? _onCancelSearchPressed;
|
||||
OnTextChangeSearchAction? _onTextChangeSearchAction;
|
||||
OnClearTextSearchAction? _onClearTextSearchAction;
|
||||
OnSearchTextAction? _onSearchTextAction;
|
||||
class SearchAppBarWidget extends StatelessWidget {
|
||||
|
||||
final ImagePaths _imagePaths;
|
||||
final SearchQuery? _searchQuery;
|
||||
final TextEditingController? _searchInputController;
|
||||
final FocusNode? _searchFocusNode;
|
||||
final ImagePaths imagePaths;
|
||||
final SearchQuery? searchQuery;
|
||||
final TextEditingController? searchInputController;
|
||||
final FocusNode? searchFocusNode;
|
||||
final List<String>? suggestionSearch;
|
||||
final bool hasBackButton;
|
||||
final bool hasSearchButton;
|
||||
final double? heightSearchBar;
|
||||
final Decoration? decoration;
|
||||
final EdgeInsets? padding;
|
||||
final EdgeInsets? margin;
|
||||
final String? hintText;
|
||||
final Widget? iconClearText;
|
||||
final OnCancelSearchPressed? onCancelSearchPressed;
|
||||
final OnTextChangeSearchAction? onTextChangeSearchAction;
|
||||
final OnClearTextSearchAction? onClearTextSearchAction;
|
||||
final OnSearchTextAction? onSearchTextAction;
|
||||
|
||||
double? _heightSearchBar;
|
||||
Decoration? _decoration;
|
||||
EdgeInsets? _padding;
|
||||
EdgeInsets? _margin;
|
||||
String? _hintText;
|
||||
Widget? _iconClearText;
|
||||
const SearchAppBarWidget({
|
||||
super.key,
|
||||
required this.imagePaths,
|
||||
required this.hasBackButton,
|
||||
required this.hasSearchButton,
|
||||
this.searchQuery,
|
||||
this.searchInputController,
|
||||
this.searchFocusNode,
|
||||
this.suggestionSearch,
|
||||
this.heightSearchBar,
|
||||
this.decoration,
|
||||
this.padding,
|
||||
this.margin,
|
||||
this.hintText,
|
||||
this.iconClearText,
|
||||
this.onCancelSearchPressed,
|
||||
this.onTextChangeSearchAction,
|
||||
this.onClearTextSearchAction,
|
||||
this.onSearchTextAction
|
||||
});
|
||||
|
||||
SearchAppBarWidget(
|
||||
this._imagePaths,
|
||||
this._searchQuery,
|
||||
this._searchFocusNode,
|
||||
this._searchInputController,
|
||||
{
|
||||
this.hasBackButton = true,
|
||||
this.suggestionSearch,
|
||||
this.hasSearchButton = false,
|
||||
}
|
||||
);
|
||||
|
||||
void addOnCancelSearchPressed(OnCancelSearchPressed onCancelSearchPressed) {
|
||||
_onCancelSearchPressed = onCancelSearchPressed;
|
||||
}
|
||||
|
||||
void addOnTextChangeSearchAction(OnTextChangeSearchAction onTextChangeSearchAction) {
|
||||
_onTextChangeSearchAction = onTextChangeSearchAction;
|
||||
}
|
||||
|
||||
void addOnClearTextSearchAction(OnClearTextSearchAction onClearTextSearchAction) {
|
||||
_onClearTextSearchAction = onClearTextSearchAction;
|
||||
}
|
||||
|
||||
void addOnSearchTextAction(OnSearchTextAction onSearchTextAction) {
|
||||
_onSearchTextAction = onSearchTextAction;
|
||||
}
|
||||
|
||||
void setHeightSearchBar(double heightSearchBar) {
|
||||
_heightSearchBar = heightSearchBar;
|
||||
}
|
||||
|
||||
void addDecoration(Decoration decoration) {
|
||||
_decoration = decoration;
|
||||
}
|
||||
|
||||
void addPadding(EdgeInsets padding) {
|
||||
_padding = padding;
|
||||
}
|
||||
|
||||
void setMargin(EdgeInsets margin) {
|
||||
_margin = margin;
|
||||
}
|
||||
|
||||
void setHintText(String text) {
|
||||
_hintText = text;
|
||||
}
|
||||
|
||||
void addIconClearText(Widget? icon) {
|
||||
_iconClearText = icon;
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
key: const Key('search_app_bar_widget'),
|
||||
height: _heightSearchBar,
|
||||
decoration: _decoration,
|
||||
padding: _padding ?? EdgeInsets.zero,
|
||||
margin: _margin,
|
||||
key: key ?? const Key('search_app_bar_widget'),
|
||||
height: heightSearchBar,
|
||||
decoration: decoration,
|
||||
padding: padding ?? EdgeInsets.zero,
|
||||
margin: margin,
|
||||
child: Row(
|
||||
children: [
|
||||
if (hasBackButton) _buildBackButton(),
|
||||
if (hasSearchButton) _buildSearchButton(),
|
||||
Expanded(child: _buildSearchInputForm()),
|
||||
if (suggestionSearch?.isNotEmpty == true
|
||||
|| (_searchQuery != null && _searchQuery!.value.isNotEmpty))
|
||||
Expanded(child: _buildSearchInputForm(context)),
|
||||
if (suggestionSearch?.isNotEmpty == true || (searchQuery != null && searchQuery!.value.isNotEmpty))
|
||||
_buildClearTextSearchButton(),
|
||||
]
|
||||
)
|
||||
@@ -104,47 +76,48 @@ class SearchAppBarWidget {
|
||||
|
||||
Widget _buildBackButton() {
|
||||
return buildIconWeb(
|
||||
icon: SvgPicture.asset(
|
||||
_imagePaths.icBack,
|
||||
colorFilter: AppColor.colorTextButton.asFilter(),
|
||||
fit: BoxFit.fill),
|
||||
onTap: () {
|
||||
_searchInputController?.clear();
|
||||
if (_onCancelSearchPressed != null) {
|
||||
_onCancelSearchPressed!();
|
||||
}
|
||||
});
|
||||
icon: SvgPicture.asset(
|
||||
imagePaths.icBack,
|
||||
colorFilter: AppColor.colorTextButton.asFilter(),
|
||||
fit: BoxFit.fill),
|
||||
onTap: () {
|
||||
searchInputController?.clear();
|
||||
if (onCancelSearchPressed != null) {
|
||||
onCancelSearchPressed!();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildClearTextSearchButton() {
|
||||
return buildIconWeb(
|
||||
icon: _iconClearText ?? SvgPicture.asset(_imagePaths.icComposerClose, width: 18, height: 18, fit: BoxFit.fill),
|
||||
onTap: () {
|
||||
_searchInputController?.clear();
|
||||
_onClearTextSearchAction?.call();
|
||||
});
|
||||
icon: iconClearText ?? SvgPicture.asset(imagePaths.icComposerClose, width: 18, height: 18, fit: BoxFit.fill),
|
||||
onTap: () {
|
||||
searchInputController?.clear();
|
||||
onClearTextSearchAction?.call();
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSearchInputForm() {
|
||||
return (TextFieldBuilder()
|
||||
..key(const Key('search_input_form'))
|
||||
..textInputAction(TextInputAction.done)
|
||||
..onChange((value) => _onTextChangeSearchAction?.call(value))
|
||||
..onSubmitted((value) => _onSearchTextAction?.call(value))
|
||||
..cursorColor(AppColor.colorTextButton)
|
||||
..autoFocus(true)
|
||||
..addFocusNode(_searchFocusNode)
|
||||
..textStyle(const TextStyle(color: AppColor.colorNameEmail, fontSize: 17))
|
||||
..textDecoration(InputDecoration(
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintText: _hintText,
|
||||
hintStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0),
|
||||
labelStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0)))
|
||||
..addController(_searchInputController))
|
||||
.build();
|
||||
Widget _buildSearchInputForm(BuildContext context) {
|
||||
return TextFieldBuilder(
|
||||
key: const Key('search_input_form'),
|
||||
textInputAction: TextInputAction.done,
|
||||
onTextChange: onTextChangeSearchAction,
|
||||
onTextSubmitted: onSearchTextAction,
|
||||
cursorColor: AppColor.colorTextButton,
|
||||
textDirection: DirectionUtils.getDirectionByLanguage(context),
|
||||
autoFocus: true,
|
||||
focusNode: searchFocusNode,
|
||||
textStyle: const TextStyle(color: AppColor.colorNameEmail, fontSize: 17),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintText: hintText,
|
||||
hintStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0),
|
||||
labelStyle: const TextStyle(color: AppColor.colorHintSearchBar, fontSize: 17.0)),
|
||||
controller: searchInputController,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSearchButton() {
|
||||
@@ -154,10 +127,10 @@ class SearchAppBarWidget {
|
||||
minSize: 40,
|
||||
iconPadding: EdgeInsets.zero,
|
||||
icon: SvgPicture.asset(
|
||||
_imagePaths.icSearchBar,
|
||||
imagePaths.icSearchBar,
|
||||
fit: BoxFit.fill
|
||||
),
|
||||
onTap: () => _onSearchTextAction?.call(_searchInputController?.text ?? '')
|
||||
onTap: () => onSearchTextAction?.call(searchInputController?.text ?? '')
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user