Validator error message changes TextFormField's height - flutter

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 :
and 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;
},
);
}

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),

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.

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 :)

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.

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);
},
),
);
}),

Related

Flutter textfield disable prefix icon on text changed

I have a text field widget that has an prefix icon . I want the prefix icon to be hidden when the text field is changed
my code:
TextField(
controller: messageInputController,
onChanged: (value){
messageInputChanged();
},
decoration: InputDecoration(
counterText: '',
prefixIcon: !showPrefixIcon ? Container() : Padding(
padding: const EdgeInsets.all(0),
child: IconButton(
onPressed: () {
imagePickerBottomSheet();
},
iconSize: 40,
color: Skin.gray,
icon: SvgPicture.asset(
'assets/svg/ic-image.svg',
height: 22,
color: Skin.gray,
)),
),
),
),
void messageInputChanged() {
if(messageInputController.text.isEmpty){
showPrefixIcon = true;
}else {
showPrefixIcon = false;
}
setState(() {});
}
But when the set state is called and the icon is hidden, the contents of the text field are also messed up
The InputDecorator accepts a Widget? for the prefixIcon, however, it does not work with a Container(). That's strange.
If I replace your
prefixIcon: !showPrefixIcon ? Container() : Padding(...)
with
prefixIcon: !showPrefixIcon ? SizedBox(height: 0.0, width: 0.0) : Padding(...)
it works.
I honestly don't know why.
updated proposal:
Use:
prefixIcon: !showPrefixIcon ? null : Padding(...)
And I just assume that you could then reasonably argue that this is ugly. Then, I fear, you have to change the way of doing it to something like this:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 70.0,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
),
borderRadius: const BorderRadius.all(Radius.circular(10))),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 600),
transitionBuilder: (child, animation) => SizeTransition(
sizeFactor: animation,
axis: Axis.horizontal,
child: child),
child: showPrefixIcon
? const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.abc,
color: Colors.blue, size: 46),
)
: Container()),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: messageInputController,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'enter text',
),
onChanged: (value) {
messageInputChanged();
},
),
),
),
],
),
),
),
),
);
}
}

How to get static container background but scrollable loginform?

I want to scroll the loginform but NOT the background, but i noticed that the cointaner that contains these "circles" are moved to the up when keyboards appears, so i added
resizeToAvoidBottomInset: true,
To the scaffold, but now i cant scroll anything in the loginform and thats is what i dont want, i want to scroll ONLY the loggin form..
Here is the screenshot of the emulator, just click this
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:landscapes/bloc/inherited_provider.dart';
import 'package:landscapes/bloc/login_bloc.dart';
import 'package:landscapes/pages/register_page.dart';
class LoginPage extends StatefulWidget {
LoginPage({Key key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: Form(
key: formKey,
child: Stack(
children: <Widget>[
_crearFondo(),
_loginForm(context),
],
),
));
}
Widget _crearFondo() {
Size size = MediaQuery.of(context).size;
final fondoGris = Container(
height: size.height, width: size.width, color: Colors.grey[850]);
final circulo = Container(
width: 90.0,
height: 90.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
color: Color.fromRGBO(255, 255, 255, 0.05)),
);
return Stack(
children: <Widget>[
fondoGris,
Positioned(top: 50.0, left: 30.0, child: circulo),
Positioned(top: 150.0, right: 50.0, child: circulo),
Positioned(bottom: 180.0, right: 20.0, child: circulo),
Positioned(bottom: 280.0, left: 40.0, child: circulo),
],
);
}
Widget _loginForm(context) {
final bloc = InheritedProvider.loginBlocInherit(context);
final size = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Column(
children: <Widget>[
SafeArea(child: Container(height: size.height * 0.05)),
SizedBox(height: size.height * 0.05),
welcomeBackForm(),
SizedBox(height: 55.0),
_formEmail(bloc),
SizedBox(height: 30.0),
_formPassword(),
SizedBox(height: 38),
_button(),
SizedBox(height: 50),
_crearCuenta(),
],
),
);
}
//MENSAJE BIENVENIDA
Widget welcomeBackForm() {
return Container(
alignment: Alignment.topLeft,
margin: EdgeInsets.only(left: 40.0),
child: Text(
'Hello!\nWelcome back',
style: GoogleFonts.playfairDisplay(
fontSize: 30, fontWeight: FontWeight.w600, color: Colors.white),
),
);
}
//TEXTFORMFIELD DEL EMAIL
Widget _formEmail(LoginBloc bloc) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 30.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
icon: Icon(
Icons.email,
color: Colors.white,
),
hintText: 'Email adress',
filled: true,
fillColor: Colors.grey[600],
enabledBorder: OutlineInputBorder(borderSide: BorderSide.none),
border: OutlineInputBorder(borderSide: BorderSide.none)),
style: TextStyle(height: 1),
validator: (value) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(pattern);
if (regExp.hasMatch(value)) {
return null;
} else {
return ('El email no es correcto');
}
},
),
);
}
//TEXTFORMFIELD DEL PASSWORD
Widget _formPassword() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 30.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
icon: Icon(
Icons.lock,
color: Colors.white,
),
hintText: 'Password',
filled: true,
fillColor: Colors.grey[600],
enabledBorder: OutlineInputBorder(borderSide: BorderSide.none),
border: OutlineInputBorder(borderSide: BorderSide.none),
),
style: TextStyle(height: 1),
),
);
}
//BOTON INGRESAR
Widget _button() {
return RaisedButton(
color: Color.fromRGBO(254, 200, 140, 1),
padding: EdgeInsets.symmetric(horizontal: 90.0, vertical: 15.0),
child: Text('ingresar'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
elevation: 0.0,
onPressed: () => _submit(),
);
}
//FLATBUTTON DE CREAR CUENTA
Widget _crearCuenta() {
return FlatButton(
onPressed: () => Navigator.pushReplacementNamed(context, 'registerpage'),
child: Text(
'Crear una nueva cuenta',
style: TextStyle(fontSize: 14),
),
textColor: Colors.white,
);
}
//SUBMIT DEL BOTON
void _submit() async {
if (!formKey.currentState.validate()) return null;
formKey.currentState.save();
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => RegisterPage()));
// Navigator.pushReplacementNamed(context, HomePage.routName).then((value) { setState(() { });});
}
}
You may try wrapping your _loginForm with SingleChildScrollView as follows :
SingleChildScrollView(child: _loginForm(context)),
By doing so the widgets within _crearFondo() will not scroll, however the widgets within _loginForm will scroll.
Please review the Flutter Documentation for SingleChildScrollView
SingleChildScrollView
A box in which a single widget can be scrolled.
This widget is useful when you have a single box that will normally be
entirely visible, for example a clock face in a time picker, but you
need to make sure it can be scrolled if the container gets too small
in one axis (the scroll direction).
It is also useful if you need to shrink-wrap in both axes (the main
scrolling direction as well as the cross axis), as one might see in a
dialog or pop-up menu. In that case, you might pair the
SingleChildScrollView with a ListBody child.
When you have a list of children and do not require cross-axis
shrink-wrapping behavior, for example a scrolling list that is always
the width of the screen, consider ListView, which is vastly more
efficient that a SingleChildScrollView containing a ListBody or Column
with many children.

Validator for a Custom InputTextField

I have created my own custom textinputfield, everything do has come up well but the thing is how to give that a validator upon the button click?
This is my custom inputfield,
class CustomInputField extends StatelessWidget {
bool _validate = false;
Icon fieldIcon;
String hintText;
TextInputType textType;
CustomInputField(this.fieldIcon, this.hintText, this.textType);
#override
Widget build(BuildContext context) {
return Container(
width: 300,
child: Material(
elevation: 5.0,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
color: Colors.deepOrange,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(12.0),
child: fieldIcon,
),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(topRight: Radius.circular(10.0), bottomRight: Radius.circular(10.0)),
),
width: 250,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _text,
decoration: InputDecoration(
border: InputBorder.none,
hintText: hintText,
fillColor: Colors.white,
errorText: _validate? 'Value can\'t be empty' : null,
filled: true,
),
keyboardType: textType,
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
),
),
),
),
],
)
),
);
}
}
And this is how I'm calling that in different pages
CustomInputField(
Icon(
Icons.lock,
color: Colors.white,
),
'Password',
TextInputType.visiblePassword),
So upon click of the button if the text field is empty, i need to give in the errorText, so could anyone help me out?
You need to do a couple of things.
Create a GlobalKey type FormState for your Stateful Widget.
GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Create a TextFormField with a validator and onSave callback.
Form(
key: _formKey, //Give you form it's key.
child: Column(
children: <Widget>[
TextFormField(
validator: (input) {
if (input.isEmpty) { //Here I check if the field data is empty.
return "Field cannot be empty.;
}
},
//Update what ever variable you want with the validated data.
//Here I picked a variable named foo.
onSaved: (input) => foo = input,
],
),
),
Make a button that uses the _formKey to call the validate function and save function.
RaisedButton(
child: Text("Do Something"),
onPressed: () {
//Validate will run on the TextFormField and return the error provided.
if (_formKey.currentState.validate()) {
_formKey.currentState.save(); //If validated, we will update our foo variable.
}
},
),

Widgets shifted down when error shows for TextFormField on button's press

I have created a login page in Flutter. I have used SingleChildScrollView to make this page scrollable.
I have used the form validation to validate the username and password text form fields.
The problem is when error messages displayed below TextFormField, the login button is shifted down.
I have also used ListView to enable the scrolling but the behavior is the same as far as shifting is concerned.
I am new to Flutter and seeking help in this.
I am providing the code and screenshot below.
Thank you.
Screenshot
Login Page
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'login_form.dart';
class LoginPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(child: LoginForm()),
);
}
}
Login Form
import 'package:flutter/material.dart';
class LoginForm extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _LoginFormState();
}
}
class _LoginFormState extends State<LoginForm> {
TextStyle style = TextStyle(fontFamily: 'Montserrat', fontSize: 20.0);
OutlineInputBorder textFieldBorder = OutlineInputBorder(
borderRadius: BorderRadius.circular(32.0),
borderSide: BorderSide(color: const Color(0xff01d277)));
final formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
/// Username field
final usernameField = TextFormField(
obscureText: false,
style: style,
validator: (value) {
if (value.isEmpty) return 'Please enter username';
return null;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: 'Username',
border: textFieldBorder,
));
/// Password field
final passwordField = TextFormField(
obscureText: true,
style: style,
validator: (value) {
if (value.isEmpty) return 'Please enter password';
return null;
},
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: 'Password',
border: textFieldBorder,
));
/// Login button
final loginButton = Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(30.0),
color: Color(0xff01d277),
child: MaterialButton(
minWidth: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
if (formKey.currentState.validate())
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Done!')));
},
child: Text("Login",
textAlign: TextAlign.center,
style: style.copyWith(
color: Colors.white, fontWeight: FontWeight.bold)),
),
);
return Padding(
padding: const EdgeInsets.all(36.0),
child: Form(
key: formKey,
child: Column(
children: <Widget>[
SizedBox(
height: 150,
child: Image.asset(
'lib/assets/images/logo_dark.png',
fit: BoxFit.contain,
),
),
SizedBox(height: 45.0),
usernameField,
SizedBox(
height: 45.0,
),
passwordField,
SizedBox(
height: 45.0,
),
loginButton,
],
)));
}
}
Use ConstrainedBox with fixed maxHeight and minHeight
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 70.0,
minHeight: 70.0,
child: TextFormField(...),
),
);
Use 'Container' widget to set full height.
Container(
width: double.infinity,
height: 73, //(text field height + error label height)
child: TextFormField(<your Text Form Field>)
)
Try wrapping the usernameField and passwordField in an Expanded widget
Expanded(child: usernameField),
SizedBox(
height: 45.0,
),
Expanded(child: passwordField),

Row with multiple TextFields and a DropDown where one TextField should be be bigger, and all should have same height

I have a Row that has 3 fields in it: 2 TextFields, 1 DropdownButtonHideUnderline wrapped in a Container. I'm trying to ensure that the first TextField takes up about 50-60% and the other two fields share the remaining space. I also want the fields to have the same height. So, something like this:
This is the code I have:
#override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).accentColor,
child: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 10.0, 5.0, 10.0),
child: Row(children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(right: 5.0),
child: TypeAheadField(
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
controller: widget.ingredientController,
style: DefaultTextStyle.of(context)
.style
.copyWith(fontStyle: FontStyle.italic),
decoration: InputDecoration(
border: InputBorder.none,
filled: true,
fillColor: Colors.white.withOpacity(1),
hintText: 'Ingredient',
suffixIcon: GestureDetector(
onTap: widget.addFunction,
child: Icon(
Icons.add,
color: Colors.grey,
)))),
suggestionsCallback: (pattern) async {
return await _findIngredients(pattern);
},
//If not items are found, return an empty container.
noItemsFoundBuilder: (context) {
return Container(height: 0, width: 0);
},
itemBuilder: (context, suggestion) {
return ListTile(
title: Text(suggestion.name),
);
},
onSuggestionSelected: (Ingredient suggestion) {
widget.ingredientController.text = suggestion.name;
},
))),
Expanded(
child: TextField(
maxLines: 1,
controller: widget.quantityController,
keyboardType: TextInputType.text,
autofocus: false,
decoration: InputDecoration(
border: InputBorder.none,
filled: true,
fillColor: Colors.white.withOpacity(1),
hintText: 'Qty',
))),
Expanded(flex: 1, child: UnitDropdown()),
])));
}
What I'm left with is this:
I've tried setting the flex factor on the Expanded to different things, but that just results in an overflow on the right side. I've also not found a way to force all of the widgets to have the same height.
Try this one,
Row(children : <Widget>[
Expanded(
Container(child: TextField1())
),
Expanded(
Row(children : <Widget>[
Expanded(child: TextFiled2()),
Expanded(child: DropDown())
]);
)
]);
Or else, You can use MediaQuery to get the exact size of the screen.
Example:
Width : MediaQuery.of(context).size.width * 0.5;
Width : MediaQuery.of(context).size.width * 0.25;
Width : MediaQuery.of(context).size.width * 0.25;
Here you have, the key is to encapsulate the TextField, Dropdown, or whatever component you have in a Container, and define the actual size from the Container, tweaking the predefined internal Paddings of each widget (if you have the chance, sometimes is not available).
double itemsHeight = 30;
Widget getTextField({String hint = 'Ingredients', Widget suffix}) {
// use Container to define the size of the child,
// and reset the original inner paddings!
return Container(
height: itemsHeight,
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: Colors.white, width: 1)),
hintText: hint,
contentPadding: EdgeInsets.all(
0), // change each value, and set 0 remainding ones.
suffixIcon: suffix,
),
expands: false,
maxLines: 1,
controller: TextEditingController(),
),
);
}
return Scaffold(
body: Container(
color: Colors.green.withOpacity(.2),
margin: EdgeInsets.symmetric(vertical: 50, horizontal: 20),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
flex: 2,
child: getTextField(
hint: 'Ingredients',
suffix: Icon(
Icons.add,
size:
18, // option 1: reduce the size of the icon, and avoid the padding issues..
)),
),
Flexible(
flex: 1,
child: getTextField(
hint: 'Qty',
// option2: trick to match the expanded height of the icon on the previous field
// make an icon transparent :)
suffix: Icon(
Icons.account_box,
color: Colors.transparent,
)),
),
Flexible(
flex: 1,
child: Container(
// use this to match the Flex size..., is like using Expanded.
width: double.infinity,
// container defines the BoxConstrains of the children
decoration: BoxDecoration(
color: Colors.white24,
border: Border.all(color: Colors.white, width: 1),
),
height: itemsHeight,
child: DropdownButton(
hint: Text("Unit"),
onChanged: (i) {},
underline: Container(),
items: List.generate(5, (i) {
return DropdownMenuItem(child: Text("item $i"));
})),
),
),
],
),
),
);
screenshot of result: