marco.pms.mobileapp/lib/helpers/widgets/my_text_field.dart

125 lines
3.8 KiB
Dart

import 'package:flutter/material.dart';
/// Reusable text field used across the app.
/// - Use [controller] when you need to keep text state externally (recommended).
/// - Use [initialValue] for quick one-off population (internal controller created).
/// - [readOnly], [maxLines], [keyboardType], [validator], [onChanged] supported.
/// - [label] used as InputDecoration.labelText, [hint] as hintText.
class MyTextField extends StatefulWidget {
final String? label;
final String? hint;
final TextEditingController? controller;
final String? initialValue;
final bool readOnly;
final bool enabled;
final int maxLines;
final TextInputType keyboardType;
final Widget? prefix;
final Widget? suffix;
final String? Function(String?)? validator;
final void Function(String)? onChanged;
final void Function()? onTap;
final String? errorText;
final EdgeInsetsGeometry? contentPadding;
const MyTextField({
Key? key,
this.label,
this.hint,
this.controller,
this.initialValue,
this.readOnly = false,
this.enabled = true,
this.maxLines = 1,
this.keyboardType = TextInputType.text,
this.prefix,
this.suffix,
this.validator,
this.onChanged,
this.onTap,
this.errorText,
this.contentPadding,
}) : super(key: key);
@override
State<MyTextField> createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
late final TextEditingController _internalController;
bool _usingInternal = false;
@override
void initState() {
super.initState();
if (widget.controller == null) {
_usingInternal = true;
_internalController =
TextEditingController(text: widget.initialValue ?? '');
} else {
_internalController = widget.controller!;
// if initialValue provided but using external controller, set it once:
if (widget.initialValue != null && widget.initialValue!.isNotEmpty) {
_internalController.text = widget.initialValue!;
}
}
}
@override
void didUpdateWidget(covariant MyTextField oldWidget) {
super.didUpdateWidget(oldWidget);
// If external controller changed, update our reference
if (oldWidget.controller != widget.controller && widget.controller != null) {
_internalController.text = widget.controller!.text;
}
// If initialValue changed and we use internal controller, update.
if (_usingInternal &&
widget.initialValue != null &&
widget.initialValue != oldWidget.initialValue) {
_internalController.text = widget.initialValue!;
}
}
@override
void dispose() {
if (_usingInternal) {
_internalController.dispose();
}
super.dispose();
}
InputBorder _defaultBorder() => OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(width: 1),
);
@override
Widget build(BuildContext context) {
return TextFormField(
controller: _internalController,
readOnly: widget.readOnly,
enabled: widget.enabled,
maxLines: widget.maxLines,
keyboardType: widget.keyboardType,
validator: widget.validator,
onChanged: widget.onChanged,
onTap: widget.onTap,
decoration: InputDecoration(
isDense: true,
contentPadding:
widget.contentPadding ?? const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
labelText: widget.label,
hintText: widget.hint,
prefixIcon: widget.prefix,
suffixIcon: widget.suffix,
errorText: widget.errorText,
border: _defaultBorder(),
enabledBorder: _defaultBorder(),
focusedBorder: _defaultBorder().copyWith(
borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 1.2)),
disabledBorder: _defaultBorder(),
),
);
}
}