user54517
user54517

Reputation: 2420

Validator error message changes TextFormField's height

When the error message shows up, it reduces the height of the TextFormField. If I understood correctly, that's because the height of the error message is taking into account in the height specified.

Here's a screen before :

before

and after :

after

Tried to put conterText: ' ' to the BoxDecoration (as I've seen on another topic) but it didn't help.

An idea ?

EDIT : OMG completly forgot to put the code, here it is :

 return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Container(
            height: 40.0,
            child: _createTextFormField(loginEmailController, Icons.alternate_email, "Email Adress", false, TextInputType.emailAddress),
          ),
          Container(
            height: 40.0,
            child: _createTextFormField(loginPasswordController, Icons.lock, "Password", true, TextInputType.text),
          ),

          SizedBox(
            width: double.infinity,
            child: loginButton
          )
        ],
      ),
    );

  }

  Widget _createTextFormField(TextEditingController controller, IconData icon, String hintText, bool obscureText, TextInputType inputType){
      return TextFormField(
        keyboardType: inputType,
        controller: controller,
        obscureText: obscureText,
        /* style: TextStyle(
          fontSize: 15.0,
        ), */
        decoration: InputDecoration(
         /*  contentPadding:
              EdgeInsets.symmetric(vertical: 5.0, horizontal: 8.0), */
          border: OutlineInputBorder(borderRadius: BorderRadius.circular(5.0)),
          icon: Icon(
            icon,
            color: Colors.black,
            size: 22.0,
          ),
          //hintText: hintText,
          labelText: hintText,
        ),
        validator: (value) {
          if (value.isEmpty) {
            return 'Enter some text';
          }
          return null;
        },
      );
    }

Upvotes: 33

Views: 29916

Answers (10)

rimsha wazir
rimsha wazir

Reputation: 1

TextFormField( textAlign: TextAlign.start, textCapitalization: textCapitalization ?? TextCapitalization.none, readOnly: readOnly, initialValue: initialValue, focusNode: focusNode, controller: controller, keyboardType: keyboardType, textInputAction: textInputAction ?? TextInputAction.next, obscureText: isPassword ? !isPasswordVisible : false, validator: validator, autovalidateMode: autoValidateMode, textAlignVertical: TextAlignVertical.top, onTap: onTap, style: Theme.of(context) .textTheme .bodyLarge! .copyWith(fontSize: 16.fs(context)), inputFormatters: inputFormatters ?? [LengthLimitingTextInputFormatter(48)], decoration: InputDecoration( filled: true, constraints: BoxConstraints(minHeight: 48.h(context)), contentPadding: EdgeInsets.symmetric(vertical: 10, horizontal: 18), isDense: true, focusedBorder: isBorder ? OutlineInputBorder( borderRadius: BorderRadius.circular(8.r(context)), borderSide: const BorderSide(color: AppColors.primaryColor), ) : OutlineInputBorder( borderRadius: BorderRadius.circular(8.r(context)), borderSide: const BorderSide( color: AppColors.textFieldBorderColor), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8.r(context)), borderSide: const BorderSide(color: AppColors.primaryColor), ), errorStyle: Theme.of(context).textTheme.bodySmall!.copyWith( color: Theme.of(context).colorScheme.error, fontSize: 12.fs(context)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8.r(context)), borderSide: const BorderSide(color: AppColors.textFieldBorderColor), ), hintStyle: Theme.of(context).textTheme.bodyLarge!.copyWith( color: color ?? Theme.of(context).colorScheme.onPrimaryFixed, fontSize: 16.fs(context)), hintText: hintText, suffixIconConstraints: const BoxConstraints( minWidth: 20, minHeight: 20, maxHeight: 40, maxWidth: 40), suffixIcon: isPassword ? IconButton( padding: EdgeInsets.zero, onPressed: () { if (passwordFieldType != null) { context .read() .add(PasswordToggledEvent(passwordFieldType!)); } }, icon: Icon( isPasswordVisible ? Icons.visibility : Icons.visibility_off, color: Theme.of(context).colorScheme.onPrimaryFixed, ), ) : null, ), onChanged: onChanged, );

Upvotes: 0

Mohd Aadil
Mohd Aadil

Reputation: 21

isDense: true,
    constraints: BoxConstraints(
        minHeight: 60, maxHeight: hasError ? (widget.height + 24) : 60),
    contentPadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 
    20),

Like That.. Without Adding Fixed Height Using SizedBox.

Upvotes: 0

Muzammil
Muzammil

Reputation: 18

To achieve a fixed height for the TextField while ensuring that the error message does not cause it to decrease in height, you can use a combination of SizedBox and constraints property of TextField.

Just give the maximum height to sizedbox in my case its 70 and give the minimum height to constraints: BoxConstraints(minHeight: 50),

Like this:

SizedBox(
                height: 70,
                width: 240,
                child: TextFormField(
                  autovalidateMode: AutovalidateMode.onUserInteraction,
                  controller: textController,
                  keyboardType: TextInputType.numberWithOptions(decimal: false,signed: false),
                  decoration: InputDecoration(
                    constraints: BoxConstraints(minHeight: 50),
                    contentPadding: EdgeInsets.symmetric(vertical: 13,horizontal: 18),
                      )
                  ),
                ),
              ),

Upvotes: 0

Aashis Shrestha
Aashis Shrestha

Reputation: 612

Problem explanation:

Since you have a TextFormField widget inside a Container with a fixed height:40, whenever you have an error, certain area is required for error message to show up. Hence, the TextFormField height shrinks to adjust both Input field and Error Message within the limits of your Container height(40).

Solution:

You can just remove the Container height parameter. This will wrap your TextFormField height to text size instead

Upvotes: 2

Sarath Molathoti
Sarath Molathoti

Reputation: 71

I tried all the above answers. Nothing worked. After a struggle for one day, I could able to achieve it.

To apply custom height to the TextFormField without shrinking its height even after the error text, We have to use a combination of constraints, contentPadding, and isDense.

TextFormField(
  controller: controller,//use your controller
  autovalidateMode: AutovalidateMode.onUserInteraction,
  decoration: InputDecoration(
    constraints: const BoxConstraints(maxHeight: 70, minHeight: 35),
    border: OutlineInputBorder(borderRadius: BorderRadius.circular(6)),
    contentPadding:const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
    hintText: hintText,
    isDense: true,
  ),
  validator: validator, //use your custom validator
),

I hope it helps. Happy Coding

Upvotes: 2

GG.
GG.

Reputation: 306

Instead of using a fixed height container to wrap the textFormField, You can try to put a space in the helper text so it will keep the height of the field constant while only displaying when there is an error.

return TextFormField(
   // ...
   decoration: InputDecoration(
     // ...
     helperText: " ",
     helperStyle: <Your errorStyle>,
   )
        

According to Flutter Doc :

To create a field whose height is fixed regardless of whether or not an error is displayed, either wrap the TextFormField in a fixed height parent like SizedBox, or set the InputDecoration.helperText parameter to a space.

Upvotes: 16

akshay toshniwal
akshay toshniwal

Reputation: 300

Above solutions did not work for me however I have figured out a very simple solution to avoid the above issue

TextFormField(
    decoration: InputDecoration(
      **errorStyle: const TextStyle(fontSize: 0.01),**
      errorBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(borderRadius),
        borderSide: const BorderSide(
          color: AppColor.neonRed,
          width: LayoutConstants.dimen_1,
          style: BorderStyle.solid,
        ),
      ),
   );

Catch in the above solution is that we are setting the size of the error message to 0.01 so as a result it don't show up.

Additionally we can have custom border for the error.

Note : Setting the Text size to 0 is not working as it don't consider the text size and textFormField widget gets shrinked.

Upvotes: 14

ammar188
ammar188

Reputation: 73

The problem with content padding is that you cant decrease the size of the field to UI requirement with an emphasize on decrease but how ever the second answer helped me come with a solution for my perticular problem, so am sharing that

StreamBuilder(
                    stream: viewModel.outEmailError,
                    builder: (context, snap) {
                      return Container(
                        width: MediaQuery.of(context).size.width*.7,
                        height: (snap.hasData)?55:35,
                        child: AccountTextFormField(
                          "E-mail",
                          textInputType: TextInputType.emailAddress,
                          focusNode: viewModel.emailFocus,
                          controller: viewModel.emailController,
                          errorText: snap.data,
                          textCapitalization: TextCapitalization.none,
                          onFieldSubmitted: (_) {
                            nextFocus(viewModel.emailFocus,
                                viewModel.passwordFocus, context);
                          },
                        ),
                      );
                    }),

Upvotes: 1

anmol.majhail
anmol.majhail

Reputation: 51196

In your Code - you need to comment out the 40 height given to each container.

Container(
             // height: 40.0,
              child: _createTextFormField(
                  loginEmailController,
                  Icons.alternate_email,
                  "Email Adress",
                  false,
                  TextInputType.emailAddress),
            ),
            Container(
            //  height: 40.0,
              child: _createTextFormField(loginPasswordController, Icons.lock,
                  "Password", true, TextInputType.text),
            ),

and then in your - TextFormField in InputDecoration, you can alter these value as per your liking.

  contentPadding:
      EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),

Upvotes: 43

Pule Maanela
Pule Maanela

Reputation: 65

The problem is that we are not able to see your code so it might be challenging to assist you but I will do everything from scratch. You can firstly create the authentication class in one dart file

class AuthBloc{
   StreamController _passController = new StreamController();
   Stream get passStream => _passController.stream;
   bool isValid(String pass){
       _passController.sink.add("");
       if(pass == null || pass.length < 6){
         _passController.sink.addError("Password is too short");
         return false;
       }
       else{
         return true;
       }
    }

    void dispose(){
      _passController.close();    
     }
}

And then insert the following code in another dart file...

class LoginPage extends StatefulWidget{
  @override
  _LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage>{
   AuthBloc authBloc = new AuthBloc();
   @override
   void dispose(){
     authBloc.dispose();
   }
   @override
   Widget build(BuildContext context){
     return Scaffold(
       body: Container(
         padding: EdgeInsets.fromLTRB(30, 0, 30, 0),
         constraints: BoxConstraints.expand(),
         children: <Widget>[
           Padding(
            padding: const EdgeInsets.fromLTRB(0, 40, 0, 20),
            child: StreamBuilder(
                stream: authBloc.passStream,
                builder: (context, snapshot) => TextField(
                  controller: _passController,
                  style: TextStyle(fontSize: 18, color: Colors.black),
                  decoration: InputDecoration(
                      errorText: snapshot.hasError ? snapshot.error:null,
                      labelText: "Password",
                      prefixIcon: Container(
                        width: 50,
                        child: Icon(Icons.lock),
                      ),
                      border: OutlineInputBorder(
                          borderSide: BorderSide(color: Color(0xffCED802), width: 1),
                          borderRadius: BorderRadius.all(Radius.circular(6))
                      )
                  ),
                ),
            )
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(0, 30, 0, 40),
            child: SizedBox(
              width: double.infinity,
              height: 52,
              child: RaisedButton(
                onPressed: _onLoginClicked,
                child: Text(
                  "Login",
                  style: TextStyle(fontSize: 18, color: Colors.white),
                ),
                color: Color(0xff327708),
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.all(Radius.circular(6))
                ),
              ),
            ),
          ),
         ]
       )
     )
   }
   _onLoginClicked(){
     var isValid = authBloc.isValid(_passController.text);
     if(isValid){
       //insert your action
     }
   }
}

I hope it works :)

Upvotes: 4

Related Questions