import 'dart:ui'; import 'package:country_code_picker/country_code_picker.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:get/get.dart'; import '../../core/constants/translation_keys.dart'; import '../../res/app_colors.dart'; import '../../res/app_theme.dart'; import '../custom_dropdown.dart'; import '../custom_text.dart'; import 'custom_dropdown_bottom_sheet.dart'; class InputField extends GetView { final FormFieldValidator? validator; GlobalKey? refKey; String labelText = ""; String hintText = ""; RxString errorTextMessage = "".obs; final RxBool _passwordVisible = true.obs; int? radius = 100; EdgeInsetsGeometry? contentPadding = const EdgeInsets.symmetric(horizontal: 15, vertical: 15); // String errorText = ""; bool isRequired = true; bool requiredPasswordIcon = false; Function(String?)? onChanged; FocusNode? focusNode = FocusNode(); Function(DropDown?)? onItemSelected; Rx selectedCountryCode = CountryCode().obs; Rx? selectedOption; // DropDown? selectedOption; final bool enabled; bool isDropDown = false; bool isTopMarginRequired = true; bool isCountryPicker = false; bool isBoxDecoration = true; bool showLabel = true; String? dropDownType = DropDownType.NORMAL; List? items; final AutovalidateMode autovalidateMode; final String? restorationId; InputDecoration? decoration; TextEditingController? controller; TextInputType? keyboardType; final TextInputAction? textInputAction; final TextCapitalization textCapitalization; final TextStyle? style; final StrutStyle? strutStyle; TextAlign textAlign; final TextAlignVertical? textAlignVertical; final TextDirection? textDirection; final bool autofocus; final String obscuringCharacter; bool isPassword = false; bool autocorrect = false; final SmartDashesType? smartDashesType; final SmartQuotesType? smartQuotesType; final bool enableSuggestions; int maxLines; int minLines; bool expands; final int? maxLength; final MaxLengthEnforcement? maxLengthEnforcement; final VoidCallback? onEditingComplete; final ValueChanged? onSubmitted; final List? inputFormatters; bool showCursor = true; double cursorWidth = 5.0; double cursorHeight = 15.0; Radius cursorRadius = const Radius.circular(10.0); Color cursorColor = AppThemeData.inputTextColor; final BoxHeightStyle selectionHeightStyle; final BoxWidthStyle selectionWidthStyle; final Brightness? keyboardAppearance; final EdgeInsets scrollPadding; final bool enableInteractiveSelection; Widget? prefixIcon; Widget? suffixIcon; final DragStartBehavior dragStartBehavior; final Function()? onClick; final TapRegionCallback? onTapOutside; final MouseCursor? mouseCursor; final InputCounterWidgetBuilder? buildCounter; final ScrollPhysics? scrollPhysics; final ScrollController? scrollController; final Iterable? autofillHints; final TextMagnifierConfiguration? magnifierConfiguration; bool readOnly; final ContentInsertionConfiguration? contentInsertionConfiguration; SpellCheckConfiguration? spellCheckConfiguration; final String? prefixText; final String? prefixCode; final double? inputFontSize; final FontWeight? inputFontWeight; final Color? inputColor; final Rx inputErrorColor = Rx(Colors.black); final Rx inputErrorBorderColor = Rx(Colors.black); RxBool isError = false.obs; InputField({ this.refKey, this.controller, this.labelText = "", this.hintText = "", // this.errorText = "", this.isRequired = true, this.requiredPasswordIcon = false, this.isBoxDecoration = true, this.showLabel = true, this.isTopMarginRequired = true, this.isDropDown = false, this.isCountryPicker = false, this.dropDownType, this.inputFontSize, this.inputFontWeight, this.inputColor, this.validator, this.contentPadding = const EdgeInsets.symmetric(horizontal: 15, vertical: 15), this.items, this.radius = 100, this.decoration, this.focusNode, this.onChanged, this.onItemSelected, this.prefixIcon, this.suffixIcon, this.selectedOption, this.enabled = true, this.autovalidateMode = AutovalidateMode.disabled, this.restorationId, this.readOnly = false, this.isPassword = false, this.textCapitalization = TextCapitalization.none, this.scrollPadding = const EdgeInsets.all(20.0), this.enableInteractiveSelection = true, this.maxLengthEnforcement, this.textAlign = TextAlign.start, this.autofocus = false, this.autocorrect = false, this.cursorWidth = 5.0, this.cursorHeight = 20.0, this.cursorRadius = const Radius.circular(10.0), this.keyboardType, this.style, this.textInputAction, this.strutStyle, this.textDirection, this.maxLength, this.onEditingComplete, this.onSubmitted, this.inputFormatters, this.cursorColor = AppThemeData.inputTextColor, this.keyboardAppearance, this.buildCounter, this.expands = false, this.minLines = 1, this.maxLines = 1, this.showCursor = true, this.onClick, this.onTapOutside, this.enableSuggestions = true, this.textAlignVertical, this.dragStartBehavior = DragStartBehavior.start, this.scrollController, this.scrollPhysics, this.selectionWidthStyle = BoxWidthStyle.tight, this.smartDashesType, this.smartQuotesType, this.selectionHeightStyle = BoxHeightStyle.tight, this.autofillHints, this.obscuringCharacter = '•', this.mouseCursor, this.magnifierConfiguration, this.contentInsertionConfiguration, this.spellCheckConfiguration, this.prefixText, this.prefixCode, }) : errorTextMessage = "".obs { // errorText = errorTextMessage.value; } void setText(String text) { controller!.text = text; markFieldAsValid(); } void clear() { controller!.clear(); markFieldAsValid(); } void setError(String errorMessage) { inputErrorBorderColor.value = AppThemeData.inputErrorBorderColor; inputErrorColor.value = AppThemeData.inputErrorColor; errorTextMessage.value = errorMessage; isError.value = true; } void markFieldAsValid() { inputErrorBorderColor.value = AppThemeData.inputBackgroundColor; inputErrorColor.value = AppThemeData.inputErrorColor; errorTextMessage.value = ""; isError.value = false; } void markFieldAsInvalid() { print("============================="); inputErrorBorderColor.value = AppThemeData.inputErrorBorderColor; inputErrorColor.value = AppThemeData.inputErrorColor; errorTextMessage.value = TranslationKeys.makeTranslation(TranslationKeys.textErrorRequired); isError.value = true; } void setDropDownOption(DropDown option) { selectedOption = Rx(option); setText(selectedOption!.value.label); markFieldAsValid(); } bool validate(String value) { if (isRequired) { if (value.isEmpty) { markFieldAsInvalid(); return false; } markFieldAsValid(); return true; } markFieldAsValid(); return true; } @override Widget build(BuildContext context) { return Obx(() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: isTopMarginRequired ? 15 : 0), Visibility( visible: isBoxDecoration && showLabel, child: Column( children: [ CustomText(labelText, style: AppThemeData.labelStyle), const SizedBox(height: 5), ], ), ), FormBuilderTextField( key: refKey, name: "", focusNode: focusNode, decoration: isCountryPicker ? countryCodeDecoration() : (isBoxDecoration ? boxedDecoration() : normalFieldDecoration()), enabled: enabled, autovalidateMode: autovalidateMode, restorationId: restorationId, readOnly: isDropDown ? true : readOnly, validator: (value) { validate(value!); return null; }, onChanged: (value) { if (value!.isNotEmpty) { markFieldAsValid(); } if (onChanged != null) { onChanged!(value); } }, textCapitalization: textCapitalization, scrollPadding: scrollPadding, enableInteractiveSelection: enableInteractiveSelection, maxLengthEnforcement: maxLengthEnforcement, textAlign: textAlign, autofocus: isDropDown ? false : autofocus, autocorrect: autocorrect, keyboardType: keyboardType, style: AppThemeData.inputStyle.copyWith(height: 1, fontSize: double.parse((inputFontSize ?? "0.0").toString()) == 0 ? AppThemeData.inputStyle.fontSize : double.parse((inputFontSize ?? "0.0").toString()), fontWeight: inputFontWeight ?? AppThemeData.inputStyle.fontWeight, color: inputColor ?? AppThemeData.inputStyle.color), controller: controller, textInputAction: textInputAction ?? TextInputAction.done, strutStyle: strutStyle, textDirection: textDirection, maxLength: maxLength, onEditingComplete: onEditingComplete, onSubmitted: onSubmitted, inputFormatters: inputFormatters, cursorColor: cursorColor, // cursorWidth: 1.5, // cursorHeight: 15, // cursorRadius: Radius.circular(10.0), keyboardAppearance: keyboardAppearance, buildCounter: buildCounter, expands: expands, maxLines: isPassword ? maxLines = 1 : maxLines, obscureText: isPassword && _passwordVisible.value, minLines: isPassword ? minLines = 1 : minLines, showCursor: isDropDown ? false : showCursor, onTap: () { if (onClick != null) { onClick!(); } if (isDropDown && items != null && items!.isNotEmpty) { // FocusScope.of(context).requestFocus(FocusNode()); CustomDropDownBottomSheet().showBottomSheet(context, items!, dropDownType ?? DropDownType.NORMAL, (selectedItem) { setText(selectedItem.label); selectedOption = Rx(selectedItem); if (onItemSelected != null) { onItemSelected!(selectedItem); } }); } }, onTapOutside: onTapOutside, enableSuggestions: enableSuggestions, textAlignVertical: textAlignVertical, dragStartBehavior: dragStartBehavior, scrollController: scrollController, scrollPhysics: scrollPhysics, selectionWidthStyle: selectionWidthStyle, smartDashesType: smartDashesType, smartQuotesType: smartQuotesType, selectionHeightStyle: BoxHeightStyle.tight, autofillHints: autofillHints, obscuringCharacter: obscuringCharacter, mouseCursor: mouseCursor, magnifierConfiguration: magnifierConfiguration, contentInsertionConfiguration: contentInsertionConfiguration, spellCheckConfiguration: spellCheckConfiguration, ), ], ); }); } InputDecoration normalFieldDecoration() { return InputDecoration( filled: true, prefixIcon: prefixIcon, fillColor: AppColors.transparent, isDense: true, contentPadding: const EdgeInsets.only(bottom: 5), labelText: labelText, labelStyle: AppThemeData.labelStyle, errorStyle: AppThemeData.errorStyle.copyWith(color: inputErrorColor.value), errorBorder: UnderlineInputBorder(borderSide: BorderSide(color: inputErrorBorderColor.value)), focusedBorder: const UnderlineInputBorder(borderSide: BorderSide(color: AppThemeData.inputFocusedBorderColor, width: 1.5)), enabledBorder: const UnderlineInputBorder(borderSide: BorderSide(color: AppThemeData.inputEnabledBorderColor, width: 1.5)), errorText: errorTextMessage.value.isEmpty ? null : errorTextMessage.value, hintText: hintText.isEmpty ? null : hintText, hintStyle: AppThemeData.hintStyle, prefixText: prefixText, prefixStyle: AppThemeData.hintStyle, suffixIcon: Icon( Icons.arrow_forward_ios_outlined, color: AppColors.colorGrey500, size: isDropDown ? 22 : 0, ), ); } InputDecoration boxedDecoration() { return InputDecoration( isDense: true, contentPadding: contentPadding ?? const EdgeInsets.symmetric(horizontal: 15, vertical: 15) /*EdgeInsets.all(10)*/, labelText: isBoxDecoration ? null : (labelText.isNotEmpty ? labelText : null), labelStyle: AppThemeData.labelStyle, prefixIcon: prefixIcon, errorStyle: AppThemeData.errorStyle.copyWith(color: inputErrorColor.value), // border: OutlineInputBorder(borderSide: BorderSide(color: inputErrorBorderColor.value), borderRadius: BorderRadius.all(Radius.circular(10))), focusedErrorBorder: OutlineInputBorder(borderSide: BorderSide(color: inputErrorBorderColor.value), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), errorBorder: OutlineInputBorder(borderSide: BorderSide(color: inputErrorBorderColor.value), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), focusedBorder: OutlineInputBorder(borderSide: const BorderSide(color: AppThemeData.inputFocusedBorderColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), enabledBorder: OutlineInputBorder(borderSide: const BorderSide(color: AppThemeData.inputEnabledBorderColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), errorText: errorTextMessage.value.isEmpty ? null : errorTextMessage.value, hintText: hintText.isEmpty ? null : hintText, hintStyle: AppThemeData.hintStyle, prefixText: prefixText, // prefixStyle: AppThemeData.hintStyle, suffixIcon: suffixIcon ?? addSuffixIcon(), ); } Widget? addSuffixIcon() { if (requiredPasswordIcon) { return IconButton( icon: Icon(size: 18, color: AppColors.colorGrey500, _passwordVisible.value ? Icons.visibility : Icons.visibility_off_rounded), onPressed: () { _passwordVisible.value = !_passwordVisible.value; }, ); } else if (isDropDown) { return const Icon( Icons.arrow_forward_ios_outlined, color: AppColors.colorGrey500, size: 22, ); } return null; } InputDecoration countryCodeDecoration() { return InputDecoration( prefixIcon: CountryCodePicker( initialSelection: 'PK', onInit: (code) { selectedCountryCode.value = code!; }, showFlagDialog: true, showCountryOnly: true, countryFilter: const [ 'pk', 'us', 'it', ], emptySearchBuilder: (context) { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 15), CustomText("No Results", style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.inputLabelColor)), ], ); }, onChanged: (val) { selectedCountryCode.value = val; }, searchDecoration: InputDecoration( filled: false, fillColor: AppColors.transparent, isDense: false, alignLabelWithHint: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), errorStyle: AppThemeData.errorStyle.copyWith(color: AppThemeData.inputErrorColor), errorBorder: const OutlineInputBorder(borderSide: BorderSide(color: AppThemeData.inputErrorBorderColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(10))), focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: AppThemeData.inputFocusedBorderColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(10))), enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: AppThemeData.inputEnabledBorderColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(10))), prefixStyle: AppThemeData.hintStyle, ), searchStyle: AppThemeData.inputStyle.copyWith(height: 1), // textStyle: AppThemeData.inputStyle.copyWith(height: 1), ), isDense: true, contentPadding: const EdgeInsets.all(10), labelText: isBoxDecoration ? null : (labelText.isNotEmpty ? labelText : null), labelStyle: AppThemeData.labelStyle, errorStyle: AppThemeData.errorStyle.copyWith(color: inputErrorColor.value), focusedErrorBorder: OutlineInputBorder(borderSide: BorderSide(color: inputErrorBorderColor.value), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), errorBorder: OutlineInputBorder(borderSide: BorderSide(color: inputErrorBorderColor.value), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), focusedBorder: OutlineInputBorder(borderSide: const BorderSide(color: AppThemeData.inputFocusedBorderColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), enabledBorder: OutlineInputBorder(borderSide: const BorderSide(color: AppThemeData.inputEnabledBorderColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(radius == -1 ? 10 : double.parse(radius.toString())))), // focusedErrorBorder: OutlineInputBorder( // borderSide: BorderSide(color: inputErrorBorderColor.value), // borderRadius: const BorderRadius.all(Radius.circular(10))), // errorBorder: OutlineInputBorder( // borderSide: BorderSide(color: inputErrorBorderColor.value), // borderRadius: const BorderRadius.all(Radius.circular(10))), // focusedBorder: const OutlineInputBorder( // borderSide: BorderSide( // color: AppThemeData.inputFocusedBorderColor, // width: 1.5, // ), // borderRadius: BorderRadius.all(Radius.circular(10))), // enabledBorder: const OutlineInputBorder( // borderSide: BorderSide( // color: AppThemeData.inputEnabledBorderColor, width: 1.5), // borderRadius: BorderRadius.all(Radius.circular(10))), errorText: errorTextMessage.value.isEmpty ? null : errorTextMessage.value, hintText: hintText.isEmpty ? null : hintText, hintStyle: AppThemeData.hintStyle, prefixText: prefixText ?? "", prefixStyle: AppThemeData.hintStyle, suffixIcon: Icon( Icons.arrow_forward_ios_outlined, color: AppColors.colorGrey500, size: isDropDown ? 22 : 0, ), ); } String getCustomText({bool isCountryCodeRequired = false, bool isPrefixRequired = false}) { if (isCountryCodeRequired) { if (isCountryPicker) { return selectedCountryCode.value.dialCode! + controller!.text; } return controller!.text; } else if (isPrefixRequired) { return (prefixCode ?? "") + controller!.text; } return controller?.text ?? ""; } } class DropDownType { static final String NORMAL = "01"; static final String SPECIAL = "02"; } class InputType { static final int INT_MAX_VALUE_ = 9223372036854775807; static final FilteringTextInputFormatter NUMBER = FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')); static final FilteringTextInputFormatter NUMBER_DECIMAL = FilteringTextInputFormatter.allow(RegExp(r'^[0-9]*\.?[0-9]*$')); static List maxValueFilter(int maxNumber, bool isDecimal, int decimalPoint) { return [ FilteringTextInputFormatter.allow(isDecimal ? RegExp(r'^\d+\.?\d{0,2}') : RegExp(r'^\d+$')), TextInputFormatter.withFunction((oldValue, newValue) { if (newValue.text.isEmpty) { return newValue.copyWith(text: '', selection: const TextSelection.collapsed(offset: 0)); } try { final numericValue = double.parse(newValue.text); if (numericValue <= maxNumber) { return newValue; } } catch (e) { return newValue; } return oldValue; }), ]; } }