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.
Concepto clave: StatefulWidget = Widget + Estado mutable que puede cambiar y actualizar la UI automáticamente.
Usa StatefulWidget cuando necesites:
Un StatefulWidget se compone de dos clases:
class MiContador extends StatefulWidget {
State<MiContador> createState() => _MiContadorState();
} class _MiContadorState extends State<MiContador> {
int contador = 0; // Estado mutable
Widget build(BuildContext context) {
return Text('$contador');
}
} 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'),
),
],
);
}
} setState() es la función clave que le dice a Flutter que el estado ha cambiado y necesita reconstruir el widget.
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.
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();
}
} 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();
}
} 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();
}
} // 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
}
} StatefulWidget es esencial para crear interfaces interactivas en Flutter. Recuerda:
Con StatefulWidget puedes crear aplicaciones dinámicas y reactivas que responden a la interacción del usuario de forma fluida y eficiente.