Saltar al contenido principal

Widgets stateful

Aprende sobre StatefulWidgets y cómo reconstruir la UI de Flutter.

Aprende cuándo los widgets necesitan ser stateful y cómo activar actualizaciones de la UI con setState.

Qué lograrás

Aprende cuándo los widgets necesitan ser stateful
Convierte un StatelessWidget en un StatefulWidget
Activa actualizaciones de la UI con setState

Pasos

1

Introducción

Hasta ahora, tu aplicación muestra una cuadrícula y un campo de entrada, pero la cuadrícula aún no se actualiza para reflejar los intentos del usuario. Cuando esta aplicación esté completa, cada casilla en la siguiente fila vacía debería actualizarse después de cada intento enviado por el usuario al:

  • Mostrar la letra correcta.
  • Cambiar de color para reflejar si la letra es correcta (verde), está en la palabra pero en una posición incorrecta (amarillo), o no aparece en la palabra en absoluto (gris).

Para manejar este comportamiento dinámico, necesitas convertir GamePage de un StatelessWidget a un StatefulWidget.

2

¿Por qué widgets stateful?

Cuando la apariencia o los datos de un Widget necesitan cambiar durante su ciclo de vida, necesitas un StatefulWidget y un objeto State asociado. Aunque el StatefulWidget en sí mismo sigue siendo inmutable (sus propiedades no pueden cambiar después de su creación), el objeto State es de larga duración, puede contener datos mutables y se puede reconstruir cuando esos datos cambian, haciendo que la UI se actualice.

Por ejemplo, el siguiente árbol de widgets imagina una aplicación simple que utiliza un widget stateful con un contador que incrementa cuando se presiona el botón.

Un diagrama de un árbol de widgets con un widget stateful y un objeto state.

Aquí está la estructura básica de StatefulWidget (aún no hace nada):

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

  @override
  State<ExampleWidget> createState() => _ExampleWidgetState();
}

class _ExampleWidgetState extends State<ExampleWidget> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
3

Convierte GamePage a un widget stateful

Para convertir el widget GamePage (o cualquier otro) de un widget stateless a un widget stateful, sigue estos pasos:

  1. Cambia GamePage para que extienda StatefulWidget en lugar de StatelessWidget.
  2. Crea una nueva clase llamada _GamePageState, que extienda State<GamePage>. Esta nueva clase contendrá el estado mutable y el método build. Mueve el método build y todas las propiedades instanciadas en el widget desde GamePage al objeto State.
  3. Implementa el método createState() en GamePage, el cual devuelve una instancia de _GamePageState.

Tu código modificado debería verse así:

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

  @override
  State<GamePage> createState() => _GamePageState();
}

class _GamePageState extends State<GamePage> {
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          for (var guess in _game.guesses)
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                for (var letter in guess)
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 2.5, vertical: 2.5),
                    child: Tile(letter.char, letter.type),
                  )
              ],
            ),
          GuessInput(
            onSubmitGuess: (_) {
              // TODO, handle guess
              print(guess); // Temporary
            },
          ),
        ],
      ),
    );
  }
}
4

Actualizar la UI con setState

Cada vez que mutes un objeto State, debes llamar a setState para indicarle al framework que actualice la interfaz de usuario y llame al método build nuevamente.

En esta aplicación, cuando un usuario hace un intento, la palabra que intentó se guarda en el objeto Game, el cual es una propiedad de la clase GamePage asociada, y por lo tanto es un estado que podría cambiar y requerir que la UI se actualice. Cuando este estado se muta, la cuadrícula debe redibujarse para mostrar el intento del usuario.

Para implementar esto, actualiza la función callback pasada a GuessInput. La función necesita llamar a setState y, dentro de setState, necesita ejecutar la lógica para determinar si el intento del usuario fue correcto.

Actualiza tu código:

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

  @override
  State<GamePage> createState() => _GamePageState();
}

class _GamePageState extends State<GamePage> {
  final Game _game = Game();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          for (var guess in _game.guesses)
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                for (var letter in guess)
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 2.5, vertical: 2.5),
                    child: Tile(letter.char, letter.type),
                  )
              ],
            ),
          GuessInput(
            onSubmitGuess: (String guess) {
              setState(() { // NEW
                _game.guess(guess);
              });
            },
          ),
        ],
      ),
    );
  }
}

Ahora, cuando escribas un intento válido en el TextInput y lo envíes, la aplicación reflejará el intento del usuario. Si llamaras a _game.guess(guess) sin llamar a setState, los datos internos del juego cambiarían, pero Flutter no sabría que necesita redibujar la pantalla, y el usuario no vería ninguna actualización.

5

Revisión

Qué lograste

Aquí tienes un resumen de lo que construiste y aprendiste en esta lección.
Aprendiste cuándo los widgets necesitan ser stateful

Cuando la apariencia o los datos de un Widget necesitan cambiar durante su ciclo de vida, necesitas un StatefulWidget. El Widget en sí permanece inmutable, pero su objeto State asociado contiene datos mutables y activa reconstrucciones.

Convertiste GamePage a un StatefulWidget

Refactorizaste GamePage para que sea stateful creando una clase _GamePageState asociada, moviendo el método build y las propiedades mutables a ella, e implementando createState(). El soporte de tu IDE para quick assists puede automatizar esta conversión.

Hiciste que tu aplicación responda a la entrada del usuario con setState

Llamar a setState le dice a Flutter que reconstruya la UI de un Widget. Cuando un usuario envía un intento, llamas a setState para actualizar el estado del juego, y la cuadrícula refleja automáticamente los nuevos datos. ¡Tu aplicación ahora es verdaderamente interactiva!

6

Ponte a prueba

Cuestionario sobre Widgets Stateful

1 / 2
When should you use a StatefulWidget instead of a StatelessWidget?
  1. Cuando la apariencia o los datos del widget necesitan cambiar durante su ciclo de vida.

    ¡Así es!

    Se necesita un StatefulWidget cuando la UI debe actualizarse en respuesta a cambios de datos a lo largo del tiempo.

  2. Cuando el widget tiene más de tres widgets hijos.

    No exactamente.

    El número de hijos no determina si un widget es stateful.

  3. Cuando el widget está en la raíz del árbol de widgets.

    No exactamente.

    Los widgets raíz pueden ser stateless; que sea stateful depende de si los datos cambian durante el ciclo de vida del widget.

  4. Cuando el widget necesita realizar peticiones HTTP.

    No exactamente.

    Las peticiones HTTP se pueden realizar desde cualquiera de los dos, pero los cambios de estado requieren StatefulWidget.

What happens if you change data in a State object without calling setState?
  1. La aplicación fallará con un error.

    No exactamente.

    La aplicación no fallará, pero la UI no se actualizará.

  2. Flutter detecta automáticamente el cambio y reconstruye la UI.

    No exactamente.

    Flutter requiere setState para saber cuándo reconstruir; no detecta automáticamente los cambios.

  3. El widget se elimina del árbol de widgets.

    No exactamente.

    El widget permanece; simplemente no se actualizará visualmente sin setState.

  4. Los datos cambian internamente, pero Flutter no reconstruirá la UI para reflejar el cambio.

    ¡Así es!

    Sin llamar a setState, Flutter no sabe que necesita redibujar, por lo que el usuario no verá las actualizaciones.