I have the following Flutter component that renders some fields inside a form:
import 'package:flutter/material.dart';
import 'package:frontend/data/models/person_model.dart';
import 'package:frontend/data/models/product.dart';
import 'package:frontend/services/my_store.dart';
import 'package:provider/provider.dart';
class ClientForm extends StatefulWidget {
const ClientForm({this.client, super.key});
final Person? client;
@override
State<ClientForm> createState() => _ClientFormState();
}
class _ClientFormState extends State<ClientForm> {
@override
Widget build(BuildContext context) {
final List<Product> products = Provider.of<GymmanStore>(context).products;
final appColors = Provider.of<GymmanStore>(context).appColors;
final levels = ["Básico", "Intermedio", "Avanzado"];
final closeDays = List<int>.generate(31, (i) => i + 1).toList().map((int day) => day.toString()).toList();
return Scaffold(
appBar: AppBar(
title: widget.client == null?
Text('Nuevo Cliente'):
Text('Editar Cliente'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Form(
child: Column(
children: [
TextFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Nombre',
hintText: 'Nombre del cliente',
),
),
TextFormField(
style: TextStyle(
color: appColors.textColor,
),
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: 'Teléfono',
hintText: 'Teléfono del cliente',
),
),
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Producto',
hintText: 'Producto adquirido',
),
items: products.map((Product product) {
return DropdownMenuItem(
value: product,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
product.name
),
);
}).toList(),
onChanged: (value) {},
),
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Nivel',
hintText: 'Nivel del cliente.',
),
items: levels.map((String level) {
return DropdownMenuItem(
value: level,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
level
),
);
}).toList(),
onChanged: (value) {},
),
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Día de Cierre',
hintText: 'Día en que se vence el pago del cliente.',
),
items: closeDays.map((String closeDay) {
return DropdownMenuItem(
value: closeDay,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
closeDay
),
);
}).toList(),
onChanged: (value) {},
),
TextButton(
onPressed: () {
// Save the client
},
child: const Text('Guardar'),
),
],
)
),
],
),
),
);
}
}
The script runs with no problem, but I want two of the fields to be in the same row in order to save space, so I added a Row widget and placed both fields in it, like this:
return Scaffold(
appBar: AppBar(
title: widget.client == null?
Text('Nuevo Cliente'):
Text('Editar Cliente'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Form(
child: Column(
children: [
TextFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Nombre',
hintText: 'Nombre del cliente',
),
),
TextFormField(
style: TextStyle(
color: appColors.textColor,
),
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: 'Teléfono',
hintText: 'Teléfono del cliente',
),
),
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Producto',
hintText: 'Producto adquirido',
),
items: products.map((Product product) {
return DropdownMenuItem(
value: product,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
product.name
),
);
}).toList(),
onChanged: (value) {},
),
Row( <<< ------------- New widget
children: [
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Nivel',
hintText: 'Nivel del cliente.',
),
items: levels.map((String level) {
return DropdownMenuItem(
value: level,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
level
),
);
}).toList(),
onChanged: (value) {},
),
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Día de Cierre',
hintText: 'Día en que se vence el pago del cliente.',
),
items: closeDays.map((String closeDay) {
return DropdownMenuItem(
value: closeDay,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
closeDay
),
);
}).toList(),
onChanged: (value) {},
),
]
),
TextButton(
onPressed: () {
// Save the client
},
child: const Text('Guardar'),
),
],
)
),
],
),
),
);
}
}
The problem is that, as soon as I wrap the fields inside the Row widget, I get the this error:
════════ Exception caught by rendering library ═════════════════════════════════
RenderBox was not laid out: RenderTransform#74346 NEEDS-LAYOUT NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 2176 pos 12: 'hasSize'
I found this answer and tried adding an Expanded widget, like this:
Expanded(
child: Row(
children: [
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Nivel',
hintText: 'Nivel del cliente.',
),
items: levels.map((String level) {
return DropdownMenuItem(
value: level,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
level
),
);
}).toList(),
onChanged: (value) {},
),
DropdownButtonFormField(
style: TextStyle(
color: appColors.textColor,
),
decoration: InputDecoration(
labelText: 'Día de Cierre',
hintText: 'Día en que se vence el pago del cliente.',
),
items: closeDays.map((String closeDay) {
return DropdownMenuItem(
value: closeDay,
child: Text(
style: TextStyle(
color: appColors.dialogTextColor,
),
closeDay
),
);
}).toList(),
onChanged: (value) {},
),
]
),
)
And I also tried wrapping the whole column with an Expanded wrapper, but the error persists.
Try to wrap each of DropdownButtonFormField
inside the Row
widget with IntrinsicWidth
.
This answer might be helpful.
(Not related to your question, the first Column
widget looks unnecessary.)