En Flutter, construyes layouts combinando widgets de diseño como Column, Row, Container, Padding y Stack para organizar elementos en pantalla.
Cada pantalla de tu app es un árbol de widgets. Tú decides cómo se acomodan, qué espacio ocupan y cómo responden al contenido.
Los cinco widgets de layout esenciales
Antes de escribir código, necesitas conocer los cinco widgets que usarás en casi todas las pantallas:
| Widget | ¿Para qué sirve? |
|---|---|
Column |
Apila widgets de arriba hacia abajo |
Row |
Acomoda widgets de izquierda a derecha |
Container |
Caja con tamaño, color, bordes y padding |
Padding |
Agrega espacio alrededor de un widget |
Stack |
Superpone widgets uno encima de otro |
Estos cinco son la base. Todo lo demás se construye combinándolos.
Column y Row: el eje principal
Column organiza sus hijos en el eje vertical. Row los organiza en el eje horizontal.
Ambos comparten las mismas propiedades de alineación:
mainAxisAlignment: controla el eje principal (vertical enColumn, horizontal enRow).crossAxisAlignment: controla el eje cruzado (horizontal enColumn, vertical enRow).
Ejemplo 1 — Tarjeta de producto de Mercado Libre
Imagina una tarjeta sencilla con el nombre del producto, el precio y un botón de compra:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Audífonos Bluetooth',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
'$850',
style: TextStyle(fontSize: 16, color: Colors.green),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {},
child: const Text('Agregar al carrito'),
),
],
)
crossAxisAlignment.start alinea todo al borde izquierdo. SizedBox crea espacio vertical entre elementos.
Ejemplo 2 — Fila de íconos de navegación
Una barra inferior con íconos, como la de Liverpool, usa Row con mainAxisAlignment.spaceAround:
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
Icon(Icons.home, size: 28),
Icon(Icons.search, size: 28),
Icon(Icons.favorite, size: 28),
Icon(Icons.person, size: 28),
],
)
spaceAround distribuye el espacio libre de forma uniforme entre los íconos.
Container: la caja multipropósito
Container es el widget más flexible para dar forma, color y espaciado a un área de la pantalla.
Sus propiedades más usadas son:
widthyheight: tamaño fijo en píxeles lógicos.color: color de fondo.padding: espacio interior entre el borde y el contenido.margin: espacio exterior alrededor del contenedor.decoration: bordes redondeados, sombras y gradientes.
Ejemplo 3 — Banner de promoción de Bimbo
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.yellow.shade700,
borderRadius: BorderRadius.circular(12),
),
child: const Text(
'¡Oferta del día! Pan Blimpy al 2x1',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
)
double.infinity hace que el contenedor ocupe todo el ancho disponible. BorderRadius.circular(12) redondea las esquinas.
Nota importante: No puedes usar
colorydecorational mismo tiempo en unContainer. Si usasBoxDecoration, define el color dentro de ella.
Padding: espacio limpio y preciso
Padding agrega espacio interior alrededor de su hijo sin necesidad de un Container.
Es más ligero que Container cuando solo necesitas espacio, sin color ni bordes.
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: const Text('Bienvenido a FEMSA Digital'),
)
Usa EdgeInsets.symmetric para aplicar el mismo valor en ambos lados del eje. Usa EdgeInsets.only para controlar cada lado por separado.
Comparación: Padding vs Container con padding
| Situación | Usa |
|---|---|
| Solo necesitas espacio | Padding |
| Necesitas espacio + color + bordes | Container |
| Necesitas tamaño fijo | Container |
Stack: capas superpuestas
Stack coloca sus hijos uno encima del otro, como capas. El último hijo en la lista queda al frente.
Es ideal para:
- Badges de notificación sobre íconos.
- Textos sobre imágenes.
- Botones flotantes personalizados.
Ejemplo 4 — Badge de carrito de compras
Stack(
children: [
const Icon(Icons.shopping_cart, size: 36),
Positioned(
right: 0,
top: 0,
child: Container(
width: 16,
height: 16,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Center(
child: Text(
'3',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
),
),
],
)
Positioned es el widget compañero de Stack. Te permite colocar un hijo en una posición exacta usando top, bottom, left y right.
Pantalla completa: combinando todos los widgets
Ahora construye una pantalla real que simula la vista de un producto en una app de Mercado Libre.
import 'package:flutter/material.dart';
class PantallaProducto extends StatelessWidget {
const PantallaProducto({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Detalle del producto')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
Container(
width: double.infinity,
height: 200,
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(12),
),
child: const Icon(Icons.headphones, size: 80, color: Colors.grey),
),
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'En stock',
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
),
],
),
const SizedBox(height: 16),
const Text(
'Audífonos Bluetooth Pro',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
Text(
'$1,299',
style: TextStyle(fontSize: 24, color: Colors.green, fontWeight: FontWeight.bold),
),
Text(
'4.8 ⭐',
style: TextStyle(fontSize: 16),
),
],
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: const Text('Comprar ahora'),
),
),
],
),
),
);
}
}
Este ejemplo combina Padding, Column, Stack, Positioned, Container, Row y SizedBox en una sola pantalla funcional.
Errores comunes
Error 1 — Column sin scroll cuando el contenido es largo.
Si agregas muchos widgets en una Column, Flutter lanza un error de overflow. Envuelve la Column en un SingleChildScrollView para hacerla scrolleable.
SingleChildScrollView(
child: Column(
children: [ /* muchos widgets */ ],
),
)
Error 2 — Usar color y decoration juntos en Container.
Esto lanza un error en tiempo de ejecución. Elige uno o el otro. Si necesitas bordes redondeados con color, usa BoxDecoration y define el color dentro de ella.
Error 3 — Olvidar Expanded o Flexible dentro de Row y Column.
Si un hijo intenta ocupar más espacio del disponible, Flutter lanza overflow. Usa Expanded para que un hijo llene el espacio restante:
Row(
children: [
const Text('Precio:'),
Expanded(
child: Text('$18,500', textAlign: TextAlign.end),
),
],
)
Error 4 — Anidar Column dentro de Column sin restricciones.
Una Column dentro de otra Column puede causar problemas de altura infinita. Si necesitas anidar columnas, envuelve la interna en un SizedBox con altura fija o usa Flexible.
Referencia rápida de alineaciones
| Valor | Comportamiento |
|---|---|
MainAxisAlignment.start |
Agrupa al inicio del eje principal |
MainAxisAlignment.center |
Centra en el eje principal |
MainAxisAlignment.end |
Agrupa al final del eje principal |
MainAxisAlignment.spaceBetween |
Espacio entre elementos, sin margen en extremos |
MainAxisAlignment.spaceAround |
Espacio alrededor de cada elemento |
MainAxisAlignment.spaceEvenly |
Espacio igual entre todos, incluidos los extremos |
CrossAxisAlignment.start |
Alinea al inicio del eje cruzado |
CrossAxisAlignment.center |
Centra en el eje cruzado |
CrossAxisAlignment.stretch |
Estira los hijos para llenar el eje cruzado |
Puntos clave para recordar
ColumnyRowcontrolan la dirección del flujo. Dominarlos es dominar el 80% del layout en Flutter.Containeres la herramienta de diseño más flexible: combina tamaño, color, bordes y espacio en un solo widget.Paddinges más ligero queContainercuando solo necesitas espacio sin decoración visual.StackconPositionedte permite superponer capas y colocar elementos en posiciones exactas.- Usa
ExpandedoFlexiblepara evitar errores de overflow cuando los hijos de unaRowoColumnocupan demasiado espacio.