📌 Funcións
En Dart, as funcións poden:
- Asignarse a variables.
- Pasarse como argumentos.
- Ser devoltas desde outras funcións.
Incluso podes facer que unha clase actúe como función implementando o método call().
✅ Declaracións básicas
Unha función pode definirse de varias formas.
- A máis común é usar a palabra clave
tipo nome(parámetros) - Tamén se pode omitir o tipo de retorno no caso de que se poda inferir.
A seguinte función comproba se un elemento está nunha lista:
bool contenElemento(List<String> lista, String elemento) {
return lista.contains(elemento);
}
Tamén se pode definir omitindo o tipo de retorno (Dart dedúceo automaticamente):
contenElemento(lista, elemento) {
return lista.contains(elemento);
}
Ambas versións fan o mesmo: devolven true se o elemento está na lista, e false se non está.
🧪 Exemplo de uso:
void main() {
var froitas = ['mazá', 'laranxa', 'plátano'];
print(contenElemento(froitas, 'mazá')); // true
print(contenElemento(froitas, 'sandía')); // false
}
📌 Nota: Aínda que Dart permite omitir o tipo de retorno, é boa práctica poñelo cando sexa posible para facer o código máis claro.
✅ Sintaxe abreviada
Dart permite definir funcións dunha soa liña usando unha sintaxe abreviada empregando o operador =>.
Esta sintaxe é útil cando a función só devolve unha expresión simple.
Forma tradicional:
int dobrar(int numero) {
return numero * 2;
}
Forma abreviada:
int dobrar(int numero) => numero * 2;
As dúas versións fan exactamente o mesmo: multiplican o número por 2 e devolven o resultado.
🧪 Exemplo de uso:
void main() {
print(dobrar(3)); // 6
print(dobrar(10)); // 20
}
📌 Nota importante:
- Só se pode usar esta sintaxe con unha única expresión que devolve un valor.
- Non se pode usar se a función leva máis dunha liña ou necesita condicións, bucles, etc.
❌ Non permitido:
// Isto non funciona:
int exemplo(int numero) => if (numero > 0) numero * 2;
✅ Solución correcta:
int exemplo(int numero) => numero > 0 ? numero * 2 : 0;
Neste último caso usamos unha expresión condicional (operador ternario), que si está permitida porque é unha única expresión que devolve valor.
📌 Tipos de parámetros
Cando definimos unha función podemos pasarlle valores (parámetros). Hai tres tipos principais:
- Parámetros obrigatorios
- Parámetros nomeados
- Parámetros opcionais
✅ 1. Parámetros obrigatorios
Son os típicos: debes pasalos na orde correcta e sempre.
void saudar(String nome, String apelido) {
print('Ola $nome $apelido!');
}
saudar('Diego', 'Rodicio'); // ✅ Correcto
saudar('Diego'); // ❌ Erro: falta o segundo parámetro
✅ 2. Parámetros nomeados
Permiten indicar o nome do parámetro cando chamas á función. Son opcionais (por defecto), pero podes facer que sexan obrigatorios usando required.
Usa
{parametro1, parametro2, …}para especificar parámetros nomeados. Se non lles asignas un valor por defecto nin os marcas comorequired, o seu valor por defecto será null.
void saudar({String? nome, String? apelido}) {
print('Ola $nome $apelido!');
}
// Chamadas á función usando o nome dos parámetros:
saudar(nome: 'Diego', apelido: 'Rodicio'); // ✅ Ola Diego Rodicio
saudar(apelido: 'Rodicio', nome: 'Diego'); // ✅ Ola Diego Rodicio (a orde non importa)
saudar(nome: 'Diego'); // ✅ Ola Diego null
saudar(); // ✅ Ola null null
📌 Neste exemplo:
- Usamos
{}para definir parámetros nomeados.- Podemos chamar á función indicando os nomes dos parámetros (
nome:,apelido:).- A orde non importa, porque están nomeados.
- Son opcionais, así que podes omitir un ou todos, e o seu valor será
nullse non se especifica.
💡 Se queres obrigar a pasar algún deles, podes usar required:
void saudar({required String nome, String? apelido}) {
print('Ola $nome $apelido!');
}
saudar(); // ❌ Erro: falta o parámetro obrigatorio 'nome'
📌 En Flutter é moi común usar só parámetros nomeados, porque así se entende mellor o que representa cada valor.
✅ Valores por defecto en parámetros nomeados
Cando defines unha función con parámetros nomeados, podes indicar un valor por defecto. Isto significa que, se non se lle pasa ese valor cando se chama á función, Dart usará automaticamente o valor indicado.
📌 Sintaxe:
void mostrarMensaxe({String texto = 'Ola mundo'}) {
print(texto);
}
mostrarMensaxe(); // Imprime: Ola mundo
mostrarMensaxe(texto: 'Bo día!'); // Imprime: Bo día!
📌 Importante: O valor por defecto ten que ser unha constante, é dicir, algo que xa se poida calcular en tempo de compilación (números, cadeas, booleanos, listas ou mapas constantes…).
❌ Non permitido:
// Isto dá erro porque DateTime.now() non é constante:
void exemplo({DateTime data = DateTime.now()}) { ... }
✅ Permitido:
void exemplo({int intentos = 3}) { ... }
✅ 3. Parámetros opcionais
Son como os obrigatorios, pero opcionais e colócanse entre corchetes.
void saudar(String nome, [String? apelido]) {
if (apelido != null) {
print('Ola $nome $apelido!');
} else {
print('Ola $nome!');
}
}
saudar('Diego'); // Ola Diego!
saudar('Diego', 'Rodicio'); // Ola Diego Rodicio!
🚫 Non se poden mesturar [] e {} na mesma función
Este código non está permitido:
// ❌ Non válido
void exemplo(String nome, [int? idade], {bool? activo}) {
// ...
}
📌 Dart non permite mesturar os dous tipos opcionais (
[]e{}) nunha mesma definición de función.
✅ Entón… como se pode combinar?
Só se poden combinar:
- Parámetros posicionais obrigatorios
- E despois un tipo de parámetros opcionais: ou
[], ou{}, pero non ambos.
// Válido: obrigatorio + nomeados
void exemplo(String nome, {int? idade, bool? activo}) {
// ...
}
// Válido: obrigatorio + posicionais opcionais
void exemplo2(String nome, [int? idade, bool? activo]) {
// ...
}
🧠 Resumo visual
| Tipo | Símbolo | Exemplo de uso | Obrigatorio? |
|---|---|---|---|
| Obrigatorio | — | String nome | ✅ Si |
| Opcional | [] | [int? idade] | ❌ Non |
| Nomeado | {} | {bool? activo} | ❌ Non |
| Nomeado obrigatorio | {} + required | {required bool activo} | ✅ Si |
📌 Función main()
En Dart, toda aplicación debe comezar cunha función chamada main(). Esta función é o punto de entrada do programa: é o primeiro que se executa cando lanzas a app.
✅ Forma básica
A forma máis sinxela da función main() non leva argumentos:
void main() {
print('Ola, mundo!');
}
✅ Con argumentos
Tamén podes definir a función main() con un parámetro: unha lista de cadeas (List<String>), que recibe os argumentos da liña de comandos.
void main(List<String> argumentos) {
print(argumentos);
}
💡 Isto é útil en programas de consola que aceptan valores ao executarse.
🧪 Exemplo:
void main(List<String> argumentos) {
if (argumentos.isEmpty) {
print('Non se pasou ningún argumento.');
} else {
print('Argumentos recibidos:');
for (var argumento in argumentos) {
print('- $argumento');
}
}
}
Se executas este programa con:
dart run programa.dart Maceda Ourense
A saída sería:
Argumentos recibidos:
- Maceda
- Ourense
📌 Funcións como obxectos
En Dart, as funcións son obxectos. Isto significa que se poden:
- Gardar en variables.
- Pasar como argumentos a outras funcións.
- Devolver desde outras funcións.
✅ 1. Pasar unha función como parámetro
A función forEach aplica outra función a cada elemento dunha lista. Podemos pasar unha función xa definida como parámetro:
void imprimirElemento(int elemento) {
print(elemento);
}
void main() {
var numeros = [1, 2, 3];
numeros.forEach(imprimirElemento); // Pásase a función sen parénteses
}
📌 Fíxate en que non se chama á función, simplemente se pasa como obxecto (
imprimirElemento, nonimprimirElemento()).
✅ 2. Gardar unha función nunha variable
Podemos asignar unha función anónima (ou lambda) a unha variable e usala máis tarde:
var exclamativa = (String mensaxe) => '!!! ${mensaxe.toUpperCase()} !!!';
void main() {
print(exclamativa('ola')); // !!! OLA !!!
}
Neste exemplo:
- A variable
exclamativacontén unha función. - Podemos chamala como se fose unha función normal.
✅ 3. Devolver unha función desde outra
Unha función tamén pode devolver outra función:
Function crearSaudo(String prefixo) {
return (String nome) => '$prefixo $nome!';
}
void main() {
var saudar = crearSaudo('Bo día');
print(saudar('María')); // Bo día María!
}
🧠 Resumo
| Acción | Exemplo |
|---|---|
| Pasar función como argumento | lista.forEach(funcion) |
| Gardar función nunha variable | var f = (x) => x + 1; |
| Devolver unha función desde outra | Function x() => () => ... ; |
📌 Closures (tamén chamados funcións anónimas ou lambda)
En Dart, é posible crear funcións sen nome, chamadas de varios modos:
- Funcións anónimas
- Lambdas
- Closures
Son moi útiles para pasar funcións como argumentos ou usalas temporalmente.
✅ Estrutura dun closure
Unha función anónima defínese así:
(parámetros) {
// corpo da función
}
Tamén pode escribirse en forma abreviada:
(parámetros) => expresión;
🧪 Exemplo práctico: converter elementos dunha lista
A función
map()é un método dispoñible nas listas (List) e noutros obxectos iterables. Serve para transformar os elementos dunha lista, devolvendo unha nova secuencia cos resultados.
A función map:
- Argumento: recibe unha función (normal ou anónima).
- Esa función aplícase a cada elemento da lista orixinal.
- O resultado é un
Iterable(unha especie de lista), que normalmente convertimos cun.toList().
const froitas = ['mazás', 'plátanos', 'laranxas'];
// Usamos unha función anónima dentro de map()
var enMaiusculas = froitas.map((elemento) {
return elemento.toUpperCase();
}).toList();
// Mostramos cada froita e a súa lonxitude
enMaiusculas.forEach((f) => print('$f: ${f.length}'));
✅ Que fai cada parte?
froitas.map(...): percorre cada elemento da listafroitas.(elemento) { return elemento.toUpperCase(); }: converte a froita a maiúsculas..toList(): converte o resultado nunha lista.forEach(...): percorre a nova lista para imprimir o resultado.
🎯 Resultado deste exemplo:
MAZÁS: 5
PLÁTANOS: 8
LARANXAS: 8
🧠 Resumo
| Función | Para que serve |
|---|---|
map() | Transforma cada elemento dunha lista |
toList() | Converte o resultado nunha nova lista |
forEach() | Realiza unha acción con cada elemento (sen devolver nada) |
📌
map()non modifica a lista orixinal. Crea unha nova.
✅ Outro exemplo: duplicar números
var numeros = [1, 2, 3, 4];
var dobrados = numeros.map((n) => n * 2).toList();
print(dobrados); // [2, 4, 6, 8]
✅ Exemplo simple: sumar números
var sumar = (int a, int b) {
return a + b;
};
print(sumar(3, 4)); // 7
Aquí sumar é unha variable que contén unha función anónima.
✅ Exemplo con sintaxe abreviada
var dobrar = (int n) => n * 2;
print(dobrar(5)); // 10
✅ Exemplo sen parámetros
Tamén podes ter funcións anónimas sen parámetros:
var saudo = () => print('Bo día!');
saudo(); // Bo día!
✅ Exemplo como callback
List<int> numeros = [1, 2, 3, 4];
numeros.forEach((n) {
print('Número: $n');
});
📌 Valores de retorno
En Dart, todas as funcións devolven un valor. Se non especificas nada, Dart devolve null por defecto.
✅ Devolver un valor simple
int sumar(int a, int b) {
return a + b;
}
void main() {
var resultado = sumar(3, 5);
print(resultado); // 8
}
A función sumar() devolve un número enteiro (int). O tipo vai antes do nome da función.
✅ Se non poñemos return
void saudar(String nome) {
print('Ola $nome');
}
void main() {
var resultado = saudar('Ana');
print(resultado); // null
}
📌 Como a función
saudarnon devolve nada, Dart devolvenullautomaticamente.
📌 Devolver varios valores
En Dart, non se poden devolver varios valores separados, pero podemos usar un rexistro (record) para empaquetalos nun único valor.
✅ Exemplo:
(String, int) obterNomeEIdade() {
return ('Lucía', 17);
}
void main() {
var (nome, idade) = obterNomeEIdade();
print('Nome: $nome');
print('Idade: $idade');
}
Neste exemplo:
- A función devolve un rexistro con dúas posicións: unha
Stringe unint. - Podemos desempaquetar o resultado usando
var (nome, idade).
✅ Outro exemplo: devolver lista ou mapa
Tamén se poden devolver outros tipos de datos compostos:
List<int> primeirosTres() {
return [1, 2, 3];
}
Map<String, dynamic> usuario() {
return {
'nome': 'Xoán',
'idade': 20,
};
}
✅ Función que non devolve nada (void)
void mostrarMensaxe() {
print('Ola!');
}
📌 As funcións
voidnon retornan nada (ou implicitamente retornannull).
🧠 Resumo
| Tipo de retorno | Exemplo |
|---|---|
| Valor simple | int sumar() => 5; |
| Sen retorno | void saudar() { ... } |
| Múltiples valores | return ('texto', 42); |
| Lista/mapa | return [1,2,3] ou {clave: valor} |
📌 A maiores…Xeradores
Un xerador é un tipo especial de función que xera elementos un a un, en lugar de devolvelos todos á vez.
Son útiles cando:
- A lista é moi longa ou infinita.
- Non queremos calcular todos os valores de golpe.
- Queremos aforrar memoria e recursos.
En Dart, úsanse coas palabras clave:
sync*→ para funcións normais (síncronas).async*→ para funcións asíncronas (conawait).yield→ para devolver valores un a un.yield*→ para delegar nun outro xerador.
✅ Xerador síncrono
Un xerador síncrono devolve un Iterable, é dicir, unha lista que se pode percorrer cun bucle for.
Iterable<int> naturaisAta(int n) sync* {
int k = 0;
while (k < n) {
yield k;
k++;
}
}
void main() {
for (var numero in naturaisAta(5)) {
print(numero);
}
}
// Saída: 0 1 2 3 4
📌 Aquí
yieldfunciona coma unreturn, pero permite seguir desde onde quedou.
✅ Xerador asíncrono
Un xerador asíncrono devolve un Stream, útil cando os datos chegan pouco a pouco (como por rede).
Stream<int> naturaisAsincronosAta(int n) async* {
int k = 0;
while (k < n) {
yield k;
await Future.delayed(Duration(seconds: 1)); // espera 1 segundo
k++;
}
}
void main() async {
await for (var numero in naturaisAsincronosAta(3)) {
print(numero);
}
}
// Saída: imprime un número por segundo
✅ Xerador recursivo
Tamén podes chamar a outro xerador dentro dun xerador, usando yield*.
Iterable<int> naturaisDescendentes(int n) sync* {
if (n > 0) {
yield n;
yield* naturaisDescendentes(n - 1);
}
}
void main() {
for (var n in naturaisDescendentes(3)) {
print(n);
}
}
// Saída: 3 2 1
🧠 Resumo
| Tipo | Palabra clave | Retorna | Exemplo de uso |
|---|---|---|---|
| Síncrono | sync* | Iterable | for (var x in ...) |
| Asíncrono | async* | Stream | await for (var x in ...) |
| Envío de valor | yield | valor único | dentro do bucle |
| Delegación | yield* | outro xerador | dentro da función |