Widgets Básicos y de Layout en Flutter

  Widgets Básicos en Flutter

Los widgets básicos son los bloques de construcción fundamentales para crear interfaces de usuario. En Flutter, todo es un widget, desde un simple texto hasta layouts complejos.

Tipos de Widgets Básicos

Flutter ofrece widgets que se dividen en dos categorías principales según su capacidad de tener elementos hijos:

Widgets Sin Hijos

Estos widgets no contienen otros widgets dentro de ellos y se usan principalmente para espaciado, separación o indicadores.

SizedBox

Define un espacio de tamaño fijo. Es el widget más eficiente para crear espacios.

dart
SizedBox(
height: 20, // Espacio vertical
width: 100, // Espacio horizontal
)

// Solo altura
SizedBox(height: 20)

// Solo ancho  
SizedBox(width: 50)

Spacer

Crea un espacio flexible que se expande para llenar el espacio disponible.

dart
Row(
children: [
  Text('Inicio'),
  Spacer(), // Empuja el siguiente widget al final
  Text('Final'),
],
)

Divider

Muestra una línea horizontal para separar contenido visualmente.

dart
Column(
children: [
  Text('Elemento 1'),
  Divider(
    color: Colors.grey,
    thickness: 1,
    indent: 20, // Margen izquierdo
    endIndent: 20, // Margen derecho
  ),
  Text('Elemento 2'),
],
)

VerticalDivider

Similar a Divider, pero muestra una línea vertical.

dart
Row(
children: [
  Text('Izquierda'),
  VerticalDivider(
    color: Colors.blue,
    thickness: 2,
    width: 20,
  ),
  Text('Derecha'),
],
)

CircularProgressIndicator

Indicador de carga circular que no requiere un hijo.

dart
CircularProgressIndicator(
color: Colors.blue,
strokeWidth: 4.0,
)

// Con valor específico (0.0 a 1.0)
CircularProgressIndicator(
value: 0.7, // 70% completado
color: Colors.green,
)

LinearProgressIndicator

Indicador de progreso lineal sin hijos.

dart
LinearProgressIndicator(
color: Colors.blue,
backgroundColor: Colors.grey[300],
)

// Con progreso específico
LinearProgressIndicator(
value: 0.5, // 50% completado
color: Colors.orange,
)

Ejemplo Completo: Widgets Sin Hijos


A continuación, se muestra un ejemplo práctico que combina varios widgets sin hijos para crear una lista con espaciado y separadores.

dart
class WidgetsSinHijos extends StatelessWidget {

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Widgets Sin Hijos')),
    body: Column(
      children: [
        Text('Inicio de la lista'),
        SizedBox(height: 20), // Espacio fijo
        Divider(), // Línea divisoria
        CircularProgressIndicator(), // Indicador de carga
        SizedBox(height: 20),
        LinearProgressIndicator(value: 0.6),
        Spacer(), // Espacio flexible
        Text('Final de la lista'),
      ],
    ),
  );
}
}

Widgets de Layout (Con Hijos)


Estos widgets pueden contener otros widgets y se usan para organizar y estructurar la interfaz.

Column

Organiza sus hijos en una columna vertical.

dart
Column(
mainAxisAlignment: MainAxisAlignment.center, // Alineación vertical
crossAxisAlignment: CrossAxisAlignment.start, // Alineación horizontal
children: [
  Text('Elemento 1'),
  Text('Elemento 2'),
  Text('Elemento 3'),
],
)

Row

Organiza sus hijos en una fila horizontal.

dart
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // Distribución horizontal
crossAxisAlignment: CrossAxisAlignment.center, // Alineación vertical
children: [
  Icon(Icons.home),
  Text('Inicio'),
  Icon(Icons.arrow_forward),
],
)

Container

El widget más versátil. Puede contener un hijo y aplicar decoración, padding, margin, etc.

dart
Container(
width: 200,
height: 100,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
  color: Colors.blue,
  borderRadius: BorderRadius.circular(12),
  boxShadow: [
    BoxShadow(
      color: Colors.grey.withOpacity(0.3),
      spreadRadius: 2,
      blurRadius: 5,
    ),
  ],
),
child: Text(
  'Contenido',
  style: TextStyle(color: Colors.white),
),
)

Stack

Permite apilar widgets unos sobre otros.

dart
Stack(
children: [
  Container(
    width: 200,
    height: 200,
    color: Colors.blue,
  ),
  Positioned(
    top: 20,
    right: 20,
    child: Icon(
      Icons.star,
      color: Colors.yellow,
      size: 30,
    ),
  ),
  Positioned(
    bottom: 10,
    left: 10,
    child: Text(
      'Superpuesto',
      style: TextStyle(color: Colors.white),
    ),
  ),
],
)

Wrap

Organiza sus hijos en filas o columnas, envolviendo a la siguiente línea cuando es necesario.

dart
Wrap(
spacing: 8.0, // Espacio horizontal entre elementos
runSpacing: 4.0, // Espacio vertical entre filas
children: [
  Chip(label: Text('Flutter')),
  Chip(label: Text('Dart')),
  Chip(label: Text('Mobile')),
  Chip(label: Text('Development')),
  Chip(label: Text('Cross-platform')),
],
)

Ejemplo Práctico: Layout Completo

dart
class LayoutCompleto extends StatelessWidget {

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Layout con Widgets Básicos'),
      backgroundColor: Colors.blue,
    ),
    body: Column(
      children: [
        // Header con Row
        Container(
          padding: EdgeInsets.all(16),
          color: Colors.grey[100],
          child: Row(
            children: [
              Icon(Icons.person, color: Colors.blue),
              SizedBox(width: 12),
              Text(
                'Perfil de Usuario',
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
              Spacer(),
              Icon(Icons.settings),
            ],
          ),
        ),
        
        Divider(),
        
        // Contenido principal con Stack
        Expanded(
          child: Stack(
            children: [
              Container(
                width: double.infinity,
                color: Colors.blue[50],
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    CircularProgressIndicator(),
                    SizedBox(height: 20),
                    Text('Cargando contenido...'),
                  ],
                ),
              ),
              
              // Botón flotante personalizado
              Positioned(
                bottom: 20,
                right: 20,
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.blue,
                    borderRadius: BorderRadius.circular(30),
                  ),
                  child: IconButton(
                    icon: Icon(Icons.add, color: Colors.white),
                    onPressed: () {},
                  ),
                ),
              ),
            ],
          ),
        ),
        
        // Footer con Wrap
        Container(
          padding: EdgeInsets.all(16),
          child: Wrap(
            spacing: 8,
            children: [
              Chip(label: Text('Tag 1')),
              Chip(label: Text('Tag 2')),
              Chip(label: Text('Tag 3')),
            ],
          ),
        ),
      ],
    ),
  );
}
}

Propiedades Importantes de Layout


MainAxisAlignment

Controla la alineación en el eje principal (vertical para Column, horizontal para Row):

  Nota

  • MainAxisAlignment.start - Al inicio
  • MainAxisAlignment.center - Al centro
  • MainAxisAlignment.end - Al final
  • MainAxisAlignment.spaceEvenly - Espacio uniforme
  • MainAxisAlignment.spaceBetween - Espacio entre elementos
  • MainAxisAlignment.spaceAround - Espacio alrededor de elementos

CrossAxisAlignment

Controla la alineación en el eje transversal:

  Conclusión

  • CrossAxisAlignment.start - Al inicio
  • CrossAxisAlignment.center - Al centro
  • CrossAxisAlignment.end - Al final
  • CrossAxisAlignment.stretch - Estirar para llenar

  Nota

Tip de rendimiento: Usa SizedBox en lugar de Container vacío para espaciado. Es más eficiente y consume menos recursos.

  Nota

Combina estos widgets básicos para crear layouts complejos. Domina Column, Row y Container antes de avanzar a widgets más especializados.

Buenas Prácticas


A continuación, se presentan algunas buenas prácticas para utilizar widgets básicos de layout en Flutter:

Espaciado Consistente

dart
// Usa constantes para espaciado consistente
class AppSpacing {
static const double small = 8.0;
static const double medium = 16.0;
static const double large = 24.0;
}

// Uso
SizedBox(height: AppSpacing.medium)

Widgets Reutilizables


Crea widgets personalizados para reutilizar en diferentes partes de la aplicación. Esto mejora la mantenibilidad y reduce la duplicación de código.

dart
class CustomCard extends StatelessWidget {
final String title;
final Widget child;

const CustomCard({
  Key? key,
  required this.title,
  required this.child,
}) : super(key: key);


Widget build(BuildContext context) {
  return Container(
    margin: EdgeInsets.all(AppSpacing.small),
    padding: EdgeInsets.all(AppSpacing.medium),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(12),
      boxShadow: [
        BoxShadow(
          color: Colors.grey.withOpacity(0.1),
          spreadRadius: 1,
          blurRadius: 3,
        ),
      ],
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
        SizedBox(height: AppSpacing.small),
        child,
      ],
    ),
  );
}
}