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 createState() => _MyTextFieldState(); } class _MyTextFieldState extends State { 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(), ), ); } }