Flutter中TextFormField高度随窗口自适应的问题求助
解决TextFormField自适应高度的光标与异常问题
问题描述
尝试实现TextFormField高度随窗口高度自适应,通过ConstrainedBox设置了高度约束,但调整contentPadding时遇到两个问题:
- 将垂直内边距设为
double.infinity会抛出异常:[ERROR:flutter/flow/layers/transform_layer.cc(23)] TransformLayer is constructed with an invalid matrix. ══╡ EXCEPTION CAUGHT BY GESTURES LIBRARY ╞══════════════════════════════════════════════════════════ The following assertion was thrown while handling a pointer data packet: 'package:flutter/src/material/input_decorator.dart': Failed assertion: line 1636 pos 18: 'transformed == position - offset': is not true. - 使用固定大数值作为垂直内边距,视觉显示正常,但光标会渲染在组件上方1000像素处。
解决方案
核心问题是错误地用contentPadding强制拉伸高度——contentPadding的作用是控制输入内容与装饰框的间距,而非调整组件整体高度。正确做法是利用ConstrainedBox的约束,配合TextFormField的专属属性实现自适应高度,同时保证光标位置正常:
修改步骤
- 移除超大垂直内边距,改为合理的间距值(如垂直方向16px)
- 给TextFormField添加
expands: true,让它填充父容器的约束空间 - 设置
maxLines: null和minLines: 1,允许输入框根据高度自动适配行数(单行输入可保持maxLines:1) - 移除
isDense: true,该属性会压缩内部间距,与自适应高度冲突
修改后的完整代码
import 'package:flutter/material.dart'; class HeliosFormTextField extends StatefulWidget { const HeliosFormTextField({ super.key, required this.controller, required this.labelText, required this.labelTextOnError, required this.validityCriteria, this.keyboardType, this.obscureText = false, this.clearOnError = true, }); final TextEditingController controller; final String labelText; final String labelTextOnError; final bool Function(String?) validityCriteria; final bool obscureText; final bool clearOnError; final TextInputType? keyboardType; @override State<HeliosFormTextField> createState() => _HeliosFormTextFieldState(); } class _HeliosFormTextFieldState extends State<HeliosFormTextField> { TextFieldStatus status = TextFieldStatus.none; ///Can change behaviour of the widget only if [widget.obscureText] is true bool obscureSymbols = true; @override Widget build(BuildContext context) { return ConstrainedBox( constraints: BoxConstraints.tightFor(height: MediaQuery.of(context).size.height * 0.065), child: TextFormField( cursorErrorColor: Theme.of(context).colorScheme.primary, obscureText: widget.obscureText ? obscureSymbols : false, style: Theme.of(context) .textTheme .bodyMedium ?.copyWith(fontWeight: FontWeight.w500), keyboardType: widget.keyboardType, autocorrect: false, // 新增属性实现自适应高度 expands: true, maxLines: null, minLines: 1, decoration: InputDecoration( labelText: status == TextFieldStatus.error ? widget.labelTextOnError : widget.labelText, labelStyle: status == TextFieldStatus.error ? Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.red) : Theme.of(context).textTheme.bodyMedium , border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(20)), borderSide: BorderSide.none), floatingLabelBehavior: FloatingLabelBehavior.never, filled: true, fillColor: Theme.of(context).colorScheme.surface, errorStyle: const TextStyle( height: 0, color: Colors.transparent, ), // 改为合理的内边距 contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), suffixIcon: widget.obscureText ? IconButton( icon: Icon(obscureSymbols ? Icons.visibility_rounded : Icons.visibility_off_rounded), onPressed: () => setState(() { obscureSymbols = !obscureSymbols; }), ) : null, ), controller: widget.controller, validator: (value) { if (!widget.validityCriteria(value)) { if (status != TextFieldStatus.error) { setState(() { status = TextFieldStatus.error; }); if (widget.clearOnError) widget.controller.clear(); } return ''; } return null; }, onChanged: (value) { if (status == TextFieldStatus.error && widget.validityCriteria(value)) { setState(() { status = TextFieldStatus.none; }); } }, ), ); } } enum TextFieldStatus { none, error, selected, }
额外说明
expands: true必须配合maxLines: null使用,否则会触发布局异常- 若需单行输入,保持
maxLines:1即可,输入框会在ConstrainedBox的高度内自动垂直居中文本与光标 - 合理的contentPadding可避免文本贴紧输入框边缘,提升视觉体验
内容的提问来源于stack exchange,提问作者Michael Feygenberg
相关产品推荐
相关产品推荐

