Kodokon kodokon.com

Interactividad: botones, StatefulWidget y setState

Da vida a tus pantallas respondiendo a los toques de botón y actualizando la visualización con setState.

10 min · 3 preguntas

Abrir esta lección en Kodokon

Nuestras pantallas son bonitas pero están congeladas: es hora de responder a los gestos del usuario. El botón estándar de Material 3 es ElevatedButton. Toma dos parámetros esenciales: child, su contenido mostrado (a menudo un Text), y onPressed, la función que se ejecuta cuando lo tocas. Una función pasada de esta manera como parámetro se llama callback: no la llamas tú mismo, Flutter la llama por ti en el momento adecuado. La sintaxis () { ... } crea una función anónima, sin nombre, escrita directamente en el sitio.

DART
import 'package:flutter/material.dart';

Widget buildHelloButton() {
  return ElevatedButton(
    onPressed: () {
      debugPrint("Button pressed!");
    },
    child: const Text("Tap here"),
  );
}
Un botón que escribe en la consola en cada toque

debugPrint escribe en la consola... pero la pantalla en sí no cambia. ¿Por qué? Porque un StatelessWidget (un widget sin estado) está congelado: una vez construido, ya no puede cambiar. Para mostrar un valor que evoluciona, necesitas un StatefulWidget (un widget con estado). El estado es el conjunto de datos que pueden cambiar durante la vida del widget: un contador, una casilla marcada, un texto escrito... Un StatefulWidget siempre va de la mano de una clase State, que guarda estos datos y contiene el método build. Aquí está el gran clásico de los inicios: el contador.

DART
import 'package:flutter/material.dart';

void main() {
  runApp(const CounterApp());
}

class CounterApp extends StatelessWidget {
  const CounterApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const CounterPage(),
    );
  }
}

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

  @override
  State<CounterPage> createState() =>
      _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "Counter: $count",
              style: const TextStyle(fontSize: 32),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  count = count + 1;
                });
              },
              child: const Text("+1"),
            ),
          ],
        ),
      ),
    );
  }
}
El contador: un StatefulWidget completo

El mecanismo se da en tres pasos. 1) La variable count vive en _CounterPageState: ese es el estado. 2) En cada toque, el callback llama a setState: este método modifica el estado y le avisa a Flutter que algo ha cambiado. 3) Flutter entonces vuelve a ejecutar build, que redibuja la pantalla con el nuevo valor de count. Recuerda la filosofía: nunca cambias la pantalla directamente, cambias los datos, y Flutter se encarga de la visualización.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. ¿Para qué sirve el parámetro onPressed de un ElevatedButton?
    • Para fijar el texto que se muestra en el botón
    • Para proporcionar la función que se ejecuta cuando se toca el botón
    • Para elegir el color del botón
  2. ¿Cuándo deberías usar un StatefulWidget en lugar de un StatelessWidget?
    • Cuando la visualización depende de datos que cambian con el tiempo
    • Cuando el widget contiene más de tres hijos
    • Cuando quieres que la app arranque más rápido
  3. ¿Qué ocurre si cambias count sin llamar a setState?
    • La app falla inmediatamente
    • La variable cambia en memoria pero la pantalla no se actualiza
    • Flutter llama a setState automáticamente por ti