StatefulWidget: qué es, ciclo de vida y principio de funcionamiento

Autor: IT Sectr Publicado: 2026-07-01 Tiempo de lectura: 9 min

StatefulWidget es un widget de Flutter con estado mutable, que permite a la interfaz reaccionar a acciones del usuario, eventos asíncronos y flujos de datos. Según la documentación oficial de Flutter (Flutter.dev, 2026), StatefulWidget se utiliza para todos los elementos interactivos de la aplicación: formularios de entrada, animaciones, casillas de verificación, interruptores y pantallas que cargan datos desde la red. A diferencia de StatelessWidget, crea un objeto State separado que persiste durante todo su ciclo de vida y puede reconstruirse sin necesidad de recrear el widget en sí.

Puntos clave

  • StatefulWidget es un widget que puede cambiar su estado durante la ejecución, provocando la reconstrucción de la interfaz mediante setState
  • Ciclo de vida — StatefulWidget pasa por las etapas createState, initState, didChangeDependencies, build, didUpdateWidget, dispose
  • Objeto State es un objeto separado que almacena el estado y existe independientemente del widget durante toda su vida
  • setState es la única forma legítima de notificar a Flutter que el widget necesita reconstruirse tras un cambio de datos
  • Rendimiento — el uso excesivo de StatefulWidget aumenta el consumo de memoria y el tiempo de renderizado

¿Qué es StatefulWidget?

StatefulWidget es una clase de Flutter que puede cambiar su estado en respuesta a acciones del usuario, eventos del sistema u operaciones asíncronas. A diferencia de StatelessWidget, StatefulWidget no se renderiza directamente — crea un objeto State que se encarga del renderizado. Esta separación en dos clases (Widget y State) permite a Flutter reconstruir la interfaz sin recrear el widget en sí, lo que proporciona una ventaja significativa de rendimiento durante actualizaciones frecuentes.

La arquitectura de StatefulWidget sigue el patrón de “separación de lo mutable y lo inmutable”: el widget en sí permanece inmutable (como StatelessWidget), mientras que todo el estado mutable se almacena en un objeto State separado. Esto permite a Flutter reutilizar widgets comparándolos por tipo y Key, preservando al mismo tiempo el estado real entre reconstrucciones.

Según Google (Flutter Architectural Overview, 2026), StatefulWidget es óptimo para escenarios donde el estado cambia más de una vez durante la vida del widget: campos de texto, animaciones, temporizadores, flujos de datos, cargas asíncronas. Para una inicialización única, StatelessWidget es suficiente.

Cuándo es necesario StatefulWidget

StatefulWidget es obligatorio cuando el widget debe responder a eventos externos: clics de botón, finalización de solicitudes HTTP, actualizaciones de datos de la base de datos, suscripciones a WebSocket. También es necesario para widgets con animaciones, campos de texto con controladores y componentes que gestionan el foco. Si un widget solo muestra datos y no genera eventos, usa StatelessWidget.

Estructura interna

StatefulWidget consta de dos clases: el propio StatefulWidget (ligero, inmutable) y State (pesado, mutable). El framework crea State mediante el método createState(), llamado una vez al insertarse en el árbol. State recibe una referencia al widget a través de la propiedad widget y puede acceder a sus campos en cualquier momento del ciclo de vida.

Ciclo de vida de StatefulWidget

El ciclo de vida de StatefulWidget consta de seis etapas principales, cada una de las cuales proporciona un método redefinible para realizar tareas específicas. Comprender estas etapas es fundamental para la gestión adecuada de recursos y evitar fugas de memoria.

createState

createState es el primer método del ciclo de vida, llamado cuando StatefulWidget se inserta en el árbol. Debe devolver una nueva instancia de State asociada a este widget. Este método se llama exactamente una vez durante toda la vida del elemento. Es importante no realizar operaciones pesadas aquí — createState debe ser lo más ligero posible.

initState

initState se llama inmediatamente después de la creación de State, antes de la primera construcción de la interfaz. Aquí se realiza: inicialización de controladores (TextEditingController, AnimationController), suscripción a flujos de datos (StreamSubscription), configuración de temporizadores e inicialización de campos. Según la documentación de Flutter (Flutter.dev, 2026), no se puede llamar a BuildContext.of() en initState — el árbol aún no está completamente montado.

didChangeDependencies

didChangeDependencies se llama después de initState y cada vez que cambian las dependencias de InheritedWidget. Este es un lugar adecuado para llamar a MediaQuery.of(context) o suscribirse a Theme — valores que pueden cambiar durante la ejecución de la aplicación. Si un widget usa InheritedWidget, la lógica de inicialización debe estar aquí, no en initState.

build y didUpdateWidget

build es el método principal que devuelve el árbol de widgets. Se llama después de initState, después de didChangeDependencies y después de cada setState. didUpdateWidget se llama cuando el padre se reconstruye y pasa un StatefulWidget con nuevos parámetros. Aquí se pueden comparar los campos antiguos y nuevos del widget y, si es necesario, actualizar el estado.

dispose

dispose es la etapa final del ciclo de vida. Aquí se liberan todos los recursos: se cancelan las suscripciones a flujos, se eliminan los controladores, se cancelan los temporizadores. No llamar a dispose provoca fugas de memoria. Después de dispose, State se considera muerto — llamar a setState dentro de él lanza una excepción.

¿Cómo funciona StatefulWidget?

El mecanismo de funcionamiento de StatefulWidget se basa en el trabajo coordinado de tres entidades: Widget (descripción ligera), Element (capa intermedia) y State (almacenamiento de datos). Cuando Flutter encuentra un StatefulWidget en la descripción, crea un StatefulElement, que llama a createState y almacena una referencia al objeto State. Cuando el padre se reconstruye, Flutter compara el nuevo widget con el Element actual — si el tipo y la Key coinciden, el Element se actualiza y el State permanece igual.

El estado solo se cambia mediante la llamada a setState, que notifica al framework que se necesita una reconstrucción. Es importante entender: setState no cambia el estado automáticamente — solo marca el widget como “sucio”. El desarrollador actualiza independientemente los campos de State en el callback pasado a setState. Después de que el callback se completa, Flutter llama a build y actualiza la interfaz.

Según el equipo de Dart/Flutter (Dart Language Specification, 2026), esta separación garantiza que todos los cambios de estado ocurran sincrónicamente antes de que se llame a build, eliminando la situación en la que la interfaz muestra datos parcialmente actualizados. Este es un mecanismo clave de consistencia de la interfaz en Flutter.

Ejemplos de código en Dart

Veamos un StatefulWidget simple — un contador de clics en un botón. Demuestra el patrón básico: creación de State, inicialización de un campo en initState, cambio mediante setState:

dart
class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Incrementar'),
        ),
      ],
    );
  }
}

Un ejemplo con carga asíncrona de datos y gestión del ciclo de vida. StatefulWidget carga datos desde la red y muestra el estado de carga:

dart
class UserProfilePage extends StatefulWidget {
  final String userId;
  const UserProfilePage({super.key, required this.userId});

  @override
  State<UserProfilePage> createState() => _UserProfilePageState();
}

class _UserProfilePageState extends State<UserProfilePage> {
  UserModel? _user;
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadUser();
  }

  Future<void> _loadUser() async {
    final user = await UserService.fetchUser(widget.userId);
    setState(() {
      _user = user;
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) return const CircularProgressIndicator();
    return Text('Hola, ${_user!.name}');
  }
}

En el segundo ejemplo, es importante señalar: initState inicia una operación asíncrona, pero el método en sí no es asíncrono. La asincronía se implementa mediante async/await dentro de un método separado _loadUser, que actualiza el estado mediante setState después de que se completa la solicitud. Este enfoque garantiza que el widget muestre correctamente el indicador de carga antes de recibir los datos.

StatefulWidget vs StatelessWidget

La elección entre StatefulWidget y StatelessWidget no se trata solo de tener estado. StatefulWidget proporciona un ciclo de vida completo con los métodos initState, didChangeDependencies, didUpdateWidget y dispose, que son necesarios para trabajar con controladores, animaciones y flujos. StatelessWidget, por su parte, no tiene estos métodos y siempre es más ligero para el framework.

La recomendación del equipo de Flutter (Flutter docs, 2026) es minimizar la cantidad de StatefulWidgets en una aplicación, elevando el estado hacia arriba en el árbol (State Hoisting) o utilizando soluciones de gestión de estado (Riverpod, Bloc, Provider). Cada StatefulWidget crea un objeto State que vive hasta que se elimina el elemento — cuantos más widgets de este tipo, mayor es la carga de memoria.

CriterioStatefulWidgetStatelessWidget
EstadoMutableInmutable
Ciclo de vida6 etapasSolo build
Objeto StateSe crea por separadoNo se requiere
setStateDisponibleNo disponible
SuscripcionesinitState/disposeNo compatibles
Constructor constLimitadoTotalmente compatible
Consumo de memoriaMayorMenor

Rendimiento y optimización

StatefulWidget requiere más recursos que StatelessWidget debido a la necesidad de crear y mantener un objeto State. Sin embargo, el uso correcto de StatefulWidget no provoca problemas de rendimiento si se siguen algunas reglas. Primero, evita el anidamiento profundo de StatefulWidget — cada nivel añade sobrecarga al recorrido del árbol. Segundo, divide un StatefulWidget complejo en varios simples, cada uno responsable de su propia parte del estado.

Según la investigación de rendimiento de Flutter (Flutter.dev, febrero de 2026), la causa más común de caídas de FPS es llamar a setState en un widget padre que reconstruye todos los descendientes, incluidos StatelessWidgets que no han cambiado su visualización. La solución es extraer la parte mutable de la interfaz en un StatefulWidget separado para que setState solo reconstruya los widgets mínimos necesarios.

Usar const dentro de State es otra técnica importante. Si los widgets hijos se declaran como const, Flutter no los reconstruirá cuando se llame a setState en el padre. Esto reduce la carga del framework y disminuye el tiempo de renderizado del fotograma.

Evita setState frecuente

Cada llamada a setState desencadena una reconstrucción completa del widget. Si el estado cambia con alta frecuencia (por ejemplo, animación o flujo de datos), considera usar AnimatedBuilder, ValueListenableBuilder o StreamBuilder en lugar de llamar a setState manualmente. Estos widgets optimizan la reconstrucción, actualizando solo la parte de la interfaz que realmente ha cambiado.

Errores comunes

El primer error común con StatefulWidget es llamar a setState después de dispose. Cuando un widget se elimina del árbol, State se considera muerto, y cualquier llamada a setState lanza una excepción “setState called after dispose”. Esto ocurre más a menudo cuando una operación asíncrona finaliza después de que el widget ha sido eliminado. La solución es verificar la bandera mounted antes de llamar a setState o cancelar las operaciones asíncronas en dispose.

El segundo error es realizar cálculos pesados en el método build. Dado que build se llama en cada setState y en cada reconstrucción del padre, todos los cálculos deben ser lo más ligeros posible. Si se necesita una operación intensiva en recursos, muévela a un Isolate separado o almacena en caché el resultado en un campo de State.

El tercer error es no llamar a super.initState() y super.dispose(). Al redefinir estos métodos, el desarrollador debe llamar a la implementación del padre. De lo contrario, el framework no podrá gestionar correctamente el estado de Element, lo que provocará errores difíciles de rastrear.

Recomendaciones para evitar errores

  • Verifica siempre mounted antes de setState en callbacks asíncronos
  • No olvides llamar a super.initState() y super.dispose()
  • No hagas solicitudes HTTP directamente en build — usa initState
  • Cancelar todas las suscripciones en dispose
  • Usa la mínima cantidad de StatefulWidgets en tu proyecto

Preguntas frecuentes

¿En qué se diferencia StatefulWidget de StatelessWidget?

StatefulWidget puede cambiar su estado mediante setState, tiene un ciclo de vida (initState, dispose) y crea un objeto State separado. StatelessWidget no puede cambiar el estado y no tiene métodos de ciclo de vida — simplemente muestra los datos que recibe.

¿Cuántas veces se llama a createState?

createState se llama exactamente una vez por cada instancia de StatefulElement. Incluso si el padre se reconstruye múltiples veces, mientras el tipo y la Key del widget no cambien, createState no se llama — se utiliza el objeto State existente.

¿Qué sucede si no se llama a dispose?

Los recursos no se liberarán: los controladores seguirán funcionando en segundo plano, las suscripciones a flujos permanecerán activas, los temporizadores no se cancelarán. Esto provoca fugas de memoria y puede causar llamadas a setState después de dispose, lo que lanza una excepción.

¿Puede StatefulWidget ser const?

Sí, el constructor de StatefulWidget puede ser const. Sin embargo, esto no proporciona el mismo beneficio que para StatelessWidget — el objeto State se creará igualmente en la primera inserción. const solo afecta al widget en sí (la envoltura ligera), no al State.

¿Para qué sirve el método didUpdateWidget?

didUpdateWidget se llama cuando el padre pasa un StatefulWidget con nuevos parámetros. Esto es necesario para sincronizar el estado con los nuevos datos — por ejemplo, si userId en los parámetros ha cambiado, se debe cargar el perfil del nuevo usuario.

Resumen

  • StatefulWidget es un widget con estado mutable que utiliza un objeto State separado para almacenar datos y gestionar el ciclo de vida
  • Ciclo de vida consta de createState, initState, didChangeDependencies, build, didUpdateWidget y dispose, cada uno con su propio propósito
  • setState es la única forma legítima de notificar al framework sobre un cambio de estado, tras el cual se llama automáticamente a build
  • mounted es una bandera que debe verificarse antes de llamar a setState en operaciones asíncronas para evitar una excepción después de dispose
  • Rendimiento — StatefulWidget requiere más recursos que StatelessWidget; se recomienda minimizar su cantidad elevando el estado a capas externas
  • Widgets hijos const dentro de State ayudan a reducir la cantidad de reconstrucción cuando se llama a setState, mejorando el rendimiento
  • Elección correcta — usa StatefulWidget solo cuando el widget necesite gestionar datos mutables u operaciones asíncronas

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también