---
title: 🧩 Funcións, callbacks e closures
description: "## 🧩 Dividir o programa en tarefas Unha función agrupa instrucións para poder usalas varias veces. Pode recibir datos (**parámetros**) e devolver un resultado con `return`. ```dart // A función recibe dúas notas e devolve a media. double calcularMedia(double primeira,…"
url: "https://www.drodicio.com/docs/dart/comezando-con-dart/funcions-2/"
updated: "2026-09-24"
category: Tema 1. Comezando con Dart
---

# 🧩 Funcións, callbacks e closures

## 🧩 Dividir o programa en tarefas

Unha función agrupa instrucións para poder usalas varias veces. Pode recibir datos (**parámetros**) e devolver un resultado con `return`.

```dart
// A función recibe dúas notas e devolve a media.
double calcularMedia(double primeira, double segunda) {
  return (primeira + segunda) / 2;
}

void main() {
  double media = calcularMedia(7, 8); // Chamamos á función.
  print(media);
}
```

`double` antes do nome indica o tipo do resultado. Se non precisamos devolver un resultado, usamos `void`, como en `main()`.

`return` remata a función. Nunha función `void` podemos escribir `return;` para saír antes de chegar ao final.

## 📥 Parámetros posicionais e nomeados

### 🔢 Posicionais obrigatorios

Os valores asígnanse segundo a posición. En `calcularMedia(7, 8)`, `7` é `primeira` e `8` é `segunda`. Hai que pasar os dous.

### 🏷️ Nomeados: moi habituais en Flutter

Defínense entre `{}`. Ao chamar á función indicamos o nome de cada argumento. Podemos marcar un como obrigatorio con `required` ou darlle un valor predeterminado.

```dart
void presentar({required String nome, String grupo = '2º DAM'}) {
  print('$nome pertence a $grupo');
}

void main() {
  presentar(nome: 'Iria'); // Usa o grupo predeterminado.
  presentar(grupo: '2º DAM', nome: 'Roi'); // A orde non importa.
}
```

Un parámetro nomeado sen `required` é opcional. Se non ten valor predeterminado, debe admitir `null`, por exemplo `{String? telefono}`. Os valores predeterminados deben ser constantes.

**`required` obriga a pasar o argumento; `?` permite que o valor sexa nulo. Son cousas distintas.**

### 📦 Posicionais opcionais

Defínense entre `[]` e pásanse sen nome. Verémolos menos ca os nomeados.

```dart
String saudar(String nome, [String saudo = 'Ola']) {
  return '$saudo, $nome';
}

void main() {
  print(saudar('Diego')); // Ola, Diego
  print(saudar('Rosa', 'Bo día'));
}
```

Podemos combinar posicionais obrigatorios con nomeados **ou** con posicionais opcionais; non usamos `[]` e `{}` na mesma lista de parámetros.

## 📦 Devolver varios datos cun rexistro

Unha función devolve un único resultado, pero ese resultado pode ser un rexistro con varios campos. Así podemos devolver, por exemplo, a media e se o alumno aprobou:

```dart
({double media, bool aprobado}) avaliar(double primeira, double segunda) {
  double media = (primeira + segunda) / 2;
  return (media: media, aprobado: media >= 5); // Dous datos nun rexistro.
}

void main() {
  final resultado = avaliar(7, 9);
  print('Media: ${resultado.media}'); // 8.0
  print('Aprobado: ${resultado.aprobado}'); // true
}
```

`({double media, bool aprobado})` indica o tipo do resultado. Despois accedemos a cada campo polo seu nome. Tamén poderiamos devolver un rexistro posicional, pero os nomes axudan a entender que representa cada valor.

## ➡️ Sintaxe de frecha

Se a función contén unha única expresión, podemos usar `=>`. Non depende do número de liñas que ocupe.

```dart
// Devolve o resultado da comparación.
bool estaAprobado(int nota) => nota >= 5;
```

Se precisamos varias instrucións, usamos chaves e, cando corresponda, `return`.

## 📌 Funcións como obxectos

En Dart, as funcións son tamén **obxectos**. Isto significa que podemos traballar con elas como facemos con outros valores:

- gardalas nunha variable;
- pasalas como argumento a outra función;
- devolvelas como resultado dunha función.

### 📦 Gardar unha función nunha variable

Podemos gardar unha función xa existente nunha variable:

```dart
int dobre(int numero) {
  return numero * 2;
}

void main() {
  int Function(int) operacion = dobre;

  print(operacion(5)); // 10
}
```

Neste exemplo, `operacion` contén unha referencia á función `dobre`.

É importante distinguir entre:

| Expresión | Que fai? |
| --- | --- |
| `dobre` | Refírese á función, sen executala. |
| `dobre(5)` | Executa a función co argumento 5. |

Ao escribir o nome sen parénteses, estamos a referirnos á función. Ao engadir os parénteses, executámola.

### 🏷️ Tipos de función

O tipo dunha función indica os parámetros que recibe e o resultado que devolve. Estas son notacións de tipos, non programas para executar:

| Tipo | Que describe? |
| --- | --- |
| `void Function()` | Unha función sen parámetros nin resultado. |
| `int Function(int)` | Recibe un enteiro e devolve outro. |
| `String Function(String)` | Recibe un texto e devolve outro. |

En `int Function(int) operacion = dobre;`, o tipo indica que función podemos gardar en `operacion`.

### ✍️ Gardar unha función anónima

Unha **función anónima** non ten nome. Podemos gardala nunha variable e chamala a través desa variable:

```dart
void main() {
  String Function(String) saudar = (String nome) {
    return 'Ola, $nome!'; // Devolvemos o texto do saúdo.
  };
  print(saudar('Iria')); // Ola, Iria!
}
```

A mesma función cunha única expresión pode escribirse con frecha:

```dart
void main() {
  String Function(String) saudar = (String nome) => 'Ola, $nome!';
  print(saudar('Iria')); // O mesmo resultado que coa versión con bloque.
}
```

Nos primeiros exemplos indicamos os tipos para facilitar a lectura. Cando Dart poida deducilos, poderemos escribir `(nome)` en vez de `(String nome)`.

## 🔔 Callbacks: pasar unha función como argumento

Un **callback** é unha función que se pasa como argumento a outra función para que esta a execute.

A función que recibe o callback decide **cando** e **con que argumentos** debe chamalo.

### 🔔 Callback sen parámetros

Creamos unha función que recibe outra función como argumento:

```dart
void executarAccion(void Function() accion) {
  print('Comeza a acción');
  accion();
  print('Remata a acción');
}
```

Conserva a función `executarAccion()` anterior e engade este código para pasarlle unha función con nome:

```dart
void avisar() {
  print('Acción realizada');
}

void main() {
  executarAccion(avisar);
}
```

A saída será:

```plain
Comeza a acción
Acción realizada
Remata a acción
```

Fixémonos novamente na diferenza:

```dart
executarAccion(avisar);   // Pasamos a función como argumento.
// executarAccion(avisar()); // Incorrecto: executa avisar e non entrega unha función.
```

O parámetro `void Function() accion` recibe a función que imos executar.

Isto indica que `accion` debe ser unha función que:

- non recibe argumentos;
- non devolve ningún valor.

Conserva `executarAccion()` e substitúe só `main()` para pasar unha función anónima:

```dart
void main() {
  executarAccion(() {
    print('Rosa revisou o aviso'); // Callback escrito directamente aquí.
  });
}
```

Conservando tamén `executarAccion()`, podemos substituír `main()` por esta versión con frecha:

```dart
void main() {
  executarAccion(() => print('Julia compartiu un exercicio'));
}
```

### 📥 Callback con parámetros e valor de retorno

Os callbacks tamén poden recibir parámetros e devolver resultados:

```dart
int aplicarOperacion(
  int numero,
  int Function(int) operacion,
) {
  return operacion(numero);
}

int dobre(int numero) {
  return numero * 2;
}

void main() {
  print(aplicarOperacion(5, dobre)); // 10

  print(
    aplicarOperacion(5, (int numero) => numero * numero),
  ); // 25
}
```

Neste caso, `aplicarOperacion()` recibe:

1. un número;
2. unha función que recibe un `int` e devolve outro `int`.

A función `aplicarOperacion()` chama o callback e devolve o resultado obtido.

### 📱 Unha primeira ollada a Flutter

> 📌 Este fragmento require Flutter e vai dentro dunha interface. Non é un programa completo de consola. No Tema 2.2 faremos unha ponte a Flutter; na UD 03 veremos os widgets e os eventos con detalle.

Os callbacks empréganse con frecuencia en Flutter para indicar o que debe suceder cando se produce un evento:

```dart
ElevatedButton(
  onPressed: () {
    print('Premiches o botón');
  },
  child: const Text('Premer'),
)
```

A función asignada a `onPressed` é un callback que Flutter executará cando a persoa usuaria prema o botón.

## 🔒 Closures: conservar o acceso ás variables do contorno

Unha **closure** é unha función que conserva o acceso ás variables do ámbito no que foi creada, mesmo cando se executa noutro lugar ou noutro momento.

Unha closure pode ser unha función con nome ou unha función anónima. Polo tanto, non debemos confundir estes conceptos:

- Unha **función anónima** é unha función que non ten nome.
- Un **callback** é unha función que se pasa como argumento a outra función.
- Unha **closure** é unha función que captura variables do seu contorno.

### 🔒 Capturar unha variable exterior

Observa o seguinte exemplo:

```dart
void main() {
  String alumno = 'Xurxo';
  final saudar = () {
    print('Ola, $alumno'); // Usa a variable do contorno.
  };

  saudar(); // Ola, Xurxo
  alumno = 'Iria';
  saudar(); // Ola, Iria
}
```

A función `saudar()` utiliza a variable `alumno`, aínda que esta non foi declarada dentro da función nin se recibiu como parámetro.

Dicimos que `saudar()` **captura** a variable `alumno`.

Ademais, a closure non garda necesariamente unha copia do valor que tiña a variable cando se creou. Conserva o acceso á propia variable, polo que pode observar os seus cambios.

### 🔢 Conservar e modificar un estado

Unha función pode devolver outra función. Esa función devolta pode conservar unha variable e modificala entre chamadas:

```dart
int Function() crearContador() {
  int conta = 0;

  return () {
    conta++;
    return conta;
  };
}

void main() {
  final contador = crearContador();

  print(contador()); // 1
  print(contador()); // 2
  print(contador()); // 3
}
```

A variable `conta` créase dentro de `crearContador()`. Porén, a función devolta continúa tendo acceso a ela despois de que remate a execución de `crearContador()`.

`int Function()` antes de `crearContador` indica que devolve unha función sen parámetros que produce un enteiro. Por iso `crearContador()` devolve unha función e `contador()` devolve un número. A variable `conta` consérvase entre esas chamadas.

Cada contador mantén o seu propio estado. Conserva `crearContador()` e substitúe só `main()` por este código:

```dart
void main() {
  final contadorA = crearContador();
  final contadorB = crearContador();

  print(contadorA()); // 1
  print(contadorA()); // 2

  print(contadorB()); // 1

  print(contadorA()); // 3
  print(contadorB()); // 2
}
```

`contadorA` e `contadorB` son funcións diferentes. Cada unha conserva a súa propia variable `conta`.

### 🧩 Un callback que tamén é closure

```dart
void executarAccion(void Function() accion) {
  accion(); // Executamos a función recibida.
}

void main() {
  String alumno = 'Iria';
  executarAccion(() {
    print('Gardouse a nota de $alumno'); // Accedemos á variable exterior.
  });
}
```

É unha función **anónima** porque non ten nome, un **callback** porque se pasa a `executarAccion()` e unha **closure** porque captura `alumno`.

### 🧭 Diferenza entre os conceptos

| Concepto | Que significa? |
| --- | --- |
| Función como obxecto | Pode gardarse, pasarse como argumento ou devolverse. |
| Función anónima | É unha función que non ten nome. |
| Callback | É unha función que se pasa a outra función para que esta a execute. |
| Closure | É unha función que conserva o acceso ás variables do contorno no que foi creada. |

Unha mesma función pode pertencer a varias destas categorías ao mesmo tempo.

### ✏️ Exercicio de comprobación

Analiza o seguinte código antes de executalo:

```dart
int Function(int) crearSumador(int cantidade) {
  return (int numero) => numero + cantidade;
}

void main() {
  final sumarCinco = crearSumador(5);
  final sumarDez = crearSumador(10);

  print(sumarCinco(3));
  print(sumarDez(3));
}
```

**Preguntas:**

Analiza o código e responde as seguintes preguntas:

1. Que tipo de valor devolve `crearSumador()`?
2. Que variable captura a función devolta?
3. Cal será a saída do programa?
4. A función devolta é anónima? É un callback? É unha closure?
5. Como crearías unha función chamada `sumarVinte`?

**Respostas:**

1. Devolve unha función de tipo `int Function(int)`: recibe un enteiro e devolve outro.
2. Captura o parámetro `cantidade`; cada chamada a `crearSumador()` crea un contorno independente.
3. Mostra `8` e `13`, resultado de sumar 5 e 10 ao número 3.
4. É anónima e closure. Neste exemplo non é callback, porque non se pasa como argumento a outra función.
5. Dentro do mesmo `main()`, podemos engadir:

```dart
final sumarVinte = crearSumador(20);
print(sumarVinte(3)); // 23
```

> 📚 Consulta: [closures — https://dart.dev/language/functions#lexical-closures](https://dart.dev/language/functions#lexical-closures).

## 🧪 Exemplo completo

> 🔗 [Ver o código en DartPad — https://dartpad.dev/?id=be0f458633ba3c80b0344ff3bf672fe6](https://dartpad.dev/?id=be0f458633ba3c80b0344ff3bf672fe6)

[https://dartpad.dev/embed-inline.html?id=be0f458633ba3c80b0344ff3bf672fe6](https://dartpad.dev/embed-inline.html?id=be0f458633ba3c80b0344ff3bf672fe6)

O comentario `///` documenta a función: o editor pode mostralo cando a usamos.

> 📚 Consulta: [funcións en Dart - https://dart.dev/language/functions](https://dart.dev/language/functions).
