From 47c85b3149c39681d4b98f347a5bf61953f3cfdc Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sat, 20 Dec 2025 21:26:35 +0200 Subject: [PATCH] [FIX] Action field longer padding. --- lib/src/Fields/src/base.dart | 28 +- lib/src/Fields/src/dropdown_field.dart | 152 +++++++ lib/src/Fields/src/dropdown_field_base.dart | 453 ++++++++++++++++++++ 3 files changed, 632 insertions(+), 1 deletion(-) create mode 100644 lib/src/Fields/src/dropdown_field.dart create mode 100644 lib/src/Fields/src/dropdown_field_base.dart diff --git a/lib/src/Fields/src/base.dart b/lib/src/Fields/src/base.dart index c308a25..910bb1c 100644 --- a/lib/src/Fields/src/base.dart +++ b/lib/src/Fields/src/base.dart @@ -422,7 +422,33 @@ class _AstromicFieldBaseState extends State { ); }, ), - widget.fieldStackedWidget != null ? widget.fieldStackedWidget!(_finalVerticalPadding(_style(), forceRespectBorder: true).vertical * 2, _finalBorderShape(_style())) : Container(), + widget.fieldStackedWidget != null + ? Builder(builder: (c) { + // Calculate the actual height of the TextFormField including padding and border + double contentHeight = _getTextHeight( + _textController.text.isNotEmpty + ? _textController.text + : (widget.hint?.isNotEmpty ?? false) + ? widget.hint! + : '', + _style().textStyle ?? Theme.of(c).textTheme.titleMedium!, + _finalTextDirection(), + ); + double verticalPadding = _finalVerticalPadding(_style()).vertical * 2; + double borderWidth = _style().borderWidth ?? 0; // Default to 0 if null + + double totalFieldHeight; + if (_config.isFixedHeight && !_config.isTextArea) { + totalFieldHeight = _style().fixedHeight ?? contentHeight + verticalPadding + (borderWidth * 2); + } else if (_config.isFixedHeight && _config.isTextArea) { + totalFieldHeight = _style().fixedHeight ?? contentHeight + verticalPadding + (borderWidth * 2); + } else { + totalFieldHeight = contentHeight + verticalPadding + (borderWidth * 2); + } + + return widget.fieldStackedWidget!(totalFieldHeight, _finalBorderShape(_style())); + }) + : Container(), ], ), ), diff --git a/lib/src/Fields/src/dropdown_field.dart b/lib/src/Fields/src/dropdown_field.dart new file mode 100644 index 0000000..4492e2e --- /dev/null +++ b/lib/src/Fields/src/dropdown_field.dart @@ -0,0 +1,152 @@ +// //s1 Imports +// //s2 Core Package Imports +// import 'package:flutter/material.dart'; +// //s2 1st-party Package Imports +// //s2 3rd-party Package Imports +// //s2 Dependancies Imports +// //s3 Routes +// //s3 Services +// //s3 Models +// import 'base.dart'; +// import 'dropdown_field_base.dart'; +// import 'models/models.exports.dart'; +// //s1 Exports + +// class AstromicDropdownField extends StatefulWidget { +// //!SECTION +// // +// const AstromicDropdownField({ +// super.key, +// // +// this.stateKey, +// required this.textController, +// required this.items, +// this.initialValue, +// this.onChanged, +// // +// this.configuration, +// this.style, +// // +// this.hint, +// this.prefixWidget, +// this.suffixWidget, +// this.messageBuilder, +// }); +// //SECTION - Widget Arguments +// //s1 -- Functionality +// final Key? stateKey; +// final TextEditingController textController; +// final (T item, String label)? initialValue; +// final List<(T item, String label)> items; +// final void Function((T item, String label) newValue)? onChanged; +// // +// //s1 -- Configurations +// final AstromicFieldConfiguration? configuration; +// // +// //s1 -- Style +// final AstromicFieldStyle Function(bool isEnabled)? style; +// // +// //s1 -- Content +// final String? hint; +// // +// final Widget? Function(bool isEnabled, VoidCallback stateSetter)? prefixWidget; +// final Widget? Function(bool isEnabled, VoidCallback stateSetter)? suffixWidget; +// final Widget? Function(bool isEnabled)? messageBuilder; + +// @override +// State> createState() => _AstromicDropdownFieldState(); +// } + +// class _AstromicDropdownFieldState extends State> { +// // +// //SECTION - State Variables +// //s1 --State +// (T item, String label)? _currentValue; +// //s1 --State +// // +// //s1 --Controllers +// late TextEditingController _textController; +// //s1 --Controllers +// // +// //s1 --Constants +// //s1 --Constants +// //!SECTION + +// @override +// void initState() { +// super.initState(); +// // +// //SECTION - State Variables initializations & Listeners +// //s1 --State +// //s1 --State +// // +// //s1 --Controllers & Listeners +// _currentValue = widget.initialValue; +// _textController = widget.textController; +// _textController.text = _currentValue?.$2 ?? ''; +// //s1 --Controllers & Listeners +// // +// //s1 --Late & Async Initializers +// //s1 --Late & Async Initializers +// //!SECTION +// } + +// @override +// void didChangeDependencies() { +// super.didChangeDependencies(); +// // +// //SECTION - State Variables initializations & Listeners +// //s1 --State +// //s1 --State +// // +// //s1 --Controllers & Listeners +// //s1 --Controllers & Listeners +// // +// //!SECTION +// } + +// //SECTION - Dumb Widgets +// //!SECTION + +// //SECTION - Stateless functions +// //!SECTION + +// //SECTION - Action Callbacks +// //!SECTION + +// @override +// Widget build(BuildContext context) { +// //SECTION - Build Setup +// //s1 --Values +// //double w = MediaQuery.of(context).size.width; +// //double h = MediaQuery.of(context).size.height; +// //s1 --Values +// // +// //s1 --Contexted Widgets +// //s1 --Contexted Widgets +// //!SECTION + +// //SECTION - Build Return + +// return AstromicDropdownFieldBase( +// key: widget.key, +// textController: _textController, +// stateKey: widget.stateKey, +// onChanged: (v) => widget.onChanged?.call(v), +// configuration: widget.configuration, +// style: widget.style == null ? null : (bool enabled, bool focused) => widget.style!(enabled), +// hint: widget.hint, +// prefixWidget: widget.prefixWidget == null ? null : (bool enabled, bool focused, void Function() stateSetter) => widget.prefixWidget!(enabled, stateSetter), +// suffixWidget: widget.suffixWidget == null ? null : (bool enabled, bool focused, void Function() stateSetter) => widget.suffixWidget!(enabled, stateSetter), +// messageBuilder: widget.messageBuilder == null ? null : (bool enabled, bool focused) => widget.messageBuilder!(enabled), +// ); +// //!SECTION +// } + +// @override +// void dispose() { +// //SECTION - Disposable variables +// //!SECTION +// super.dispose(); +// } +// } diff --git a/lib/src/Fields/src/dropdown_field_base.dart b/lib/src/Fields/src/dropdown_field_base.dart new file mode 100644 index 0000000..b58e34d --- /dev/null +++ b/lib/src/Fields/src/dropdown_field_base.dart @@ -0,0 +1,453 @@ +// //s1 Imports +// //s2 Core Package Imports +// import 'package:flutter/material.dart'; +// import 'package:intl/intl.dart' as intl; +// import 'dart:ui' as ui; +// //s2 1st-party Package Imports +// import 'package:astromic_extensions/astromic_extensions.dart'; +// //s2 3rd-party Package Imports +// //s2 Dependancies Imports +// //s3 Routes +// //s3 Services +// //s3 Models +// import '../../../dependencies/gradient_border/gradient_borders.dart'; +// import '../../../dependencies/gradient_border/input_borders/gradient_underline_input_border.dart'; +// import 'enums/enums.exports.dart'; +// import 'models/models.exports.dart'; +// //s1 Exports + +// class AstromicDropdownFieldBase extends StatefulWidget { +// //!SECTION +// // +// const AstromicDropdownFieldBase({ +// super.key, +// this.stateKey, +// // +// required this.textController, +// this.onChanged, +// // +// this.configuration, +// // +// this.style, +// // +// this.hint, +// this.prefixWidget, +// this.suffixWidget, +// this.messageBuilder, +// this.contextButtons, +// }); +// //SECTION - Widget Arguments +// //s1 -- Functionality +// final TextEditingController textController; +// final Key? stateKey; +// // +// final void Function(String v)? onChanged; +// // +// //s1 -- Configurations +// final AstromicFieldConfiguration? configuration; +// // +// //s1 -- Style +// final AstromicFieldStyle Function(bool isEnabled, bool isFocused)? style; +// // +// //s1 -- Content +// final String? hint; +// // +// final Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter)? prefixWidget; +// final Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter)? suffixWidget; +// final Widget? Function(bool isEnabled, bool isFocused)? messageBuilder; +// // +// final Iterable? contextButtons; + +// @override +// State createState() => _AstromicDropdownFieldBaseState(); +// } + +// class _AstromicDropdownFieldBaseState extends State { +// // +// //SECTION - State Variables +// //s1 --Controllers +// late TextEditingController _textController; +// //s1 --Controllers +// // +// //s1 --State +// // late bool widget.configuration.isEnabled; +// final FocusNode _focusNode = FocusNode(); +// late bool _isFocused; +// // +// late AstromicFieldStyle Function([bool isFocused]) _style; +// late AstromicFieldConfiguration _config; +// // +// //s1 --State +// // +// //s1 --Constants +// //s1 --Constants +// //!SECTION + +// @override +// void initState() { +// super.initState(); +// // +// //SECTION - State Variables initializations & Listeners +// //s1 --Controllers & Listeners +// _textController = widget.textController; +// //s1 --Controllers & Listeners +// // +// //s1 --State +// // widget.configuration.isEnabled = widget.configuration.isEnabled; +// _isFocused = false; +// //s1 --State +// // +// //s1 --Late & Async Initializers +// //s1 --Late & Async Initializers +// //!SECTION +// } + +// @override +// void didChangeDependencies() { +// super.didChangeDependencies(); +// // +// //SECTION - State Variables initializations & Listeners +// //s1 --State +// _focusNode.addListener(() { +// if (mounted) { +// setState(() { +// _isFocused = _focusNode.hasFocus; +// }); +// } +// }); +// //s1 --State +// //!SECTION +// } + +// //SECTION - Dumb Widgets +// //!SECTION + +// //SECTION - Stateless functions +// _getTextHeight(String text, TextStyle style, ui.TextDirection direction) { +// final TextSpan span = TextSpan(text: text, style: style); +// final TextPainter tp = TextPainter(text: span, textDirection: direction); +// tp.layout(); +// return tp.height; +// } + +// ui.TextDirection _finalTextDirection() { +// ui.TextDirection fromLocale = intl.Bidi.isRtlLanguage(Localizations.localeOf(context).languageCode) ? ui.TextDirection.rtl : ui.TextDirection.ltr; +// // +// if (_config.textDirection != null) { +// // Get form Style +// return _config.textDirection!; +// } else { +// // Detect form Text +// return _textController.text != '' +// ? _config.withAutomaticDirectionalitySwitching +// ? intl.Bidi.startsWithLtr(_textController.text) +// ? ui.TextDirection.ltr +// : ui.TextDirection.rtl +// : fromLocale +// : fromLocale; +// } +// } + +// EdgeInsetsGeometry _finalVerticalPadding( +// AstromicFieldStyle style, { +// bool? forceRespectBorder, +// }) { +// double desiredFixedHeight = style.fixedHeight ?? 0; +// // +// double providedPadding = style.contentPadding.vertical; +// // +// double borderOffset = (forceRespectBorder ?? _config.respectBorderWidthPadding) ? (_finalBorderShape(style)?.borderSide.strokeInset ?? 0.0) - (style.borderWidth ?? 0) : 0.0; +// // +// double fontOffset = _getTextHeight( +// widget.textController.text.isNotEmpty +// ? widget.textController.text +// : (widget.hint?.isNotEmpty ?? false) +// ? widget.hint! +// : '', +// widget.textController.text.isNotEmpty && _style().textStyle != null +// ? _style().textStyle! +// : _style().hintStyle != null +// ? _style().hintStyle! +// : const TextStyle(), +// _finalTextDirection(), +// ); +// // +// double finalVertical = +// //- +// _config.isFixedHeight +// ? +// //- +// _config.isTextArea +// ? +// //s1 Fixed Height with Text Area +// borderOffset + providedPadding +// : +// //s1 Fixed Height and not Text Area +// desiredFixedHeight - (fontOffset + borderOffset) +// //s1 - Not Fixed Height +// : providedPadding + borderOffset; +// // +// return EdgeInsets.symmetric( +// vertical: finalVertical / 2, +// ); +// } + +// InputBorder? _finalBorderShape(AstromicFieldStyle style) { +// InputBorder defaultBorder = const UnderlineInputBorder(borderSide: BorderSide(color: Colors.grey, width: 2)); + +// switch (style.borderType) { +// case AstromicFieldBorderType.cusotm: +// return style.customBorder ?? defaultBorder; +// case AstromicFieldBorderType.outlined: +// return style.borderGradient != null +// ? GradientOutlineInputBorder( +// gradient: style.borderGradient!, +// gapPadding: 0, +// width: style.borderWidth ?? 2.0, +// borderRadius: style.borderRadius?.resolve( +// _config.textDirection, +// ) ?? +// BorderRadius.circular(0)) +// : OutlineInputBorder( +// borderSide: BorderSide(color: style.borderColor ?? Colors.grey, width: style.borderWidth ?? 2.0), +// borderRadius: style.borderRadius?.resolve( +// _config.textDirection, +// ) ?? +// BorderRadius.circular(0)); +// case AstromicFieldBorderType.underlined: +// return style.borderGradient != null +// ? GradientUnderlineInputBorder( +// gradient: style.borderGradient!, +// gapPadding: 0, +// width: style.borderWidth ?? 2.0, +// borderRadius: style.borderRadius?.resolve( +// _config.textDirection, +// ) ?? +// BorderRadius.circular(0)) +// : UnderlineInputBorder( +// borderSide: BorderSide(color: style.borderColor ?? Colors.grey, width: style.borderWidth ?? 2.0), +// borderRadius: style.borderRadius?.resolve( +// _config.textDirection, +// ) ?? +// BorderRadius.circular(0)); +// } +// } +// //!SECTION + +// //SECTION - Action Callbacks +// //!SECTION + +// @override +// Widget build(BuildContext context) { +// //SECTION - Build Setup +// //s1 --Values +// //double w = MediaQuery.of(context).size.width; +// //double h = MediaQuery.of(context).size.height; +// _config = widget.configuration ?? const AstromicFieldConfiguration(); +// _style = ([bool? isFocused]) => widget.style?.call(_config.isEnabled, isFocused ?? _isFocused) ?? const AstromicFieldStyle(); +// //s1 --Values +// // +// //s1 --Contexted Widgets +// InputDecorationTheme inputDecoration = InputDecorationTheme( +// //s1 -- Functionality +// // enabled: _config.isEnabled, +// //s1 -- Configurations +// isDense: true, +// // hintTextDirection: _config.hintDirection ?? _finalTextDirection(), +// //s1 -- Style +// filled: _style().isFilled, +// fillColor: _style().fillColor, +// // +// hintStyle: _style().hintStyle, +// // +// enabledBorder: _finalBorderShape(_style(false)), +// focusedBorder: _finalBorderShape(_style(true)), +// disabledBorder: _finalBorderShape(_style()), +// errorBorder: _finalBorderShape(_style(false)), +// focusedErrorBorder: _finalBorderShape(_style(true)), +// // +// errorStyle: const TextStyle(height: 0.001, color: Colors.transparent), +// contentPadding: _finalVerticalPadding(_style()), +// //s1 -- Content +// // hintText: widget.hint, +// // +// prefixIcon: widget.prefixWidget != null && +// widget.prefixWidget!(_config.isEnabled, _isFocused, () { +// setState(() {}); +// }) != +// null +// ? Container( +// margin: EdgeInsetsDirectional.fromSTEB(_style().prefixSpacing, 0, _style().contentPadding.resolveToDirectional(_finalTextDirection()).start, 0), +// child: widget.prefixWidget!(_config.isEnabled, _isFocused, () { +// setState(() {}); +// }), +// ) +// : SizedBox(width: _style().contentPadding.resolveToDirectional(_finalTextDirection()).start), +// // +// prefixIconConstraints: widget.prefixWidget != null && +// widget.prefixWidget!(_config.isEnabled, _isFocused, () { +// setState(() {}); +// }) != +// null +// ? _style().prefixSize != 0 +// ? BoxConstraints.expand( +// width: _style().prefixSize + _style().prefixSpacing + _style().contentPadding.resolveToDirectional(_finalTextDirection()).start, +// height: _style().prefixSize, +// ) +// : const BoxConstraints.tightForFinite() +// : BoxConstraints.tightForFinite(width: _style().contentPadding.resolveToDirectional(_finalTextDirection()).start), +// // +// suffixIcon: widget.suffixWidget != null && +// widget.suffixWidget!(_config.isEnabled, _isFocused, () { +// setState(() {}); +// }) != +// null +// ? Container( +// margin: EdgeInsetsDirectional.fromSTEB(_style().contentPadding.resolveToDirectional(_finalTextDirection()).end, 0, _style().suffixSpacing, 0), +// child: widget.suffixWidget!(_config.isEnabled, _isFocused, () { +// setState(() {}); +// }), +// ) +// : SizedBox(width: _style().contentPadding.resolveToDirectional(_finalTextDirection()).end), +// suffixIconConstraints: widget.suffixWidget != null && +// widget.suffixWidget != null && +// widget.suffixWidget!(_config.isEnabled, _isFocused, () { +// setState(() {}); +// }) != +// null +// ? _style().suffixSize != 0 +// ? BoxConstraints.expand( +// width: _style().suffixSize + _style().suffixSpacing + _style().contentPadding.resolveToDirectional(_finalTextDirection()).end, +// height: _style().suffixSize, +// ) +// : const BoxConstraints.tightForFinite() +// : BoxConstraints.tightForFinite(width: _style().contentPadding.resolveToDirectional(_finalTextDirection()).end), +// ); +// //s1 --Contexted Widgets +// //!SECTION +// // +// //SECTION - Build Return +// return Column( +// mainAxisSize: MainAxisSize.min, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Directionality( +// textDirection: _finalTextDirection(), +// child: Container( +// height: _config.isFixedHeight +// ? !_config.isTextArea +// ? null +// : _style().fixedHeight +// : null, +// alignment: _style().textAlignVertical.toAlignment(), +// child: Stack( +// children: [ +// DropdownMenuFormField( +// //s1 -- Functionality +// key: widget.stateKey, +// dropdownMenuEntries: [DropdownMenuEntry(value: value, label: label)], +// controller: _textController, +// focusNode: _focusNode, +// textInputAction: _config.inputAction, +// // +// onSelected: (String v) { +// setState(() { +// if (widget.onChanged != null) { +// widget.onChanged!(v); +// } +// }); +// }, +// // +// // onFieldSubmitted: (String v) { +// // // +// // if (_config.inputAction == TextInputAction.next) { +// // _focusNode.nextFocus(); +// // } +// // if (widget.onSubmited != null) { +// // widget.onSubmited!(v); +// // } +// // }, +// // +// // onTapOutside: (PointerDownEvent tapEvent) { +// // if (_config.unfocusOnTapOutside) { +// // _focusNode.unfocus(); +// // } +// // }, +// // +// //s1 -- Configurations +// hintText: widget.hint, + +// // +// // autofillHints: _config.autofillHints, +// autovalidateMode: _config.validatingMode ?? AutovalidateMode.disabled, +// // autofocus: _config.withAutofocus, +// keyboardType: _config.inputType, +// // textDirection: _finalTextDirection(), +// // obscureText: _config.withObscurity ? (_config.isTextObscured ?? false) : false, +// inputFormatters: _config.inputFormatters, +// // +// // minLines: _config.isFixedHeight && _config.isTextArea ? null : _style().minLines, +// maxLines: (_config.isFixedHeight +// ? _config.isTextArea +// ? null +// : _style().maxLines +// : _style().maxLines) ?? +// 1, + +// // +// // maxLength: _style().maxLength, +// // maxLengthEnforcement: _config.maxLengthEnforcement, +// // +// // expands: _config.isFixedHeight && _config.isTextArea ? true : false, +// // - Validation +// validator: _config.validator != null ? (String? s) => _config.validator!(_config.isEnabled, _isFocused, s) : null, +// // - Validation +// //s1 -- Style +// textStyle: _style().textStyle, +// // cursorColor: _style().cursorColor, +// // cursorErrorColor: _style().cursorColor, +// textAlign: _style().textAlign, +// // textAlignVertical: _style().textAlignVertical, +// // +// //s1 -- Input Decoration +// inputDecorationTheme: inputDecoration, +// //s1 -- Content +// // contextMenuBuilder: (_, EditableTextState state) { +// // List baseContextButtons = state.contextMenuButtonItems; +// // if (widget.contextButtons != null) { +// // baseContextButtons.addAll(widget.contextButtons!); +// // } +// // return AdaptiveTextSelectionToolbar.buttonItems( +// // buttonItems: baseContextButtons, +// // anchors: state.contextMenuAnchors, +// // ); +// // }, +// ), +// widget.fieldStackedWidget != null ? widget.fieldStackedWidget!(_finalVerticalPadding(_style(), forceRespectBorder: true).vertical * 2, _finalBorderShape(_style())) : Container(), +// ], +// ), +// ), +// ), +// widget.messageBuilder != null && +// widget.messageBuilder!( +// _config.isEnabled, +// _isFocused, +// ) != +// null +// ? Padding( +// padding: _style().messagePadding, +// child: widget.messageBuilder!(_config.isEnabled, _isFocused) ?? Container(), +// ) +// : Container() +// ], +// ); +// //!SECTION +// } + +// @override +// void dispose() { +// //SECTION - Disposable variables +// //!SECTION +// super.dispose(); +// } +// }