Function

En Dart, las funciones también son objetos, lo que lo diferencia un poco de otros lenguajes de programación.

Las funciones son objetos de primera clase

Dart es un lenguaje totalmente orientado a objetos: las propias funciones son objetos, con el tipo Function.

Se pueden asignar a variables y pasarse como argumentos a otras funciones;

Las instancias de clase se pueden invocar directamente como si fueran funciones

1 Definición estándar de función

// Declaración completa con anotaciones de tipo de retorno y parámetros
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}
Lenguaje del código: Dart (dart)

Ejemplo completo

// Tabla de correspondencia de números atómicos de gases nobles
final Map<int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};

// Declaración completa con tipo de retorno y tipos de parámetros
// El tipo int no admite valores null; usa int? para aceptar parámetros anulables
bool isNoble(int? atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null no es una clave del mapa → false
  print(isNoble(2));          // Coincide con un gas noble → true
  print(isNoble(9));          // Sin coincidencia → false
  print(isNoble(86));         // Coincide con un gas noble → true
}
Lenguaje del código: Dart (dart)
false
true
false
trueLenguaje del código: JavaScript (javascript)

2 Omitir anotaciones de tipo

La guía de estilo Effective Dart establece que las APIs públicas visibles externamente deben llevar anotaciones de tipo explícitas y completas.

Se pueden omitir las anotaciones de tipo en funciones privadas internas

Ejemplo


final Map<int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};


isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null no es una clave del mapa → false
  print(isNoble(2));          // Coincide con un gas noble → true
  print(isNoble(9));          // Sin coincidencia → false
  print(isNoble(86));         // Coincide con un gas noble → true
}

Lenguaje del código: Dart (dart)

En el ejemplo anterior, la función isNoble no especifica el tipo de retorno bool ni el tipo de parámetro int?, pero funciona perfectamente.

Cuando no escribes tipos, Dart realiza una inferencia de tipos automática:

  • Parámetro atomicNumber: Sin tipo explícito → se infiere como dynamic (tipo dinámico, acepta cualquier valor: números, cadenas, null)
  • Valor de retorno: Sin anotación de tipo al principio → Dart deduce que el tipo de retorno es bool a partir de la expresión tras return, sin escribirlo en el código
¿Por qué las APIs públicas deben llevar tipos explícitos obligatoriamente?

Legibilidad: Los desarrolladores que llamen tu función ven inmediatamente los tipos de entrada y salida, sin tener que leer el código interno para adivinar;

Seguridad de tipos: El tipo dynamic desactiva la comprobación estática; pasar tipos incorrectos no genera errores de compilación y suele provocar fallos en ejecución;

Intellisense del IDE: Las anotaciones de tipo activan el autocompletado y los avisos de parámetros en el editor; sin tipos, el IDE no puede ofrecer sugerencias precisas.

¿Cuándo se pueden omitir las anotaciones de tipo?

Solo recomendado para código privado local: funciones usadas exclusivamente dentro del archivo actual y nunca invocadas desde fuera.

Sintaxis abreviada de flecha (=>)

Si el cuerpo de la función (código entre {}) solo contiene una única expresión de retorno, puedes usar la sintaxis de flecha.

Ejemplo

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
Lenguaje del código: Dart (dart)
  • => expr equivale a { return expr; };
  • Solo se pueden escribir expresiones entre la flecha y el punto y coma; sentencias como if están prohibidas (usa operadores ternarios en su lugar).
// Tabla de correspondencia de números atómicos de gases nobles
final Map<int, String> _nobleGases = {
  2: 'Helio',
  10: 'Neón',
  18: 'Argón',
  36: 'Kriptón',
  54: 'Xenón',
  86: 'Radón',
};

// Función abreviada de flecha: retorna directamente una expresión en una línea, equivalente a funciones de bloque con return
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

void main() {
  // Probar gases nobles existentes
  print(isNoble(2));   // true
  print(isNoble(18));  // true

  // Probar elementos comunes
  print(isNoble(6));   // false (Carbono)
  print(isNoble(99));  // false (sin elemento coincidente)
}
Lenguaje del código: Dart (dart)

Parámetros

Las funciones admiten parámetros posicionales obligatorios, seguidos de una única opción: parámetros con nombre o parámetros posicionales opcionales — no se pueden mezclar ambos tipos.

Las comas finales , están permitidas al definir o invocar parámetros para mejorar la legibilidad del código.

1. Parámetros con nombre Named parameters

Delimitados con {}, son opcionales por defecto; agrega required para obligar a enviarlos.

1) Parámetros con nombre anulables básicos (sin valor por defecto, su valor predeterminado es null, el tipo debe marcarse con ? para ser anulable)

void enableFlags({bool? bold, bool? hidden}) {}
// Especifica explícitamente los nombres de los parámetros al invocar
enableFlags(bold: true, hidden: false);
Lenguaje del código: Dart (dart)

Ejemplo completo

/// Activar banderas de estilo de texto: negrita, oculto
void enableFlags({bool? bold, bool? hidden}) {
  print('Negrita: $bold, Oculto: $hidden');
}

void main() {
  // Enviar todos los parámetros con nombre
  enableFlags(bold: true, hidden: false);

  // Enviar solo un parámetro con nombre, el otro queda null
  enableFlags(bold: true);

  // Enviar solo hidden
  enableFlags(hidden: true);

  // No enviar ningún parámetro, ambos valores son null
  enableFlags();
}
Lenguaje del código: Dart (dart)

Resultado de ejecución

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Negrita: true, Oculto: false
Negrita: true, Oculto: null
Negrita: null, Oculto: true
Negrita: null, Oculto: nullLenguaje del código: JavaScript (javascript)

2) Establecer valores por defecto constantes en tiempo de compilación (no hace falta marcar el tipo como anulable al existir un valor predeterminado)

void enableFlags({bool bold = false, bool hidden = false}) {}
enableFlags(bold: true); // hidden usa automáticamente false

Lenguaje del código: Dart (dart)

Ejemplo

/// Activar banderas de estilo de texto: negrita, oculto
void enableFlags({bool bold = false, bool hidden = false}) {
  print('Negrita: $bold, Oculto: $hidden');
}

void main() {
  enableFlags();//Sin parámetros enviados, ambos usan el valor por defecto false → Negrita: false, Oculto: false
}
Lenguaje del código: Dart (dart)

3) Parámetros con nombre obligatorios con required (Nota: Muy usados en constructores de componentes Flutter). El ejemplo siguiente es código Flutter; aprende más en los tutoriales de Flutter.

const Scrollbar({super.key, required Widget child});
// Los parámetros required también aceptan tipos anulables
const Scrollbar({super.key, required Widget? child});

Lenguaje del código: Dart (dart)

Dart no obliga a colocar los parámetros con nombre al final de la lista; se pueden insertar en cualquier posición de la lista de parámetros

repeat(times: 2, () {})Lenguaje del código: CSS (css)
2. Parámetros posicionales opcionales Optional positional parameters

Delimitados con [], su valor es null si se omiten; se pueden definir valores por defecto constantes.

// Sin valor por defecto, tipo marcado con ?
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) result = '$result with a $device';
  return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');

Lenguaje del código: Dart (dart)

Ejemplo completo

// [String? device] parámetro posicional opcional sin valor por defecto, vale null si se omite
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device'; //Agregar información del dispositivo al mensaje si device no es null
  }
  return result;
}

void main() {
  // Omitir el tercer parámetro opcional
  String res1 = say('Bob', 'Howdy');
  print(res1);

  // Enviar el tercer parámetro posicional opcional
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2);

  // Enviar manualmente null al parámetro opcional
  String res3 = say('Alice', 'Hi', null);
  print(res3);
}
Lenguaje del código: Dart (dart)

Establecer valores por defecto:

String say(String from, String msg, [String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');

Lenguaje del código: Dart (dart)

Ejemplo completo

// Parámetro posicional opcional con valor por defecto carrier pigeon (paloma mensajera)
String say(String from, String msg, [String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}

void main() {
  // Omitir el tercer parámetro opcional, usa automáticamente el valor por defecto carrier pigeon
  String res1 = say('Bob', 'Howdy');
  print(res1); //Bob says Howdy with a carrier pigeon

  // Enviar dispositivo personalizado para sobrescribir el valor por defecto
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2); //Bob says Howdy with a smoke signal

  String res3 = say('Alice', 'Hello', 'mobile phone');
  print(res3); // Alice says Hello with a mobile phone
  
  String res4 = say('Jack', 'Tom', 'website foxdevelop.com');
  print(res4); // Jack says Tom with a website foxdevelop.com
}
Lenguaje del código: Dart (dart)

Resultado de ejecución

Built firstdart:firstdart.
Bob says Howdy with a carrier pigeon
Bob says Howdy with a smoke signal
Alice says Hello with a mobile phone
Jack says Tom with a website foxdevelop.comLenguaje del código: JavaScript (javascript)


Función de punto de entrada main ()

Cada programa Dart debe tener una función main() de nivel superior; es el único punto de ejecución del programa.

  1. Versión básica sin parámetros
void main() {
  print('Hello, welcome to foxdevelop.com!');
}

Lenguaje del código: Dart (dart)
Hello, welcome to foxdevelop.com!Lenguaje del código: CSS (css)
  1. Programa de línea de comandos que recibe argumentos de lanzamiento
// Comando de ejecución: dart run firstdart.dart 1 test
void main(List<String> arguments) {
  print(arguments); // [1, test]
}

Lenguaje del código: Dart (dart)

Usa la librería oficial args para analizar argumentos de línea de comandos complejos.

Reemplaza firstdart.dart anterior por el nombre de tu archivo Dart


Las funciones como objetos de primera clase First-class objects

1. Pasar funciones como parámetros

void printElement(int element) => print(element);
var list = [1, 2, 3];
list.forEach(printElement); // Pasar la función directamente

Lenguaje del código: Dart (dart)

Ejemplo completo

void main() {
    void printElement(int element) => print(element);
	var list = [1, 2, 3];
	list.forEach(printElement); // Pasar la función directamente
}
Lenguaje del código: Dart (dart)

Salida

1
2
3

2. Asignar funciones anónimas a variables

void main() {
    var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
	print(loudify('hello')); //!!! HELLO !!! 
}
Lenguaje del código: Dart (dart)

Tipos de función Function Type

Puedes declarar explícitamente los tipos de variables de función

Formato: TipoRetorno Function(ListaParametros, {ParametrosConNombre})

void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
// Declarar variable de tipo función
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');

Lenguaje del código: Dart (dart)

Usa typedef para crear alias de tipos de función, mejorar la reutilización y legibilidad.

{String greeting}) → Un parámetro con nombre greeting de tipo String

void main() {
    void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
	// Declarar variable de tipo función
	void Function(String, {String greeting}) g = greet;
	g('Jack', greeting: 'Howdy');
}
Lenguaje del código: Dart (dart)

Salida

Howdy Jack!

Funciones anónimas (Lambda / Closure cierre léxico)

Funciones sin nombre definido, dos formas de escritura:

  1. Sintaxis de bloque (varias líneas)
void main() {
    const list = ['apples', 'bananas'];
	var uppercaseList = list.map((item) {
	  return item.toUpperCase();
	}).toList();
	print(uppercaseList);
}
Lenguaje del código: Dart (dart)

(item) { return item.toUpperCase(); } en el código anterior es una función anónima (lambda)

Salida: [APPLES, BANANAS]

  1. Abreviatura de flecha (expresión de una sola línea)
void main() {
    const list = ['fox', 'develop','foxdevelop.com'];
    var uppercaseList = list.map((item) => item.toUpperCase()).toList();
    uppercaseList.forEach((item) => print('$item: ${item.length}'));
}

Lenguaje del código: Dart (dart)

Salida

FOX: 3
DEVELOP: 7
FOXDEVELOP.COM: 14Lenguaje del código: HTTP (http)

Ámbito léxico Lexical scope

Dart usa un ámbito léxico estático: el ámbito de una variable se define por la jerarquía de llaves del código, y los ámbitos internos pueden acceder a todas las variables de los ámbitos externos.

bool topLevel = true;
void main() {
  var insideMain = true;
  void myFunction() {
    var insideFunction = true;
    void nestedFunction() {
      var insideNestedFunction = true;
      // Todas accesibles
      assert(topLevel && insideMain && insideFunction && insideNestedFunction);
    }
  }
}

Lenguaje del código: Dart (dart)

Cierres léxicos Lexical closures

Cierre: un objeto función puede capturar y conservar variables del ámbito externo, y acceder a ellas incluso después de abandonar el ámbito original.

Function makeAdder(int addBy) {
  // Capturar variable externa addBy
  return (int i) => addBy + i;
}

void main() {
  var add2 = makeAdder(2);
  var add4 = makeAdder(4);
  assert(add2(3) == 5);
  assert(add4(3) == 7);
}

Lenguaje del código: Dart (dart)

Desglose paso a paso del flujo de ejecución anterior

Paso 1: var add2 = makeAdder(2);

  1. Ejecutar makeAdder, pasar addBy = 2
  2. Crear función anónima: (int i) => 2 + i
  3. Asignar esta función a la variable add2. En este momento add2 es un objeto función cuyo cuerpo es (int i) => 2 + i; recibe un entero i y retorna i+2
  4. La ejecución de la función makeAdder finaliza, pero la función anónima recuerda permanentemente addBy=2

Paso 2: var add4 = makeAdder(4);

  1. Ejecutar makeAdder de nuevo, pasar addBy = 4
  2. Crear una nueva función anónima totalmente independiente: (int i) => 4 + i
  3. Asignar a add4, que almacena su propia copia independiente de addBy=4 sin interferir con add2. Es decir, el valor addBy se guarda por separado en los dos objetos función add2 y add4, uno guarda 2 y el otro 4
  • La llamada a makeAdder(2) genera un cierre independiente con un valor vinculado único addBy = 2, asignado a add2;
  • La llamada a makeAdder(4) genera un cierre totalmente nuevo y separado con un valor vinculado único addBy = 4, asignado a add4;
  • add2 y add4 son objetos función completamente separados, cada uno almacena en privado su variable addBy capturada sin interferirse mutuamente.

Paso 3: Invocar add2(3)

Ejecutar función interna: 2 + 3 = 5, la aserción se cumple

Paso 4: Invocar add4(3)

Ejecutar función interna: 4 + 3 = 7, la aserción se cumple

Tear-offs de función

Si invocas una función/método sin paréntesis, creas un tear-off (cierre ligero), que reemplaza las lambdas anónimas escritas manualmente para un código más limpio.

Esta es la sintaxis recomendada; solo tienes que pasar la función directamente.

var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();
charCodes.forEach(print); // Tear-off de función
charCodes.forEach(buffer.write); // Tear-off de método

Lenguaje del código: Dart (dart)

Sintaxis equivalente más verbosa

charCodes.forEach((code) => print(code));
charCodes.forEach((code) => buffer.write(code));

Lenguaje del código: Dart (dart)

Comprobación de igualdad de funciones

  • Funciones de nivel superior, métodos estáticos: == devuelve true si los nombres de función coinciden exactamente;
  • Métodos de instancia: solo los tear-offs vinculados a la misma instancia de objeto se consideran iguales.
void foo() {}
class A {
  static void bar() {}
  void baz() {}
}
void main() {
  var x = foo;
  assert(foo == x);

  x = A.bar;
  assert(A.bar == x);

  var v = A(), w = A(), y = w;
  x = w.baz;
  assert(y.baz == x); // Misma instancia → iguales
  assert(v.baz != w.baz); // Distintas instancias → no iguales
}

Lenguaje del código: Dart (dart)

Valores de retorno Return values

  1. Todas las funciones tienen valor de retorno: si no existe un return explícito, se añade implícitamente return null; al final
foo() {}
assert(foo() == null);

Lenguaje del código: Dart (dart)
  1. Retornar varios valores: usar tuplas Record
(String, int) foo() {
  return ('something', 42);
}

Lenguaje del código: Dart (dart)

Accesores Getter & Setter

Leer o asignar cualquier propiedad equivale fundamentalmente a llamar su getter o setter; las variables normales generan automáticamente accesores implícitos, también puedes definirlos explícitamente a mano.

Ventaja: el código externo accede uniformemente con .nombrePropiedad, la lógica interna se puede modificar en cualquier momento sin actualizar el código llamante externo.

String _secret = 'Hello';

// Getter para lectura
String get secret {
  print('Se invoca el getter');
  return _secret.toUpperCase();
}

// Setter para asignaciones
set secret(String newMsg) {
  print('Se invoca el setter');
  if (newMsg.isNotEmpty) _secret = newMsg;
}

void main() {
  print(secret); // Lectura, activa getter → HELLO
  secret = 'Dart is fun'; // Asignación, activa setter
  print(secret); // DART IS FUN
}

Lenguaje del código: Dart (dart)

* Generadores Generators

Generan secuencias de forma diferida bajo demanda, dos variantes: sincrónico y asincrónico, usan yield para emitir valores.

  1. Generador sincrónico sync*, retorna Iterable
Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

Lenguaje del código: Dart (dart)
  1. Generador asincrónico async*, retorna Stream
Stream<int> asyncNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

Lenguaje del código: Dart (dart)
  1. Optimización de generadores recursivos yield* (delega a otro generador para mejorar el rendimiento)
Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

Lenguaje del código: Dart (dart)

Function

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *