¿Qué es un Widget en Flutter?

  ¿Qué es un Widget?

En Flutter, todo es un Widget. Un Widget es un componente inmutable que describe parte de la interfaz de usuario. Los widgets son los bloques de construcción fundamentales que definen tanto la estructura como el estilo de tu aplicación.

Filosofía "Everything is a Widget"

En Flutter, desde un simple texto hasta layouts complejos, todo se construye con widgets:

  Flutter Widgets Básicos

  • Elementos visuales: Text, Image, Icon
  • Layouts: Column, Row, Stack, Container
  • Interactivos: Button, TextField, Slider
  • Estilización: Padding, Margin, Theme
  • Navegación: Scaffold, AppBar, Drawer

Inmutabilidad de Widgets

Los widgets son inmutables, lo que significa que una vez creados, no pueden cambiar. Para actualizar la UI, Flutter crea nuevos widgets y reemplaza los antiguos.

dart
// ❌ Esto NO es posible - los widgets son inmutables
Text miTexto = Text('Hola');
miTexto.data = 'Nuevo texto'; // Error!

// ✅ Forma correcta - crear un nuevo widget
Text('Hola')  // Widget original
Text('Nuevo texto')  // Nuevo widget

Tipos de Widgets Principales


StatelessWidget

Widgets que no cambian durante su ciclo de vida. Son ideales para UI estática.

dart
class MiWidgetEstatico extends StatelessWidget {
final String titulo;

const MiWidgetEstatico({Key? key, required this.titulo}) : super(key: key);


Widget build(BuildContext context) {
  return Container(
    padding: EdgeInsets.all(16),
    child: Text(
      titulo,
      style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
    ),
  );
}
}

// Uso
MiWidgetEstatico(titulo: 'Texto que no cambia')

StatefulWidget

Widgets que pueden cambiar su estado interno y reconstruir la UI.

dart
class Contador extends StatefulWidget {

_ContadorState createState() => _ContadorState();
}

class _ContadorState extends State<Contador> {
int _contador = 0;

void _incrementar() {
  setState(() {
    _contador++;
  });
}


Widget build(BuildContext context) {
  return Column(
    children: [
      Text('Contador: $_contador'),
      ElevatedButton(
        onPressed: _incrementar,
        child: Text('Incrementar'),
      ),
    ],
  );
}
}

Características Clave de los Widgets


Árbol de Widgets (Widget Tree)

Flutter organiza los widgets en una estructura jerárquica llamada Widget Tree:

dart
MaterialApp(                    // Raíz
home: Scaffold(               // Estructura principal
  appBar: AppBar(             // Barra superior
    title: Text('Mi App'),    // Texto del título
  ),
  body: Center(               // Centrar contenido
    child: Column(            // Columna vertical
      children: [
        Text('¡Hola!'),       // Primer elemento
        ElevatedButton(       // Segundo elemento
          onPressed: () {},
          child: Text('Botón'),
        ),
      ],
    ),
  ),
),
)

Método build()

El corazón de todo widget. Define cómo se ve el widget:

dart

Widget build(BuildContext context) {
// Este método se llama cada vez que el widget necesita reconstruirse
return Container(
  width: 200,
  height: 100,
  color: Colors.blue,
  child: Center(
    child: Text('Widget personalizado'),
  ),
);
}

setState() - Actualización de Estado

Método para notificar a Flutter que el estado ha cambiado:

dart
class MiWidget extends StatefulWidget {

_MiWidgetState createState() => _MiWidgetState();
}

class _MiWidgetState extends State<MiWidget> {
bool _visible = true;

void _toggleVisibilidad() {
  setState(() {  // ← Notifica que algo cambió
    _visible = !_visible;
  });
}


Widget build(BuildContext context) {
  return Column(
    children: [
      if (_visible) Text('¡Soy visible!'),
      ElevatedButton(
        onPressed: _toggleVisibilidad,
        child: Text(_visible ? 'Ocultar' : 'Mostrar'),
      ),
    ],
  );
}
}

Widgets Básicos Esenciales


Widgets de Texto y Contenido

El Text widget se utiliza para mostrar texto en la UI. Puedes personalizar su apariencia con estilos, como tamaño de fuente, color y peso.

dart
// Text - Mostrar texto
Text(
  'Hola Flutter',
  style: TextStyle(
    fontSize: 24,
    color: Colors.blue,
    fontWeight: FontWeight.bold,
  ),
)

// Image - Mostrar imágenes
Image.network('https://ejemplo.com/imagen.jpg')
Image.asset('assets/images/logo.png')

// Icon - Iconos predefinidos
Icon(
Icons.favorite,
color: Colors.red,
size: 30,
)

Widgets de Layout

dart
// Container - Caja con estilo
Container(
width: 200,
height: 100,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
  color: Colors.blue,
  borderRadius: BorderRadius.circular(12),
),
child: Text('Contenido'),
)

// Column - Organizar verticalmente
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('Elemento 1'),
    Text('Elemento 2'),
    Text('Elemento 3'),
  ],
)

// Row - Organizar horizontalmente
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
  Icon(Icons.home),
  Icon(Icons.search),
  Icon(Icons.settings),
],
)

Widgets Interactivos

Los widgets interactivos permiten a los usuarios interactuar con tu app. Ejemplos incluyen botones, campos de texto y switches.

dart
// ElevatedButton - Botón elevado
ElevatedButton(
onPressed: () {
  print('Botón presionado');
},
child: Text('Presionar'),
)

// TextField - Campo de texto
TextField(
decoration: InputDecoration(
  labelText: 'Ingresa tu nombre',
  border: OutlineInputBorder(),
),
onChanged: (valor) {
  print('Texto: $valor');
},
)

// Switch - Interruptor
Switch(
value: true,
onChanged: (valor) {
  print('Switch: $valor');
},
)

Ejemplo Práctico: Tarjeta de Usuario


dart
class TarjetaUsuario extends StatelessWidget {
final String nombre;
final String email;
final String avatarUrl;

const TarjetaUsuario({
  Key? key,
  required this.nombre,
  required this.email,
  required this.avatarUrl,
}) : super(key: key);


Widget build(BuildContext context) {
  return Card(
    margin: EdgeInsets.all(16),
    elevation: 4,
    child: Padding(
      padding: EdgeInsets.all(16),
      child: Row(
        children: [
          // Avatar circular
          CircleAvatar(
            radius: 30,
            backgroundImage: NetworkImage(avatarUrl),
          ),
          SizedBox(width: 16),
          // Información del usuario
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  nombre,
                  style: TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                SizedBox(height: 4),
                Text(
                  email,
                  style: TextStyle(
                    color: Colors.grey[600],
                  ),
                ),
              ],
            ),
          ),
          // Botón de acción
          IconButton(
            icon: Icon(Icons.more_vert),
            onPressed: () {
              print('Más opciones para $nombre');
            },
          ),
        ],
      ),
    ),
  );
}
}

// Uso
TarjetaUsuario(
nombre: 'Ana García',
email: 'ana@ejemplo.com',
avatarUrl: 'https://ejemplo.com/avatar.jpg',
)

Ciclo de Vida de un StatefulWidget


  Métodos del Ciclo de Vida

createState(): Crea el estado del widget initState(): Inicialización (se ejecuta una vez) build(): Construye la UI (se ejecuta múltiples veces) setState(): Notifica cambios de estado dispose(): Limpieza cuando el widget se destruye

dart
class MiWidgetCompleto extends StatefulWidget {

_MiWidgetCompletoState createState() => _MiWidgetCompletoState();
}

class _MiWidgetCompletoState extends State<MiWidgetCompleto> {
String _mensaje = '';


void initState() {
  super.initState();
  _mensaje = 'Widget inicializado';
  print('initState: Widget creado');
}


Widget build(BuildContext context) {
  print('build: Construyendo UI');
  return Column(
    children: [
      Text(_mensaje),
      ElevatedButton(
        onPressed: () {
          setState(() {
            _mensaje = 'Estado actualizado: ${DateTime.now()}';
          });
        },
        child: Text('Actualizar'),
      ),
    ],
  );
}


void dispose() {
  print('dispose: Widget destruido');
  super.dispose();
}
}

Buenas Prácticas con Widgets


Composición sobre Herencia

Prefiere combinar widgets simples en lugar de crear widgets complejos:

dart
// ✅ Buena práctica - Composición
Widget construirBotonPersonalizado() {
return Container(
  decoration: BoxDecoration(
    gradient: LinearGradient(colors: [Colors.blue, Colors.purple]),
    borderRadius: BorderRadius.circular(25),
  ),
  child: ElevatedButton(
    style: ElevatedButton.styleFrom(backgroundColor: Colors.transparent),
    onPressed: () {},
    child: Text('Botón Gradiente'),
  ),
);
}

Extracción de Widgets

Divide widgets grandes en componentes más pequeños:

dart
// ✅ Widget extraído y reutilizable
class BotonAccion extends StatelessWidget {
final String texto;
final VoidCallback onPressed;
final Color color;

const BotonAccion({
  Key? key,
  required this.texto,
  required this.onPressed,
  this.color = Colors.blue,
}) : super(key: key);


Widget build(BuildContext context) {
  return ElevatedButton(
    style: ElevatedButton.styleFrom(backgroundColor: color),
    onPressed: onPressed,
    child: Text(texto),
  );
}
}
  Nota

Los widgets son la base fundamental de Flutter. Dominar los conceptos de StatelessWidget, StatefulWidget, build() y setState() es esencial para crear aplicaciones efectivas.