¿Qué es un StatefulWidget?

Un StatefulWidget es un widget que puede cambiar su estado durante el tiempo de vida de la aplicación. A diferencia de los StatelessWidget, estos widgets pueden reconstruirse cuando sus datos internos cambian.

  Nota

Concepto clave: StatefulWidget = Widget + Estado mutable que puede cambiar y actualizar la UI automáticamente.

¿Cuándo usar StatefulWidget?


Usa StatefulWidget cuando necesites:

  • Interactividad: Botones, formularios, switches
  • Datos que cambian: Contadores, listas dinámicas
  • Animaciones: Transiciones, efectos visuales
  • Estado temporal: Variables que se modifican

Anatomía de un StatefulWidget

Un StatefulWidget se compone de dos clases:

1. La clase Widget (inmutable)

dart
class MiContador extends StatefulWidget {

State<MiContador> createState() => _MiContadorState();
}

2. La clase State (mutable)

dart
class _MiContadorState extends State<MiContador> {
int contador = 0; // Estado mutable


Widget build(BuildContext context) {
  return Text('$contador');
}
}

Ejemplo Práctico: Contador Simple

dart
class Contador extends StatefulWidget {

State<Contador> createState() => _ContadorState();
}

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

void incrementar() {
  setState(() {
    numero++; // Actualiza el estado
  });
}


Widget build(BuildContext context) {
  return Column(
    children: [
      Text(
        '$numero',
        style: TextStyle(fontSize: 48),
      ),
      ElevatedButton(
        onPressed: incrementar,
        child: Text('Incrementar'),
      ),
    ],
  );
}
}

El método setState()


setState() es la función clave que le dice a Flutter que el estado ha cambiado y necesita reconstruir el widget.

dart
void cambiarTexto() {
setState(() {
  // Solo código que modifica el estado
  mensaje = 'Texto actualizado';
  contador++;
});
}
 

Importante: Solo coloca dentro de setState() el código que modifica variables de estado. Operaciones costosas van fuera.

Ciclo de Vida de un StatefulWidget


Métodos principales:

dart
class _MiWidgetState extends State<MiWidget> {


void initState() {
  super.initState();
  // Se ejecuta UNA vez al crear el widget
  print('Widget creado');
}


Widget build(BuildContext context) {
  // Se ejecuta cada vez que cambia el estado
  return Container();
}


void dispose() {
  // Limpieza antes de destruir el widget
  print('Widget destruido');
  super.dispose();
}
}

Ejemplo Avanzado: Lista Dinámica

dart
class ListaTareas extends StatefulWidget {

State<ListaTareas> createState() => _ListaTareasState();
}

class _ListaTareasState extends State<ListaTareas> {
List<String> tareas = ['Estudiar Flutter', 'Hacer ejercicio'];
final controlador = TextEditingController();

void agregarTarea() {
  if (controlador.text.isNotEmpty) {
    setState(() {
      tareas.add(controlador.text);
      controlador.clear();
    });
  }
}

void eliminarTarea(int index) {
  setState(() {
    tareas.removeAt(index);
  });
}


Widget build(BuildContext context) {
  return Column(
    children: [
      // Campo de texto
      TextField(
        controller: controlador,
        decoration: InputDecoration(
          hintText: 'Nueva tarea',
          suffixIcon: IconButton(
            icon: Icon(Icons.add),
            onPressed: agregarTarea,
          ),
        ),
      ),
      
      // Lista de tareas
      Expanded(
        child: ListView.builder(
          itemCount: tareas.length,
          itemBuilder: (context, index) {
            return ListTile(
              title: Text(tareas[index]),
              trailing: IconButton(
                icon: Icon(Icons.delete),
                onPressed: () => eliminarTarea(index),
              ),
            );
          },
        ),
      ),
    ],
  );
}


void dispose() {
  controlador.dispose(); // Liberar memoria
  super.dispose();
}
}

Buenas Prácticas


Lo que debes hacer

dart
class _MiWidgetState extends State<MiWidget> {
// 1. Variables de estado al inicio
bool isLoading = false;
String mensaje = '';

// 2. Métodos de estado claros
void toggleLoading() {
  setState(() {
    isLoading = !isLoading;
  });
}

// 3. Limpieza en dispose

void dispose() {
  // Cancelar timers, streams, controllers
  super.dispose();
}
}

Debes Evitar

dart
// ❌ No hagas esto
void malEjemplo() {
setState(() {
  // ❌ Operaciones costosas dentro de setState
  for (int i = 0; i < 1000000; i++) {
    // Procesamiento pesado
  }
  contador++; // Solo esto debería estar aquí
});
}

// ❌ No olvides dispose
class _BadState extends State<BadWidget> {
Timer? timer;


void initState() {
  timer = Timer.periodic(Duration(seconds: 1), (t) {});
  // ❌ Sin dispose, causa memory leaks
}
}

Widgets Comunes que usan StatefulWidget

  • TextField: Para entrada de texto
  • Checkbox: Para selecciones
  • Switch: Para toggle on/off
  • Slider: Para valores numéricos
  • AnimatedContainer: Para animaciones
  • PageView: Para navegación por páginas
  Resumen

StatefulWidget es esencial para crear interfaces interactivas en Flutter. Recuerda:

  1. Dos clases: Widget (inmutable) + State (mutable)
  2. setState(): Para actualizar la UI cuando cambia el estado
  3. Ciclo de vida: initState() → build() → dispose()
  4. Limpieza: Siempre libera recursos en dispose()

Con StatefulWidget puedes crear aplicaciones dinámicas y reactivas que responden a la interacción del usuario de forma fluida y eficiente.