📌 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á
null
se 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
exclamativa
conté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
saudar
non devolve nada, Dart devolvenull
automaticamente.
📌 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
String
e 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
void
non 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í
yield
funciona 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 |