import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/utils/extension.dart'; import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class DidvanTextField extends StatefulWidget { final void Function(String value)? onChanged; final void Function(String value)? onSubmitted; final bool enabled; final bool autoFocus; final TextAlign? textAlign; final String? title; final String? hintText; final dynamic initialValue; final bool obsecureText; final bool acceptSpace; final String? Function(String value)? validator; final TextInputType? textInputType; final bool disableBorders; final bool isSmall; final int? maxLine; final int? minLine; final int? maxLength; final bool hasHeight; final bool showLen; const DidvanTextField({ Key? key, this.onChanged, this.enabled = true, this.title, this.hintText, this.initialValue, this.validator, this.textInputType, this.textAlign, this.obsecureText = false, this.autoFocus = false, this.onSubmitted, this.acceptSpace = true, this.disableBorders = false, this.isSmall = false, this.maxLine, this.minLine, this.maxLength, this.hasHeight = true, this.showLen = false, }) : super(key: key); @override State createState() => _DidvanTextFieldState(); } class _DidvanTextFieldState extends State { final FocusNode _focusNode = FocusNode(); final TextEditingController _controller = TextEditingController(); bool _hideContent = false; String? _error; @override void initState() { if (widget.initialValue != null) { _controller.text = widget.initialValue; } _hideContent = widget.obsecureText; _focusNode.addListener(() { // setState(() {}); }); super.initState(); } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (widget.title != null) DidvanText( widget.title!, color: _titleColor(), ), if (widget.title != null) const SizedBox(height: 8), Container( height: widget.hasHeight ? 48 : null, padding: const EdgeInsets.symmetric(horizontal: 12).copyWith( left: widget.obsecureText ? 0 : 12, ), decoration: BoxDecoration( color: _fillColor(), borderRadius: DesignConfig.lowBorderRadius, border: widget.disableBorders ? null : Border.all(color: _borderColor()), ), child: ValueListenableBuilder( valueListenable: _controller, builder: (context, val, _) { return Directionality( textDirection: val.text.startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, child: TextFormField( inputFormatters: [ if (!widget.acceptSpace) FilteringTextInputFormatter.allow( RegExp("[0-9a-zA-Z]")), ], autofocus: widget.autoFocus, obscureText: _hideContent, textAlign: widget.textAlign ?? TextAlign.start, keyboardType: widget.textInputType, focusNode: _focusNode, controller: _controller, onFieldSubmitted: widget.onSubmitted, onChanged: _onChanged, validator: _validator, maxLines: _hideContent ? 1 : widget.maxLine, minLines: widget.minLine, maxLength: widget.maxLength, obscuringCharacter: '*', buildCounter: widget.showLen ? null : (context, {required currentLength, required isFocused, required maxLength}) => const SizedBox(), style: (widget.isSmall ? Theme.of(context).textTheme.bodySmall! : Theme.of(context).textTheme.bodyMedium!) .copyWith( fontFamily: DesignConfig.fontFamily.padRight(3)), decoration: InputDecoration( suffixIcon: _suffixBuilder(), enabled: widget.enabled, border: InputBorder.none, hintText: widget.hintText, errorStyle: const TextStyle(height: 0.01), hintStyle: (widget.isSmall ? Theme.of(context).textTheme.bodySmall! : Theme.of(context).textTheme.bodyMedium!) .copyWith(color: Theme.of(context).colorScheme.hint), ), ), ); }), ), AnimatedVisibility( isVisible: _error != null, duration: DesignConfig.lowAnimationDuration, child: Column( children: [ const SizedBox(height: 8), Row( children: [ Icon( DidvanIcons.lightbulb_exclamation_regular, color: Theme.of(context).colorScheme.error, size: 14, ), DidvanText( _error ?? '', style: Theme.of(context).textTheme.bodySmall, color: Theme.of(context).colorScheme.error, ), ], ), ], ), ) ], ); } Color _borderColor() { if (_focusNode.hasFocus) { return Theme.of(context).colorScheme.focusedBorder; } else if (_error != null) { return Theme.of(context).colorScheme.error; } return Theme.of(context).colorScheme.border; } Color? _titleColor() { if (!widget.enabled) { return Theme.of(context).colorScheme.hint; } if (_error != null) { return Theme.of(context).colorScheme.error; } return null; } Color _fillColor() { if (!widget.enabled) { return DesignConfig.isDark ? Theme.of(context).colorScheme.disabledBackground : Theme.of(context).colorScheme.secondCTA; } if (_focusNode.hasFocus) { return Theme.of(context).colorScheme.focused; } if (_error != null) { return Theme.of(context).colorScheme.errorBack; } return Theme.of(context).colorScheme.surface; } Widget? _suffixBuilder() { if (widget.obsecureText) { return FittedBox( fit: BoxFit.scaleDown, child: GestureDetector( onTap: () { setState(() { _hideContent = !_hideContent; }); }, child: Icon( _hideContent ? DidvanIcons.eye_solid : DidvanIcons.eye_close_solid, ), ), ); } return null; } void _onChanged(String value) { setState(() { _error = null; }); // value = value.toEnglishDigit(); widget.onChanged?.call(value); } String? _validator(String? value) { if (widget.validator != null) { final String? error = widget.validator!(value!); if (error != null) { setState(() { _error = error; }); return ''; } else { setState(() { _error = null; }); } } return null; } @override void dispose() { _controller.dispose(); super.dispose(); } }