Funciones de Dart
Las funciones son la unidad básica de organización del código: encapsulan una lógica reutilizable y se invocan cuando sea necesario.
El sistema de funciones de Dart es muy flexible; admite parámetros nombrados, parámetros opcionales, funciones flecha y funciones de orden superior.
Definición de funciones y valor de retorno
Una función se compone de tipo de retorno, nombre de la función, lista de parámetros y cuerpo de la función.
Ejemplo
// String es el tipo de retorno, greet es el nombre de la función, (String name) es la lista de parámetros
String greet(String name) {
return '¡Hola, $name! Bienvenido a EJEMPLO.';
}
// Las funciones sin valor de retorno usan void
void printWelcome() {
print('=== Tutorial de Dart de EJEMPLO ===');
}
void main() {
printWelcome();
// Llamar a la función y recibir el valor de retorno
String message = greet('Xiaoming');
print(message);
}
=== EJEMPLO Dart 教程 === 你好,小明!欢迎来到 EJEMPLO.
Valor de retorno
Toda función tiene un valor de retorno.
Si no hay un return explícito, la función devuelve null de forma implícita (pero en el null safety esto causaría un desajuste de tipos, por lo que normalmente se usa void para indicar que no hay valor de retorno).
Ejemplo
int add(int a, int b) {
return a + b;
}
// Usar sintaxis de flecha para simplificar una función de una sola expresión
int multiply(int a, int b) => a * b;
// void indica que no hay un valor de retorno significativo
void log(String msg) {
print('[Registro EJEMPLO] $msg');
}
void main() {
print('3 + 5 = ${add(3, 5)}');
print('3 × 5 = ${multiply(3, 5)}');
log('Aprendizaje de funciones completado');
}
3 + 5 = 8 3 × 5 = 15 [EJEMPLO 日志] 函数学习完成
Parámetros nombrados y opcionales
Los parámetros de Dart se dividen en dos tipos: parámetros obligatorios (required) y opcionales.
Los opcionales se dividen a su vez en parámetros nombrados (named) y posicionales (positional).
Parámetros nombrados
Los parámetros nombrados se escriben entre llaves {}, se pasan por su nombre al llamar y su orden puede ser cualquiera.
Ejemplo
// La palabra clave required los marca como obligatorios
String createUser({
required String name, // Parámetro nombrado obligatorio
int age = 0, // Opcional, valor por defecto 0
String? email, // Opcional, puede ser null
bool isVip = false, // Opcional, valor por defecto false
}) {
var info = 'Usuario: $name, Edad: $age';
if (email != null) {
info += ', Email: $email';
}
if (isVip) {
info += ' [Usuario VIP]';
}
return info;
}
void main() {
// Parámetros nombrados: se pasan por nombre, el orden no importa
print(createUser(name: 'ejemplo', age: 10));
print(createUser(name: 'admin', email: '[email protected]', isVip: true));
// Los parámetros obligatorios no se pueden omitir
// createUser(); // Error: falta el parámetro obligatorio name
}
用户名: ejemplo, 年龄: 10 用户名: admin, 邮箱: [email protected] [VIP用户]
Parámetros posicionales
Los parámetros posicionales se escriben entre corchetes [] y se pasan en orden al llamar.
Ejemplo
String buildUrl(String host, [String path = '/', int port = 80]) {
return 'http://$host:$port$path';
}
void main() {
// Solo se pasan los obligatorios, los opcionales usan su valor por defecto
print(buildUrl('www.example.com'));
// Pasar 2 parámetros
print(buildUrl('www.example.com', '/dart'));
// Pasar 3 parámetros
print(buildUrl('localhost', '/api', 8080));
}
http://www.example.com:80/ http://www.example.com:80/dart http://localhost:8080/api
Parámetros nombrados vs posicionales: cómo elegir
| Escenario | Forma recomendada | Motivo |
|---|---|---|
| Muchos parámetros (más de 3) | Parámetros nombrados | Al llamar, el nombre del parámetro se autodocumenta y es difícil equivocarse en el orden. |
| El significado de los parámetros no es evidente | Parámetros nombrados | Valores como true/false no explican su propósito; añadir un nombre lo hace más claro. |
| Pocos parámetros y con significado claro | Parámetros posicionales | Como add(1, 2), simple y claro. |
| Construcción de widgets de Flutter | Parámetros nombrados | Estilo estándar de Flutter |
Una función no puede usar simultáneamente parámetros posicionales opcionales y parámetros nombrados opcionales. Solo puedes elegir una de las dos.
Valores de parámetros por defecto
Los parámetros opcionales pueden tener un valor por defecto, que se usa cuando quien llama no pasa ese parámetro.
Ejemplo
String formatMessage(
String content, {
String prefix = '[EJEMPLO]',
String suffix = '',
bool uppercase = false,
}) {
var result = '$prefix $content $suffix';
return uppercase ? result.toUpperCase() : result;
}
void main() {
// Usar todos los valores por defecto
print(formatMessage('El tutorial de Dart se ha actualizado'));
// Sobrescribir algunos valores por defecto
print(formatMessage('Aviso importante', prefix: '[Anuncio]', suffix: '!!!'));
// Sobrescribir el valor por defecto + activar mayúsculas
print(formatMessage('error', prefix: '[Error]', uppercase: true));
}
[EJEMPLO] Dart 教程更新了 [公告] 重要通知 !!! [错误] ERROR
Los valores por defecto deben ser constantes en tiempo de compilación. Es decir, un valor por defecto no puede ser DateTime.now() ni el valor de retorno de una función (a menos que esa función sea const). Si necesitas determinar el valor por defecto en tiempo de ejecución, puedes usar ?? dentro del cuerpo de la función.
Funciones flecha y funciones anónimas
Cuando el cuerpo de la función tiene una sola expresión, se puede abreviar con la sintaxis de flecha (=>).
Una función anónima es una función sin nombre, normalmente se pasa como parámetro a otras funciones.
Ejemplo
var numbers = [1, 2, 3, 4, 5];
// Función anónima (escritura completa)
numbers.forEach((number) {
print('Número EJEMPLO: $number');
});
print('---');
// Función flecha (abreviación de una sola expresión)
var doubled = numbers.map((n) => n * 2);
print('Doble: $doubled');
// Uso de la función flecha en la definición de funciones
// Cuando el cuerpo de la función tiene solo una declaración return, se puede abreviar con =>
int square(int x) => x * x;
print('Cuadrado de 5: ${square(5)}');
// Almacenar una función anónima en una variable
var sayHello = (String name) => '¡Hola, $name!';
print(sayHello('ejemplo'));
}
EJEMPLO 数字: 1 EJEMPLO 数字: 2 EJEMPLO 数字: 3 EJEMPLO 数字: 4 EJEMPLO 数字: 5 --- 翻倍: (2, 4, 6, 8, 10) 5 的平方: 25 你好,ejemplo!
Funciones de orden superior y cierres
Una función de orden superior es aquella que puede recibir una función como parámetro o devolver una función como resultado.
Un cierre es una función que puede acceder a variables de su ámbito léxico, incluso si se invoca fuera de ese ámbito.
Funciones de orden superior
Ejemplo
List<int> filterList(
List<int> items, bool Function(int) predicate) {
return items.where(predicate).toList();
}
// Función de orden superior: devuelve una función
Function makeMultiplier(int factor) {
// Devuelve un cierre: recuerda el factor externo
return (int n) => n * factor;
}
void main() {
var numbers = [10, 15, 20, 25, 30];
// Pasar una función anónima como condición de filtrado
var bigNumbers = filterList(numbers, (n) => n > 18);
print('Números de EJEMPLO mayores de 18: $bigNumbers');
// Obtener la función devuelta
var doubleIt = makeMultiplier(2);
var tripleIt = makeMultiplier(3);
print('5 × 2 = ${doubleIt(5)}');
print('5 × 3 = ${tripleIt(5)}');
}
EJEMPLO 大于18的数: [20, 25, 30] 5 × 2 = 10 5 × 3 = 15
Cierre
Un cierre es un objeto de función que puede acceder a las variables de su ámbito léxico, incluso si la función se invoca fuera de su ámbito original.
Ejemplo
Function makeCounter() {
int count = 0; // Esta variable es "capturada" por la función devuelta
return () {
count++;
return count;
};
}
void main() {
// counterA y counterB tienen cada uno su propio count independiente
var counterA = makeCounter();
var counterB = makeCounter();
print('Contador EJEMPLO A: ${counterA()}'); // 1
print('Contador EJEMPLO A: ${counterA()}'); // 2
print('Contador EJEMPLO B: ${counterB()}'); // 1 (contador independiente)
print('Contador EJEMPLO A: ${counterA()}'); // 3
}
EJEMPLO 计数器 A: 1 EJEMPLO 计数器 A: 2 EJEMPLO 计数器 B: 1 EJEMPLO 计数器 A: 3
Otras extensionesLa clave del cierre es que la función interna "recuerda" las variables de la función externa, incluso después de que la función externa haya terminado de ejecutarse. Cada llamada a makeCounter() crea una variable count y un cierre completamente nuevos, por lo que varios contadores no se afectan entre sí.