I have a CustomTextFormField which is a wrapper over a TextFormField as below:
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),
),
),
),
);
}
}
This is used in my User Profile screen as follows:
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"),
),
[...]
The error message is properly displayed when there is no text in the TextFormField, but it is vertically trimmed when the user starts typing. Please see the attached screenshots for clarity.
[
Edit: My App Theme:
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,
);
Check to see if the theme for your app is causing this. When I tested your initial popup dialog I did not run into the problem that you described. Try commenting out your theme setting where you set up your MaterialApp and see if the problem goes away. If it does look closer at your theme to see what might be causing the issue.