Flutter:如何消除InputFormField因errorText产生的额外空白
解决TextFormField错误文本导致布局移位的问题
核心问题是TextFormField处于错误状态时,即便你返回空的错误文本,它依然会默认预留错误文本的显示空间。要彻底解决这个问题,只需两步:
- 禁用TextFormField自带的错误文本空间:在
InputDecoration中添加errorStyle: const TextStyle(height: 0, fontSize: 0),这样组件不会为错误文本预留高度,同时保留错误状态下的边框样式。 - 保留自定义错误文本布局:你已通过Stack实现错误文本的自定义定位,只需确保定位准确即可。
修改后的完整代码如下:
class CustomFormField extends StatefulWidget { final GlobalKey formKey; final TextEditingController controller; final FocusNode currentFocusNode; final FocusNode futureFocusNode; final TextInputType inputType; final TextInputAction inputAction; final bool allowShowPassword; final bool enabled; final bool autoFocus; final String labelText; final String hintText; final FormFieldValidator<String>? validator; final Function? onChanged; final FormFieldSetter<String>? onSaved; const CustomFormField( {super.key, required this.formKey, required this.controller, required this.currentFocusNode, required this.futureFocusNode, this.inputType = TextInputType.text, this.inputAction = TextInputAction.next, this.allowShowPassword = true, this.enabled = true, this.autoFocus = false, this.labelText = '', this.hintText = '', this.validator, this.onChanged, this.onSaved}); @override State<CustomFormField> createState() => _CustomFormFieldState(); } class _CustomFormFieldState extends State<CustomFormField> { bool _passwordHidden = true; bool onError = false; String _errorText = ''; @override Widget build(BuildContext context) { ThemeData theme = Theme.of(context); return Padding( padding: const EdgeInsets.symmetric(horizontal: 5.0), child: Stack( children: [ TextFormField( enabled: widget.enabled, autofocus: widget.autoFocus, textInputAction: widget.inputAction, onEditingComplete: () => FocusScope.of(context).requestFocus(widget.futureFocusNode), focusNode: widget.currentFocusNode, keyboardType: widget.inputType, obscureText: (widget.inputType == TextInputType.visiblePassword && _passwordHidden) ? _passwordHidden : false, controller: widget.controller, validator: (value) { String? errorText = widget.validator?.call(value); setState(() { onError = errorText != null; _errorText = errorText ?? ''; }); if (errorText != null && Static.formIsValid) { Static.formIsValid = false; widget.currentFocusNode.requestFocus(); } return errorText == null ? null : ''; }, onSaved: widget.onSaved, onChanged: (value) { widget.onChanged?.call(value); }, style: TextStyle( color: widget.enabled ? Constants.formFieldColor : theme.disabledColor, fontSize: 16, fontWeight: FontWeight.normal, fontFamily: 'Ariel'), decoration: InputDecoration( labelText: widget.labelText, hintText: widget.hintText, hintStyle: const TextStyle(color: Colors.white38), contentPadding: const EdgeInsets.symmetric(horizontal: 25), filled: true, border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(25)), borderSide: BorderSide(color: Colors.black)), enabledBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(25)), borderSide: BorderSide(color: Colors.black)), focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(25)), borderSide: BorderSide(color: Colors.black)), errorBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(25)), borderSide: BorderSide(color: Colors.red)), focusedErrorBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(25)), borderSide: BorderSide(color: Colors.black)), disabledBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(25)), borderSide: BorderSide(color: Colors.black)), alignLabelWithHint: true, focusColor: Colors.black, // 关键:禁用自带错误文本的预留空间 errorStyle: const TextStyle(height: 0, fontSize: 0), suffixIcon: widget.inputType == TextInputType.visiblePassword && widget.allowShowPassword ? InkWell( onTap: () { setState(() { _passwordHidden = !_passwordHidden; }); }, child: Icon( _passwordHidden ? Icons.visibility_off_outlined : Icons.visibility_outlined, color: Constants.passwordShowHideIconColor, size: 18, ), ) : null, ), ), onError ? Positioned( bottom: 17, left: 25, child: Container( height: 15, decoration: const BoxDecoration(color: Colors.white), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: Text(_errorText, style: const TextStyle( color: Colors.red, fontSize: 13, fontStyle: FontStyle.normal)), ), ), ) : const SizedBox.shrink(), ], ), ); } }
额外优化点:统一validator中的状态更新逻辑,用SizedBox.shrink()替代空Container,代码更简洁。
修改后,TextFormField不会再为错误文本预留空间,自定义错误文本通过Stack定位也不会影响其他字段布局,彻底解决移位问题。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

