didvan-app/lib/views/widgets/didvan/text_field.dart

265 lines
8.0 KiB
Dart

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<DidvanTextField> createState() => _DidvanTextFieldState();
}
class _DidvanTextFieldState extends State<DidvanTextField> {
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: <TextInputFormatter>[
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();
}
}