import 'package:flutter/material.dart'; class AppTextFormField extends StatelessWidget { final TextEditingController? controller; final FocusNode? focusNode; final String label; final String? errorText; final TextInputType keyboardType; final TextInputAction textInputAction; final bool obscureText; final FormFieldValidator? validator; final ValueChanged? onSubmitted; const AppTextFormField({ Key? key, this.controller, this.focusNode, required this.label, this.errorText, this.keyboardType = TextInputType.text, this.textInputAction = TextInputAction.next, this.obscureText = false, this.validator, this.onSubmitted, }) : super(key: key); @override Widget build(BuildContext context) { return FormField( initialValue: '', validator: validator, autovalidateMode: AutovalidateMode.onUserInteraction, builder: (formFieldState) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, maxLines: 1, style: Theme.of(context).textTheme.bodyText1, ), const SizedBox(height: 4), TextField( controller: controller, focusNode: focusNode, keyboardType: keyboardType, textInputAction: textInputAction, obscureText: obscureText, decoration: InputDecoration( errorText: formFieldState.hasError ? '' : null, errorStyle: const TextStyle(height: 0), border: const OutlineInputBorder(), ), onChanged: (value) { formFieldState.didChange(value); }, onSubmitted: onSubmitted, ), const SizedBox(height: 4), Align( alignment: Alignment.topLeft, child: Text( formFieldState.hasError ? formFieldState.errorText! : '', overflow: TextOverflow.ellipsis, maxLines: 1, style: Theme.of(context).textTheme.caption!.copyWith( color: Theme.of(context).colorScheme.error, ), ), ), ], ); }, ); } }