Flutter中TextFormField输入时错误消息垂直截断问题
输入框错误消息垂直截断问题解决
问题背景
我封装了一个基于TextFormField的CustomTextFormField组件,在用户资料页面使用时遇到问题:输入框为空时错误消息显示正常,但用户开始输入时,错误消息会被垂直截断,效果如下:

自定义输入框组件代码
import 'package:flutter/material.dart'; class CustomTextFormField extends StatelessWidget { final TextEditingController controller; final String hintText; final String label; final bool obscureText; final FormFieldValidator<String>? validator; final ValueChanged<String>? onChanged; const CustomTextFormField({ Key? key, required this.controller, required this.hintText, this.obscureText = false, required this.label, this.validator, this.onChanged, }) : super(key: key); @override Widget build(BuildContext context) { return TextFormField( autovalidateMode: AutovalidateMode.onUserInteraction, controller: controller, obscureText: obscureText, validator: validator, onChanged: onChanged, decoration: InputDecoration( filled: true, fillColor: Theme.of(context).colorScheme.surfaceVariant, contentPadding: const EdgeInsets.fromLTRB(16, 8, 16, 8), labelText: label, labelStyle: Theme.of(context).textTheme.labelSmall, hintText: hintText, hintStyle: Theme.of(context).textTheme.labelMedium, border: OutlineInputBorder( borderSide: BorderSide(width: 0.50, color: Theme.of(context).colorScheme.primary), gapPadding: 0, borderRadius: const BorderRadius.all( Radius.circular(10), ), ), ), ); } }
组件使用场景(用户资料页面)
class UserProfileScreen extends GetView<UserController> { UserProfileScreen({Key? key}) : super(key: key); final _formKey = GlobalKey<FormState>(); final _usernameFormKey = GlobalKey<FormState>(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('User Profile'), ), drawer: CustomDrawer(), body: Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Obx(() { UserModel user = controller.user.value!; return Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ const CircleAvatar( radius: 50, backgroundColor: Colors.grey, child: Icon( Icons.person, size: 50, ), ), const SizedBox(height: 20.0), Text( user.username, style: const TextStyle( fontSize: 20.0, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 10.0), ElevatedButton( onPressed: () { showDialog( context: context, builder: (_) => AlertDialog( title: const Text('New Username'), content: Form( key: _usernameFormKey, child: CustomTextFormField( controller: controller.usernameController, label: 'Username', hintText: 'Enter your username', onChanged: (value) { user = user.copyWith(username: value); controller.user.value = user; }, validator: FormValidators.validateUsername, ), ), actions: [ TextButton( child: const Text('Cancel'), onPressed: () => Navigator.of(context).pop(), ), TextButton( child: const Text('Save'), onPressed: () { if (_usernameFormKey.currentState!.validate()) { controller.updateUser(user); Navigator.of(context).pop(); } }, ), ], ), ); }, child: const Text("Change Username"), ), // 省略剩余代码 ], ); }), ), ), ); } }
应用主题配置
// AppTheme类 import 'package:flutter/material.dart'; import 'package:mastermind_together/src/ui/theme/color_scheme.dart'; import 'package:mastermind_together/src/ui/theme/text_styles.dart'; class AppTheme { AppTheme._(); static ThemeData lightTheme = ThemeData( colorScheme: lightThemeColors, useMaterial3: true, textTheme: textTheme, buttonTheme: ButtonThemeData( buttonColor: lightThemeColors.primary, textTheme: ButtonTextTheme.primary, ), canvasColor: lightThemeColors.surface, ); static TextTheme textTheme = const TextTheme( displayLarge: h1, displayMedium: h2, bodyLarge: bodyMedium, bodyMedium: body, bodySmall: labelSmall, labelLarge: btnText, labelMedium: placeholderBodyMedium, labelSmall: labelText, headlineMedium: h3, titleMedium: bodyMedium, ); } // 文本样式定义 import 'package:flutter/material.dart'; import 'package:mastermind_together/src/ui/theme/color_scheme.dart'; const String fontFamily = 'Inter'; const Color textColor = darkerPrimaryColor; const Color textPlaceholderColor = placeholderColor; const Color btnTextColor = Colors.white; //TODO move to color_scheme const Color linkColor = Colors.blue; const TextStyle h1 = TextStyle( color: textColor, fontSize: 64, fontFamily: fontFamily, fontWeight: FontWeight.w700, ); const TextStyle h2 = TextStyle( color: textColor, fontSize: 42, fontFamily: fontFamily, fontWeight: FontWeight.w400, ); const TextStyle h3 = TextStyle( color: textColor, fontSize: 26, fontFamily: fontFamily, fontWeight: FontWeight.w700, ); const TextStyle body = TextStyle( color: textColor, fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w400, ); const TextStyle bodyMediumLink = TextStyle( color: textColor, fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w500, decoration: TextDecoration.underline, ); const TextStyle linkStyle = TextStyle( color: linkColor, fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w700, decoration: TextDecoration.underline, ); const TextStyle bodyMedium = TextStyle( color: textColor, fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w500, ); const TextStyle placeholderBodyMedium = TextStyle( color: textPlaceholderColor, fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w400, height: 1.6, ); const TextStyle labelText = TextStyle( color: textColor, fontSize: 14, fontFamily: fontFamily, fontWeight: FontWeight.w500, // height: 2.0, ); const TextStyle cardTitle = TextStyle( color: textColor, fontSize: 26, fontFamily: fontFamily, fontWeight: FontWeight.w600, ); const TextStyle labelSmall = TextStyle( color: textColor, fontSize: 10, fontFamily: fontFamily, fontWeight: FontWeight.w400, height: 0.4, ); const TextStyle btnText = TextStyle( color: btnTextColor, fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w700, );
问题原因
问题出在主题配置的labelSmall样式中,你设置了height: 0.4,这个值过小,导致错误消息文本的行高被强制压缩。当输入框有内容时,错误消息的垂直空间被挤压,最终出现截断现象。
解决方法
方案1:修改主题中的行高
调整labelSmall的height属性为合理值(比如1.2),或者直接移除该属性使用默认行高:
const TextStyle labelSmall = TextStyle( color: textColor, fontSize: 10, fontFamily: fontFamily, fontWeight: FontWeight.w400, height: 1.2, // 修改为合理行高,或删除此行 );
方案2:在输入框中显式指定错误样式
在CustomTextFormField的InputDecoration中单独设置错误文本样式,覆盖主题的影响:
decoration: InputDecoration( // 保留原有配置 errorStyle: TextStyle( height: 1.2, fontSize: 10, fontFamily: fontFamily, color: Colors.red, // 可选:指定错误文本颜色 ), ),
两种方案都能保证错误消息拥有足够的垂直显示空间,避免截断。
内容的提问来源于stack exchange,提问作者Răzvan Puiu
相关产品推荐
相关产品推荐

