[DEV] Done with the action field.

This commit is contained in:
2024-06-18 12:16:29 +03:00
parent b12b2cd3c2
commit 4b330cb8d1
2 changed files with 470 additions and 1 deletions

View File

@@ -1,6 +1,7 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
// //
import 'src/action_field.dart';
import 'src/text_field.dart'; import 'src/text_field.dart';
import 'src/configuration.dart'; import 'src/configuration.dart';
import 'src/style.dart'; import 'src/style.dart';
@@ -27,7 +28,6 @@ class AstromicFields {
// //
required AstromicFieldStyle Function(bool isEnabled, bool isFocused) style, required AstromicFieldStyle Function(bool isEnabled, bool isFocused) style,
// //
//s1 -- Content
String? hint, String? hint,
// //
Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter)? prefixWidget, Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter)? prefixWidget,
@@ -57,4 +57,49 @@ class AstromicFields {
), ),
], ],
); );
//S1 -- Action Field
static Widget action<T>(
{Key? stateKey,
(T item, String label)? initialValue,
required TextEditingController controller,
//
Future<(T item, String label)?> Function((T item, String label)? currentValue)? onTap,
Future<(T item, String label)?> Function((T item, String label)? currentValue)? onHold,
required String Function(String? oldValue, String newValue) textFieldMapper,
//
required AstromicFieldConfiguration configuration,
//
AutovalidateMode? validatingMode,
String? Function(bool isEnabled, String? text)? validator,
List<TextInputFormatter>? inputFormatters,
//
required AstromicFieldStyle Function(bool isEnabled) style,
//
String? hint,
//
Widget? Function(bool isEnabled, VoidCallback stateSetter)? prefixWidget,
Widget? Function(bool isEnabled, VoidCallback stateSetter)? suffixWidget,
Widget? Function(bool isEnabled)? messageBuilder}) =>
Column(
children: [
AstromicActionField(
stateKey: stateKey,
textController: controller,
initialValue: initialValue,
onTap: onTap,
onHold: onHold,
textFieldMapper: textFieldMapper,
configuration: configuration,
validatingMode: validatingMode,
validator: validator,
inputFormatters: inputFormatters,
style: style,
hint: hint,
prefixWidget: prefixWidget,
suffixWidget: suffixWidget,
messageBuilder: messageBuilder,
),
],
);
} }

View File

@@ -0,0 +1,424 @@
// ignore_for_file: depend_on_referenced_packages
//SECTION - Imports
//
//s1 PACKAGES
//---------------
//s2 CORE
import 'package:astromic_mobile_elements/Infrastructure/insets_extension.dart';
import 'package:astromic_mobile_elements/Infrastructure/misc_extensions.dart';
import 'package:astromic_mobile_elements/astromic_mobile_elements.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart' as intl;
import 'dart:ui' as ui;
//s2 3RD-PARTY
//
//s1 DEPENDENCIES
//---------------
//s2 SERVICES
//s2 MODELS
//s2 MISC
//!SECTION - Imports
//
//SECTION - Exports
//!SECTION - Exports
//
class AstromicActionField<T> extends StatefulWidget {
//SECTION - Widget Arguments
//s1 -- Functionality
final Key? stateKey;
final (T item, String label)? initialValue;
final TextEditingController textController;
final Future<(T item, String label)?> Function((T item, String label)? currentValue)? onTap;
final Future<(T item, String label)?> Function((T item, String label)? currentValue)? onHold;
final String Function(String? oldValue, String newValue) textFieldMapper;
//
//s1 -- Configurations
final AstromicFieldConfiguration configuration;
//
final AutovalidateMode? validatingMode;
final String? Function(bool isEnabled, String? text)? validator;
final List<TextInputFormatter>? inputFormatters;
final bool? obscureText;
//
//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;
//!SECTION
//
const AstromicActionField({
super.key,
//
this.initialValue,
this.stateKey,
required this.textController,
required this.textFieldMapper,
this.onTap,
this.onHold,
//
required this.configuration,
//
this.validatingMode,
this.validator,
this.inputFormatters,
this.obscureText,
//
required this.style,
//
this.hint,
this.prefixWidget,
this.suffixWidget,
this.messageBuilder,
});
@override
State<AstromicActionField<T>> createState() => _AstromicActionFieldState<T>();
}
class _AstromicActionFieldState<T> extends State<AstromicActionField<T>> {
//
//SECTION - State Variables
//s1 --Controllers
late TextEditingController _textController;
//s1 --Controllers
//
//s1 --State
(T item, String label)? _currentValue;
//
//s1 --State
//
//s1 --Constants
//s1 --Constants
//!SECTION
@override
void initState() {
super.initState();
//
//SECTION - State Variables initializations & Listeners
//s1 --Controllers & Listeners
_currentValue = widget.initialValue;
_textController = widget.textController;
_textController.text = widget.initialValue?.$2 ?? '';
//s1 --Controllers & Listeners
//
//s1 --State
//s1 --State
//
//s1 --Late & Async Initializers
//s1 --Late & Async Initializers
//!SECTION
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//
//SECTION - State Variables initializations & Listeners
//s1 --State
//s1 --State
//!SECTION
}
//SECTION - Dumb Widgets
//!SECTION
//SECTION - Stateless functions
getTextHeight(String text, TextStyle style, ui.TextDirection direction) {
final span = TextSpan(text: text, style: style);
final tp = TextPainter(text: span, textDirection: direction);
tp.layout(maxWidth: double.infinity);
return tp.height;
}
//-
//----------------------------------------------------------------
//-
ui.TextDirection finalTextDirection() {
ui.TextDirection fromLocale = intl.Bidi.isRtlLanguage(Localizations.localeOf(context).languageCode) ? ui.TextDirection.rtl : ui.TextDirection.ltr;
//
if (widget.configuration.textDirection != null) {
// Get form Style
return widget.configuration.textDirection!;
} else {
// Detect form Text
return _textController.text != ''
? widget.configuration.withAutomaticDirectionalitySwitching
? intl.Bidi.startsWithLtr(_textController.text)
? ui.TextDirection.ltr
: ui.TextDirection.rtl
: fromLocale
: fromLocale;
}
}
//-
//----------------------------------------------------------------
//-
EdgeInsetsGeometry finalVerticalPadding(
AstromicFieldStyle style, {
bool forceRespectBorder = false,
}) {
double desiredFixedHeight = style.fixedHeight ?? 0;
//
double providedPadding = style.contentPadding.vertical;
//
double borderOffset = widget.configuration.respectBorderWidthPadding || forceRespectBorder ? style.border?.borderSide.strokeInset ?? 0.0 : 0.0;
//
double fontOffset = getTextHeight(
widget.textController.text.isNotEmpty
? widget.textController.text
: (widget.hint?.isNotEmpty ?? false)
? widget.hint!
: "",
widget.textController.text.isNotEmpty && widget.style(widget.configuration.isEnabled).textStyle != null
? widget.style(widget.configuration.isEnabled).textStyle!
: widget.style(widget.configuration.isEnabled).hintStyle != null
? widget.style(widget.configuration.isEnabled).hintStyle!
: const TextStyle(),
finalTextDirection(),
);
//
double finalVertical =
//-
widget.configuration.isFixedHeight
?
//-
widget.configuration.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 / 2);
//
return EdgeInsets.symmetric(
vertical: finalVertical / 2,
);
}
//!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
InputDecoration inputDecoration = InputDecoration(
//s1 -- Functionality
enabled: widget.configuration.isEnabled,
//s1 -- Configurations
isDense: true,
hintTextDirection: widget.configuration.hintDirection ?? finalTextDirection(),
//s1 -- Style
filled: widget.style(widget.configuration.isEnabled).isFilled,
fillColor: widget.style(widget.configuration.isEnabled).fillColor,
//
hintStyle: widget.style(widget.configuration.isEnabled).hintStyle,
//
enabledBorder: widget.style(widget.configuration.isEnabled).border,
focusedBorder: widget.style(widget.configuration.isEnabled).border,
disabledBorder: widget.style(widget.configuration.isEnabled).border,
errorBorder: widget.style(widget.configuration.isEnabled).border,
focusedErrorBorder: widget.style(widget.configuration.isEnabled).border,
//
errorStyle: const TextStyle(height: 0),
contentPadding: finalVerticalPadding(widget.style(widget.configuration.isEnabled)),
//s1 -- Content
hintText: widget.hint,
//
prefixIcon: widget.prefixWidget != null &&
widget.prefixWidget!(widget.configuration.isEnabled, () {
setState(() {});
}) !=
null
? Container(
margin: EdgeInsetsDirectional.fromSTEB(
widget.style(widget.configuration.isEnabled).prefixSpacing, 0, widget.style(widget.configuration.isEnabled).contentPadding.resolveToDirectional(finalTextDirection()).start, 0),
child: widget.prefixWidget!(widget.configuration.isEnabled, () {
setState(() {});
}),
)
: SizedBox(width: widget.style(widget.configuration.isEnabled).contentPadding.resolveToDirectional(finalTextDirection()).start),
//
prefixIconConstraints: widget.prefixWidget != null &&
widget.prefixWidget!(widget.configuration.isEnabled, () {
setState(() {});
}) !=
null
? widget.style(widget.configuration.isEnabled).prefixSize != 0
? BoxConstraints.expand(
width: widget.style(widget.configuration.isEnabled).prefixSize +
widget.style(widget.configuration.isEnabled).prefixSpacing +
widget.style(widget.configuration.isEnabled).contentPadding.resolveToDirectional(finalTextDirection()).start,
height: widget.style(widget.configuration.isEnabled).prefixSize,
)
: const BoxConstraints.tightForFinite()
: BoxConstraints.tightForFinite(width: widget.style(widget.configuration.isEnabled).contentPadding.resolveToDirectional(finalTextDirection()).start),
//
suffixIcon: widget.suffixWidget != null &&
widget.suffixWidget!(widget.configuration.isEnabled, () {
setState(() {});
}) !=
null
? Container(
margin: EdgeInsetsDirectional.fromSTEB(
widget.style(widget.configuration.isEnabled).contentPadding.resolveToDirectional(finalTextDirection()).end, 0, widget.style(widget.configuration.isEnabled).suffixSpacing, 0),
child: widget.suffixWidget!(widget.configuration.isEnabled, () {
setState(() {});
}),
)
: SizedBox(width: widget.style(widget.configuration.isEnabled).contentPadding.resolveToDirectional(finalTextDirection()).end),
suffixIconConstraints: widget.suffixWidget != null &&
widget.suffixWidget != null &&
widget.suffixWidget!(widget.configuration.isEnabled, () {
setState(() {});
}) !=
null
? widget.style(widget.configuration.isEnabled).suffixSize != 0
? BoxConstraints.expand(
width: widget.style(widget.configuration.isEnabled).suffixSize +
widget.style(widget.configuration.isEnabled).suffixSpacing +
widget.style(widget.configuration.isEnabled).contentPadding.resolveToDirectional(finalTextDirection()).end,
height: widget.style(widget.configuration.isEnabled).suffixSize,
)
: const BoxConstraints.tightForFinite()
: BoxConstraints.tightForFinite(width: widget.style(widget.configuration.isEnabled).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: widget.configuration.isFixedHeight
? !widget.configuration.isTextArea
? null
: widget.style(widget.configuration.isEnabled).fixedHeight
: null,
alignment: widget.style(widget.configuration.isEnabled).textAlignVertical.toAlignment(),
child: Stack(
children: [
TextFormField(
//s1 -- Functionality
key: widget.stateKey,
controller: _textController,
textInputAction: widget.configuration.inputAction,
//
//s1 -- Configurations
autovalidateMode: widget.validatingMode,
autofocus: widget.configuration.withAutofocus,
keyboardType: widget.configuration.inputType,
textDirection: finalTextDirection(),
obscureText: widget.configuration.withObscurity ? (widget.obscureText ?? false) : false,
inputFormatters: widget.inputFormatters,
//
minLines: widget.configuration.isFixedHeight && widget.configuration.isTextArea ? null : widget.style(widget.configuration.isEnabled).minLines,
maxLines: widget.configuration.isFixedHeight
? widget.configuration.isTextArea
? null
: widget.style(widget.configuration.isEnabled).maxLines
: widget.style(widget.configuration.isEnabled).maxLines,
//
maxLength: widget.style(widget.configuration.isEnabled).maxLength,
maxLengthEnforcement: widget.configuration.maxLengthEnforcement,
//
expands: widget.configuration.isFixedHeight && widget.configuration.isTextArea ? true : false,
// - Validation
validator: widget.validator != null ? (s) => widget.validator!(widget.configuration.isEnabled, s) : null,
// - Validation
//s1 -- Style
style: widget.style(widget.configuration.isEnabled).textStyle,
cursorColor: widget.style(widget.configuration.isEnabled).cursorColor,
textAlign: widget.style(widget.configuration.isEnabled).textAlign,
textAlignVertical: widget.style(widget.configuration.isEnabled).textAlignVertical,
//
//s1 -- Input Decoration
decoration: inputDecoration,
),
SizedBox(
width: double.infinity,
height: finalVerticalPadding(widget.style(widget.configuration.isEnabled), forceRespectBorder: true).vertical * 2,
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () async {
if (widget.onTap != null) {
(T item, String label)? c = _currentValue;
(T item, String label)? newValue = await widget.onTap!(_currentValue);
if (newValue != null) {
setState(() {
_currentValue = newValue;
_textController.text = widget.textFieldMapper(c?.$2, newValue.$2);
});
}
}
},
splashFactory: NoSplash.splashFactory,
onLongPress: () async {
if (widget.onHold != null) {
(T item, String label)? c = _currentValue;
(T item, String label)? newValue = await widget.onHold!(_currentValue);
if (newValue != null) {
setState(() {
_currentValue = newValue;
_textController.text = widget.textFieldMapper(c?.$2, newValue.$2);
});
}
}
},
customBorder: widget.style(widget.configuration.isEnabled).border,
highlightColor: Colors.transparent,
),
),
)
],
),
),
),
widget.messageBuilder != null &&
widget.messageBuilder!(
widget.configuration.isEnabled,
) !=
null
? Padding(
padding: widget.style(widget.configuration.isEnabled).messagePadding,
child: widget.messageBuilder!(widget.configuration.isEnabled) ?? Container(),
)
: Container()
],
);
//!SECTION
}
@override
void dispose() {
//SECTION - Disposable variables
//!SECTION
super.dispose();
}
}