Gestión del estado de tus variables Flutter con setState


Avatar de Pedro Cortez

¿Cómo transmitir información a través de tus diferentes widgets en Flutter? En esta guía, te explico cómo hacerlo utilizando setState.


Gestión del estado de tus variables Flutter con setState

¿Qué es la gestión del estado?

En el artículo anterior, vimos cómo transmitir información de manera descendente desde un widget padre a un widget hijo. Este es uno de los primeros conceptos que hay que comprender en Flutter: los datos fluyen de arriba hacia abajo, del padre al hijo. Sin embargo, en la mayoría de las aplicaciones reales, es necesario enviar información de vuelta o compartir datos entre varios widgets. Por ejemplo, un widget hijo podría necesitar informar a su padre sobre un cambio, o widgets del mismo nivel podrían necesitar intercambiar datos. Aquí es donde entra en juego la gestión del estado.

La gestión del estado es un concepto fundamental en el desarrollo de aplicaciones interactivas. Permite almacenar, modificar y compartir datos entre widgets. Por ejemplo, cuando quieres que tu aplicación reaccione a una acción del usuario (como hacer clic en un botón o cambiar un valor), necesitas saber cómo actualizar el estado para que la interfaz refleje esos cambios.

Esto se vuelve especialmente importante en casos como:

  • Actualizar un formulario o una interfaz en respuesta a la interacción del usuario.
  • Modificar un contador cada vez que se presiona un botón.
  • Cambiar la apariencia de un botón según una acción.

¿Cómo funciona la gestión del estado en Flutter?

En Flutter, una de las formas más simples de gestionar el estado es mediante el uso del método setState. Permite notificar a Flutter que ha ocurrido un cambio de estado y que la interfaz debe actualizarse en consecuencia.

Cuando ocurre un cambio de estado (por ejemplo, el valor de un contador cambia), llamas a setState para informar a Flutter que el estado ha cambiado. Flutter entonces reconstruye el widget afectado para mostrar el nuevo valor.

Esto permite, por ejemplo, enviar información desde un widget hijo a su padre o hacer circular datos entre varios widgets que no tienen una relación padre-hijo.

El papel de setState

En realidad, setState no reconstruye todo el árbol de widgets. Solo el widget afectado y sus hijos directos son reconstruidos. Esto ayuda a mantener un buen rendimiento en la aplicación. En otras palabras, cuando modificas el estado con setState, Flutter activa una actualización solo en las partes necesarias, optimizando el uso de recursos.

Esto permite, por ejemplo, enviar información desde un widget hijo a su padre o hacer circular datos entre varios widgets que no tienen una relación padre-hijo.

¿Cómo enviar un valor desde un widget hijo hacia su widget padre?

A veces, es posible que necesites no solo transmitir un valor del widget padre al widget hijo, sino hacer lo contrario. Para esto, puedes usar una función de retorno (callback). El padre proporcionará una función al widget hijo, que este podrá utilizar para enviar información a su padre y actualizarlo.

Por ejemplo, imaginemos que ParentWidget muestra un contador y que ChildWidget contiene un botón para incrementarlo. Cada clic en el botón enviará un valor de ChildWidget a ParentWidget para actualizar el contador:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: ParentWidget()));
}

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  int counter = 0; // Contador a mostrar

  // Método para incrementar el contador
  void incrementCounter() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            'Counter: $counter', // Muestra el contador actual
            style: TextStyle(fontSize: 24),
          ),
          SizedBox(height: 20),
          ChildWidget(onIncrement: incrementCounter), // Pasa la función al widget hijo
        ],
      )),
    );
  }
}

class ChildWidget extends StatelessWidget {
  final VoidCallback onIncrement; // Función de callback para el botón

  const ChildWidget({required this.onIncrement});
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onIncrement, // Llama a la función del padre
      child: Text("Increment Counter"),
    );
  }
}

Explicación

  1. Declaración del contador en el widget padre: En ParentWidget, declaramos una variable counter para almacenar el número de incrementos. La función incrementCounter se usa para aumentar este contador y llamar a setState para actualizar la interfaz.
  2. Pasar la función al widget hijo: En ParentWidget, pasamos incrementCounter a ChildWidget a través del parámetro onIncrement.
  3. Llamar a la función en el widget hijo: En ChildWidget, el botón usa onIncrement como función de retorno (callback). Cada vez que se presiona, onIncrement se llama, lo que activa incrementCounter en ParentWidget y actualiza la visualización del contador.

Resultado

Con este modelo, el widget hijo ChildWidget puede activar una acción en el widget padre ParentWidget, y este puede mostrar la actualización del contador en tiempo real.

Transmitir valores entre múltiples widgets

Ahora que sabes cómo transmitir información de un widget a otro, aquí te mostramos cómo hacerlo en un sistema de tres widgets o más.

Imaginemos un escenario donde ChildWidget modifica un contador, y este cambio debe ser enviado al RootWidget (el padre), pasando por MiddleWidget (el intermediario). Aquí está el proceso que seguirá la información:

Cuando se presione el botón en ChildWidget, llamará a la función para informar al padre sobre el cambio de valor.

  • RootWidget (el padre) definirá una función de actualización (updateCounter).
  • Esta función será transmitida a MiddleWidget y luego a ChildWidget.
  • Cuando se presione el botón en ChildWidget, llamará a la función para informar al padre sobre el cambio de valor.
import 'package:flutter/material.dart';

class RootWidget extends StatefulWidget {
  @override
  _RootWidgetState createState() => _RootWidgetState();
}

class _RootWidgetState extends State<RootWidget> {
  int _counter = 0;

  // Función que actualiza el contador
  void updateCounter(int newValue) {
    setState(() {
      _counter = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Ejemplo de transmisión de datos")),
      body: Center(
        child: MiddleWidget(
          counter: _counter, // Pasa el valor actual
          onCounterChanged: updateCounter, // Pasa la función de actualización
        ),
      ),
    );
  }
}

Conclusión

A medida que tu aplicación crece, gestionar el estado a través de múltiples widgets se vuelve crucial. Usando setState de manera adecuada, puedes transmitir valores eficientemente entre distintos widgets, permitiendo que tu interfaz se mantenga dinámica e interactiva.

Sin embargo, a medida que tu aplicación se vuelve más compleja, otras soluciones como Provider, Riverpod o Bloc pueden ser necesarias para gestionar un estado más global y evitar un paso excesivo de datos entre múltiples widgets.

Avatar de Pedro Cortez