diegoRodicio

View Categories

🧩 Callbacks en Flutter

Tempo de lectura estimado: 4 minutos

En Flutter, moitos widgets personalizados (como botóns, iconas ou contedores táctiles) precisan executar unha acción cando o usuario realiza unha interacción, por exemplo, ao premer unha tecla ou tocar un botón.

Para conseguilo, o widget non executa directamente a acción, senón que recibe unha función (callback) definida polo widget pai.
Deste modo, o widget pai decide que facer, e o fillo simplemente chama á función cando corresponde.

⚠️ Nota Importante: O obxectivo é crear widgets reutilizables e personalizables que poidamos empregar en distintas partes da aplicación. Para conseguilo, a miúdo necesitamos que o widget reciba unha función por parámetro, e isto é precisamente o que logramos empregando callbacks.

🧠 Que é exactamente un callback?

Un callback é unha función que se lle pasa como parámetro a outro widget para que a execute nun momento determinado (xeralmente cando ocorre un xesto).

Por exemplo, imaxina que temos unha función no widget pai:

void borrar() {
  print("Borrando datos...");
}

E a pasamos a un botón:

ElevatedButton(
  onPressed: borrar, // 👈 PASAMOS a función como callback (sen parénteses)
  child: const Text("Borrar"),
);

Aquí:

  • borrar é o callback.
  • ElevatedButton non sabe que significa “borrar”; só sabe que, cando o usuario preme, debe chamar á función que lle chegou en onPressed.

Internamente, o botón fará algo así:

class MeuBoton extends StatelessWidget {
  final VoidCallback onPressed;

  const MeuBoton({super.key, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onPressed, // 👈 chama ao callback cando se toca o botón
      child: const Text("Son un botón"),
    );
  }
}

Deste xeito, o comportamento do botón é totalmente configurable: depende da función (callback) que lle pase o widget pai.

⚙️ Como se declara un callback nun widget personalizado?

Cando creas un widget, podes permitir que acepte unha función como parámetro.

As formas máis habituais son:

final VoidCallback onTap;          // función sen parámetros e sen retorno
final void Function(int) onNumero; // función con parámetro

💡 Tipos de callbacks máis usados

Tipo de callbackCando usaloFunción no paiPai: como PASA o callbackFillo: como RECIBE e USA
Sen parámetros
VoidCallback
O fillo só debe avisar dunha acciónvoid borrar() { ... }BotonBorrar(onTap: borrar)onPressed: onTap
Con número
void Function(int)
O fillo debe enviar un númerovoid sumar(int n) { ... }TeclaNumero(onNumero: sumar)onPressed: () => onNumero(7)
Con texto
void Function(String)
O fillo debe enviar textovoid actualizar(String t) { ... }CampoTexto(onCambio: actualizar)onChanged: (v) => onCambio(v)
Con obxecto
void Function(Persoa)
O fillo envía un obxecto completovoid gardarPersoa(Persoa p) { ... }Editor(onGardar: gardarPersoa)onPressed: () => onGardar(p)
Callback que devolve valor
int Function() / String Function()
O fillo necesita pedirlle información ao paiint obterId() { ... }Vista(onGetId: obterId)final id = onGetId()

Notas:

  • Se non precisas datos, usa VoidCallback.
  • Se precisas pasar un valor, usa void Function(Tipo) (por exemplo, void Function(int)).

Equivalentes (sen parámetros):

final VoidCallback accion1;
final void Function() accion2; // fan o mesmo

⚡ Callback sen parámetros: VoidCallback

O tipo VoidCallback é a forma máis sinxela de definir un callback en Flutter.
Representa unha función sen argumentos e sen retorno, que se executa cando ocorre unha acción.

É moi común en botóns e xestos, como ElevatedButton, IconButton ou GestureDetector.

🧠 Exemplo práctico

class BotonSimple extends StatelessWidget {
  final String texto;
  final VoidCallback onTap;

  const BotonSimple({super.key, required this.texto, required this.onTap});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onTap, // chamamos ao callback
      child: Text(texto),
    );
  }
}

Uso:

BotonSimple(
  texto: 'Borrar',
  onTap: () {
    print("Botón premido");
  },
);

O fillo non sabe que debe facer → só chama onTap().

🧩 Callback con parámetros

Hai casos nos que non chega con saber que se premeu, senón que valor se premeu.
Por exemplo, nun teclado de calculadora, queremos saber que número tocou o usuario.

Para iso non abonda con VoidCallback (que non admite parámetros).
Debemos usar unha función que reciba un argumento, como:

final void Function(int) onNumero;

O fillo pode facer:

onTap(numeroBoton);

🔼 Comunicación fillo → pai mediante callbacks

Este patrón é fundamental:

o pai controla o estado, pero o fillo provoca cambios chamando á función que lle pasa o pai.

1️⃣ O widget pai define unha función e pásalla ao fillo

class _PantallaPrincipalState extends State<PantallaPrincipal> {
  String nome = "Diego";

  void actualizarNome(String novoNome) {
    setState(() {
      nome = novoNome;
    });
  }

  @override
  Widget build(BuildContext context) {
    return EditorNomeCallback(
      nomeInicial: nome,
      aoGardar: actualizarNome, // 👈 callback enviado
    );
  }
}

2️⃣ O widget fillo recibe a función e chámase cando corresponda

class EditorNomeCallback extends StatefulWidget {
  final String nomeInicial;
  final Function(String) aoGardar;

  const EditorNomeCallback({
    super.key,
    required this.nomeInicial,
    required this.aoGardar,
  });

  @override
  State<EditorNomeCallback> createState() => _EditorNomeCallbackState();
}

class _EditorNomeCallbackState extends State<EditorNomeCallback> {
  late String nomeEditado;

  @override
  void initState() {
    super.initState();
    nomeEditado = widget.nomeInicial;
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        widget.aoGardar(nomeEditado); // 👈 comunicación cara arriba
        Navigator.pop(context);
      },
      child: const Text("Gardar"),
    );
  }
}

📘 O exemplo completo:

🔗 Ver o código en DartPad

📌 Paso de parámetros vs callbacks. Cando usar cada método?

MétodoCando se recomenda
Navigator + pop(valor)Cando o fillo é unha pantalla completa que debe devolver datos ao pai
CallbackCando o fillo é parte da UI e hai comunicación continua e inmediata

🧭 Resumo final

  • O estado vive no pai.
  • O fillo non modifica o estado directamente.
  • O fillo chama unha función que lle pasa o pai.
  • O pai actualiza o estado con setState().
  • Isto permite widgets totalmente reutilizables, simples e limpos.

🧪 Exemplo avanzado: dous tipos de callbacks

Neste exemplo móstrase como varios widgets fillos poden comunicarse cara arriba co widget pai para modificar o seu estado. Esta comunicación faise mediante callbacks, é dicir, funcións que o pai lles pasa aos fillos para que estes poidan executalas cando sexa necesario.

  • 🧩 O estado vive en PantallaPrincipal. Esta clase é un StatefulWidget porque garda e actualiza o contador. Dispón de dúas funcións que modifican o estado mediante setState(), e que se pasan aos fillos como callbacks:
    • incrementarContador() → suma 1
    • sumarNumero(int numero) → suma o valor indicado
  • 🧩 Widgets fillos: reciben unha función e a executan. Hai dous tipos:
    • Contador_ExemploCallback: É un StatelessWidget que recibe o contador e unha función sen parámetros. Ao premer o botón, chama ao callback e o pai actualiza o seu estado.
    • BotonNumero_ExemploCallback: Tamén é StatelessWidget. Recibe un texto, un número e unha función con parámetro. Ao premer, chama ao callback pasando o número correspondente, e o pai actualiza o contador.

🔗 Ver o código en DartPad

📚 Fontes oficiais