diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

View Categories

📌 Funcions

📌 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 como required, 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 #

TipoSímboloExemplo de usoObrigatorio?
ObrigatorioString 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, non imprimirElemento()).

✅ 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ónExemplo
Pasar función como argumentolista.forEach(funcion)
Gardar función nunha variablevar f = (x) => x + 1;
Devolver unha función desde outraFunction 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? #

  1. froitas.map(...): percorre cada elemento da lista froitas.
  2. (elemento) { return elemento.toUpperCase(); }: converte a froita a maiúsculas.
  3. .toList(): converte o resultado nunha lista.
  4. forEach(...): percorre a nova lista para imprimir o resultado.

🎯 Resultado deste exemplo: #

MAZÁS: 5
PLÁTANOS: 8
LARANXAS: 8

🧠 Resumo #

FunciónPara 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 devolve null 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 un int.
  • 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 retornan null).

🧠 Resumo #

Tipo de retornoExemplo
Valor simpleint sumar() => 5;
Sen retornovoid saudar() { ... }
Múltiples valoresreturn ('texto', 42);
Lista/mapareturn [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 (con await).
  • 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 un return, 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 #

TipoPalabra claveRetornaExemplo de uso
Síncronosync*Iterablefor (var x in ...)
Asíncronoasync*Streamawait for (var x in ...)
Envío de valoryieldvalor únicodentro do bucle
Delegaciónyield*outro xeradordentro da función

Exemplos #

🔗 Ver o código en DartPad