Diferencia entre revisiones de «Flutter DART Funciones»

De MediaWiki
Ir a la navegación Ir a la búsqueda
 
(No se muestran 32 ediciones intermedias del mismo usuario)
Línea 1: Línea 1:
 
==Introducción==
 
==Introducción==
  
−
* Las funciones en DART funcionan igual que en cualquier lenguaje de programación orientado a objetos, por lo que no voy a explicar nada del mismo.
+
* En Dart, las funciones permiten agrupar código que puede ejecutarse desde diferentes partes de un programa.
−
: Como comenté, este curso está orientada a alumnos que ya saben programar en algún lenguaje..  
+
  : Como este curso está orientado a alumnado que ya sabe programar en algún lenguaje, no profundizaremos en los conceptos generales de las funciones, sino en las características específicas que ofrece Dart.
 +
 
 +
* Es recomendable indicar el tipo de los parámetros que recibe una función y el tipo del dato que devuelve.
  
 
* Veamos un ejemplo de función con parámetros:
 
* Veamos un ejemplo de función con parámetros:
−
:<syntaxhighlight lang="java" enclose="div" highlight="1-3" >
+
 
−
double sumarValores(double val1, double val2){
+
:<syntaxhighlight lang="dart" enclose="div" highlight="1-3">
−
  return val1+val2;
+
double sumarValores(double val1, double val2) {
 +
return val1 + val2;
 
}
 
}
  
 
void main() {
 
void main() {
−
+
final num1 = 10.32;
−
  final num1 = 10.32;
+
final num2 = 1.35;
−
  final num2 = 1.35;
 
−
 
 
−
  print(sumarValores(num1,num2));
 
−
}
 
−
</syntaxhighlight>
 
  
 +
print(sumarValores(num1, num2));
 +
} </syntaxhighlight>
  
 
<br />
 
<br />
 +
 
==Paso de parámetros==
 
==Paso de parámetros==
  
−
* Al igual que otros lenguajes podemos pasar datos a las funciones empleando los parámetros.
+
* Al igual que en otros lenguajes, podemos pasar datos a las funciones empleando parámetros.
−
: Como ya vimos, [https://wiki.cifprodolfoucha.es/index.php?title=Flutter_DART_variables_-_tipos_de_datos#Valores_nulos podemos definir los parámetros para que acepten valores nulos].
+
 
 +
: Como ya vimos anteriormente, podemos definir parámetros que admitan valores nulos utilizando los tipos ''nullable'', como por ejemplo <code>String?</code>, <code>int?</code> o <code>double?</code>.
  
 
<br />
 
<br />
  
−
===Por valor/referencia===
+
===Paso por valor===
 +
 
 +
* En Dart, los argumentos de una función se pasan '''por valor'''.
 +
 
 +
: Las variables de Dart contienen referencias a objetos. Cuando enviamos una variable como argumento de una función, se copia el valor almacenado en dicha variable.
 +
 
 +
: Si dicho valor es una referencia a un objeto, se copia la referencia. Por tanto, tanto la variable original como el parámetro pueden hacer referencia al mismo objeto.
 +
 
 +
: Esto provoca dos comportamientos que debemos diferenciar:
 +
 
 +
:* Si reasignamos el parámetro dentro de la función, no modificamos la variable utilizada en la llamada.
  
−
* Al igual que en Java, el paso va a ser por valor en los tipos de datos 'simples' como: int, double, bool y String (también String :) )
+
:* Si utilizamos la referencia recibida para modificar el estado de un objeto mutable, el cambio será visible fuera de la función, ya que ambas referencias permiten acceder al mismo objeto.
−
: Los valores que sean objetos de una clase serán pasados por referencia.
 
  
−
:<syntaxhighlight lang="java" enclose="div" highlight="" >
+
:<syntaxhighlight lang="dart" enclose="div">
 
class Pelota {
 
class Pelota {
 
   double? peso;
 
   double? peso;
Línea 39: Línea 50:
 
}
 
}
  
−
void modificarValores(Pelota pelota, int valor){
+
void modificarValores(
−
 
+
  Pelota pelota,
 +
  int valor,
 +
  String cadena,
 +
) {
 
   valor = 10;
 
   valor = 10;
 +
  cadena = 'NUEVA CADENA';
 +
 
   pelota.peso = 100.2;
 
   pelota.peso = 100.2;
 
   pelota.id = 'P2';
 
   pelota.id = 'P2';
−
 
 
 
}
 
}
 +
 
void main() {
 
void main() {
−
    
+
   var valorPrueba = 5;
−
  int valorPrueba = 5;
+
   var cadena = 'VIEJA CADENA';
−
   Pelota pel = new Pelota();
 
−
  pel.id='P1';
 
−
  pel.peso = 50.34;
 
−
 
 
−
  modificarValores(pel, valorPrueba);
 
−
 
 
−
  print('El valor no es modificado ya que lo pasa por valor: $valorPrueba');
 
−
  print('El objeto es modificado ya que lo pasa por referencia: ${pel.id} -- ${pel.peso}');
 
  
 +
  final pelota = Pelota();
 +
 +
  pelota.id = 'P1';
 +
  pelota.peso = 50.34;
 +
 +
  modificarValores(
 +
    pelota,
 +
    valorPrueba,
 +
    cadena,
 +
  );
 +
 +
  print('Valor numérico: $valorPrueba');
 +
  print('Cadena: $cadena');
 +
  print('Objeto: ${pelota.id} -- ${pelota.peso}');
 
}
 
}
−
</syntaxhighlight>  
+
</syntaxhighlight>
 +
 
 +
 
 +
: Después de ejecutar la función:
  
 +
:* <code>valorPrueba</code> sigue valiendo <code>5</code>.
 +
:* <code>cadena</code> sigue conteniendo <code>'VIEJA CADENA'</code>.
 +
:* Las propiedades del objeto <code>pelota</code> sí han cambiado.
  
 +
: Por tanto, no es correcto decir que Dart pasa unos tipos por valor y otros por referencia. '''Todos los argumentos se pasan por valor'''; cuando trabajamos con objetos, el valor copiado es una referencia al objeto.
  
 
<br />
 
<br />
−
===Parámetros optativos===
 
  
−
* Podemos definir un parámetro como optativo mediante el empleo de '''[   ]''' de la forma siguiente:
+
===Parámetros opcionales posicionales===
−
:<syntaxhighlight lang="java" enclose="div" highlight="1" >
+
 
−
void imprimirMensaje([String mensaje='Nada enviado']){
+
<center>
−
 
+
{{#ev:youtube|9a2ANaIjOa0}}
−
  print(mensaje);
+
</center>
 +
 
 +
 
 +
 
 +
* Podemos definir parámetros posicionales opcionales escribiéndolos entre corchetes <code>[ ]</code>.
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div" highlight="1">
 +
void imprimirMensaje([String mensaje = 'Nada enviado']) {
 +
print(mensaje);
 
}
 
}
  
 
void main() {
 
void main() {
−
 
+
imprimirMensaje();
−
  imprimirMensaje();   // No enviamos el dato, imprime el valor por defecto
+
imprimirMensaje('Nuevo mensaje');
−
 
+
} </syntaxhighlight>
−
  imprimirMensaje('Nuevo Mensaje');
+
 
 +
: En la primera llamada no enviamos ningún valor, por lo que el parámetro utiliza su valor por defecto.
 +
 
 +
* Si el parámetro admite valores nulos, no es necesario establecer un valor por defecto:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div" highlight="1">
 +
void imprimirMensaje([String? mensaje]) {
 +
print(mensaje);
 
}
 
}
−
</syntaxhighlight>
 
  
−
* Fijarse que tenemos que establecer un valor por defecto, a no ser que permitamos que el parámetro pueda tener valor nulo, que será el valor que tenga cuando no se envíe dato a la función:
+
void main() {
−
:<syntaxhighlight lang="java" enclose="div" highlight="1" >
+
imprimirMensaje();
−
void imprimirMensaje([String? mensaje]){
+
imprimirMensaje('Nuevo mensaje');
 +
} </syntaxhighlight>
  
−
}
+
: Si no enviamos ningún valor, <code>mensaje</code> tendrá el valor <code>null</code>.
−
</syntaxhighlight>  
+
 
 +
* Si una función tiene parámetros posicionales obligatorios y opcionales, los opcionales deben aparecer al final:
  
 +
:<syntaxhighlight lang="dart" enclose="div" highlight="1">
 +
void imprimirMensaje(int numero, [String? mensaje]) {
 +
print('$numero - $mensaje');
 +
} </syntaxhighlight>
  
 
<br />
 
<br />
  
−
===Orden de envío de los datos===
+
===Parámetros con nombre===
−
* Esta es una característica que tiene DART y que no aparece en otros lenguajes.
 
−
: Cuando definimos los parámetros, estos están definidos en un orden y el envío de datos tiene que respetar dicho orden.
 
  
−
: En DART es posible indicar que cuando llamemos a una función, podamos definir los valores de los parámetros en cualquier orden. Esto lo implementa indicando desde la llamada qué valor estará asociada a qué parámetro.
+
* Dart permite definir '''parámetros con nombre'''.
  
 +
: Para ello se escriben los parámetros entre llaves <code>{ }</code>.
  
−
* La forma de hacer esto es <u>definiendo el conjunto de parámetros de la función '''entre llaves'''</u>.
+
:<syntaxhighlight lang="dart" enclose="div">
−
<center>
+
void imprimirMensaje({
−
{{#ev:youtube|9a2ANaIjOa0}}
+
String? mensaje,
−
</center>
+
}) {
 +
print(mensaje);
 +
} </syntaxhighlight>
 +
 
 +
* Al realizar la llamada indicamos el nombre del parámetro:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
imprimirMensaje(
 +
mensaje: 'Hola',
 +
);
 +
} </syntaxhighlight>
  
 +
* Una ventaja de los parámetros con nombre es que podemos indicar los argumentos por su nombre, haciendo las llamadas más fáciles de leer.
  
 
<br />
 
<br />
−
==Funciones flecha o Lambda==
 
  
−
* Son las '''arrow function''' o funciones '''lambda'''.
+
====Parámetros con nombre opcionales====
−
: Son funciones que retornan una expresión y que <u>no pueden tener bloque de código</u>.
 
−
: La expresión que se va a devolver va a continuación de '''=>'''.
 
−
: Viene a ser equivalente a '''return ??????''' aunque también se pueden emplear para ejecutar una instrucción cualquiera.
 
  
 +
* Los parámetros con nombre son opcionales por defecto.
  
−
: Su sintaxis sería la siguiente:
+
: Si el parámetro puede valer <code>null</code>, podemos declararlo como ''nullable'':
−
::<syntaxhighlight lang="java" enclose="div" highlight="1,5" >
 
−
return_type function_name(arguments) => expression;
 
−
</syntaxhighlight>  
 
  
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void imprimirMensaje1({
 +
String? mensaje,
 +
}) {
 +
print(mensaje);
 +
}
  
 +
void main() {
 +
imprimirMensaje1();
 +
imprimirMensaje1(
 +
mensaje: 'Hola',
 +
);
 +
} </syntaxhighlight>
  
−
: Por ejemplo:
+
* Otra posibilidad es proporcionar un valor por defecto:
−
:<syntaxhighlight lang="java" enclose="div" highlight="1,5" >
+
 
−
double sumar(double a, double b) => a+b;
+
:<syntaxhighlight lang="dart" enclose="div">
 +
void imprimirMensaje2({
 +
String mensaje = 'Valor por defecto',
 +
}) {
 +
print(mensaje);
 +
}
  
 
void main() {
 
void main() {
 +
imprimirMensaje2();
 +
 +
imprimirMensaje2(
 +
mensaje: 'Nuevo mensaje',
 +
);
 +
} </syntaxhighlight>
 +
 +
<br />
  
−
  print(sumar(10.22,22.34));
+
====Parámetros con nombre obligatorios====
 +
 
 +
* Podemos indicar que un parámetro con nombre es obligatorio utilizando la palabra clave <code>required</code>.
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div" highlight="2">
 +
void imprimirMensaje({
 +
required String mensaje,
 +
}) {
 +
print(mensaje);
 
}
 
}
−
</syntaxhighlight>
 
  
 +
void main() {
 +
imprimirMensaje(
 +
mensaje: 'Mensaje',
 +
);
 +
} </syntaxhighlight>
  
 +
: En este caso estamos obligados a enviar el argumento correspondiente a <code>mensaje</code>.
  
−
* Si queremos que ejecuta una intrucción cualquiera:
+
: Como el parámetro es obligatorio y su tipo es <code>String</code>, no necesitamos asignarle un valor por defecto ni permitir que sea <code>null</code>.
−
:<syntaxhighlight lang="java" enclose="div" highlight="1" >
 
−
void imprimirMediaNumero({required int num1, required int num2}) => print((num1+num2)/2);
 
  
 +
<br />
 +
 +
====Combinación de parámetros posicionales y con nombre====
 +
 +
* Podemos combinar parámetros posicionales y parámetros con nombre.
 +
 +
: Los parámetros posicionales se escriben antes que los parámetros con nombre:
 +
:<syntaxhighlight lang="dart" enclose="div" highlight="2,3">
 +
void imprimirMensaje(
 +
  int numero, {
 +
    String? mensaje,
 +
  }
 +
) {
 +
  print(
 +
    'El número es $numero y el mensaje es $mensaje',
 +
  );
 +
}
  
 
void main() {
 
void main() {
 +
  imprimirMensaje(
 +
    10,
 +
    mensaje: 'Mensaje',
 +
  );
 +
}
 +
</syntaxhighlight>
  
−
  imprimirMediaNumero(num1: 10, num2: 15);
+
<br />
−
}
+
 
−
</syntaxhighlight>  
+
==Funciones flecha o ''arrow functions''==
 +
 
 +
* Cuando una función está formada por una única expresión, podemos utilizar la sintaxis de '''función flecha''' mediante el operador <code>=></code>.
 +
 
 +
: Por ejemplo, la siguiente función:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
double sumar(double a, double b) {
 +
return a + b;
 +
} </syntaxhighlight>
 +
 
 +
: puede escribirse como:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
double sumar(double a, double b) => a + b; </syntaxhighlight>
 +
 
 +
* Después de <code>=></code> se escribe una expresión.
 +
 
 +
: El resultado de dicha expresión será el valor devuelto por la función cuando esta tenga un tipo de retorno distinto de <code>void</code>.
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
double calcularMedia(
 +
double num1,
 +
double num2,
 +
) =>
 +
(num1 + num2) / 2; </syntaxhighlight>
 +
 
 +
* También podemos emplearla con una expresión que no produzca un valor útil, como una llamada a <code>print</code>:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void imprimirMediaNumero({
 +
required int num1,
 +
required int num2,
 +
}) =>
 +
print((num1 + num2) / 2);
 +
 
 +
void main() {
 +
imprimirMediaNumero(
 +
num1: 10,
 +
num2: 15,
 +
);
 +
} </syntaxhighlight>
  
 +
* Si necesitamos ejecutar varias instrucciones, debemos emplear un bloque de código con llaves:
  
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void mostrarDatos(int numero) {
 +
print('Número recibido:');
 +
print(numero);
 +
} </syntaxhighlight>
  
 
<br />
 
<br />
  
 
==Funciones anónimas==
 
==Funciones anónimas==
−
* Son funciones que no tienen nombre asociado.
 
−
: Es decir, son funciones que se emplean dentro de la llamada a un método y en vez de llamar a una función previamente definida por nosotros, ejecutamos una o varias instrucciones que conformaría la función si la tuviéramos definida.
 
  
−
* Veamos un ejemplo con el [https://api.dart.dev/stable/1.10.1/dart-core/List/forEach.html método forEach asociado a una lista].
+
* Una '''función anónima''' es una función que no tiene un nombre asociado.
−
: Si os fijáis en la definición, el método forEach espera recibir una función que no devuelve nada (void) y que tiene un parámetro de tipo E.
+
 
−
: El tipo va a depender del tipo de dato de los elementos que conforman la lista.
+
: Las funciones anónimas son especialmente útiles como argumentos de métodos que esperan recibir otra función.
−
: Veamos en el ejemplo que vimos antes que tipo de datos va a ser E:
+
 
−
[[Imagen:Flutter_dart_fuhciones_1.JPG|500px|center]]
+
: Este tipo de métodos aparece frecuentemente cuando trabajamos con colecciones y también en Flutter mediante ''callbacks''.
 +
 
 +
* Una función anónima puede tener un bloque de código:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
(elemento) {
 +
print(elemento);
 +
} </syntaxhighlight>
 +
 
 +
* Si únicamente necesitamos una expresión, podemos emplear la sintaxis flecha:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
(elemento) => print(elemento) </syntaxhighlight>
 +
 
 +
<br />
 +
 
 +
===Funciones anónimas y forEach===
 +
 
 +
* El método <code>forEach</code> de un <code>Iterable</code> espera recibir una función que será ejecutada una vez por cada elemento.
 +
 
 +
: Por ejemplo:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final lista = <int>[1, 2, 3, 4, 5, 6];
 +
 
 +
lista.forEach((elemento) {
 +
print('Accediendo al elemento $elemento');
 +
});
 +
} </syntaxhighlight>
 +
 
 +
: El parámetro <code>elemento</code> representa, en cada ejecución, uno de los elementos de la lista.
 +
 
 +
* Podemos expresar lo mismo mediante una función flecha:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final lista = <int>[1, 2, 3, 4, 5, 6];
 +
 
 +
lista.forEach(
 +
(elemento) => print(
 +
'Accediendo al elemento $elemento',
 +
),
 +
);
 +
} </syntaxhighlight>
  
−
: Al tener un conjunto de datos enteros, espera ejecutar una función a la que se le va a pasar cada uno de los elementos de la lista, por lo que espera un entero.
+
<u>'''Nota importante:'''</u>
−
: La implementación de la funcuón anónima sería la siguiente:
+
 
−
:<syntaxhighlight lang="java" enclose="div" highlight="5" >
+
: Aunque estas formas son válidas y sirven para comprender el funcionamiento de las funciones anónimas, para realizar un recorrido sencillo de un <code>Iterable</code>, Dart recomienda normalmente utilizar <code>for...in</code> en lugar de <code>forEach</code> con una función anónima.
 +
 
 +
: Por ejemplo:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 
void main() {
 
void main() {
 +
final lista = <int>[1, 2, 3, 4, 5, 6];
 +
 +
for (final elemento in lista) {
 +
print(
 +
'Accediendo al elemento $elemento',
 +
);
 +
}
 +
} </syntaxhighlight>
 +
 +
<br />
  
−
  List<int> lista = [1,2,3,4,5,6];
+
===Pasar una función existente como parámetro===
  
−
  lista.forEach((elemento) { // Código de la función
+
* Cuando un método espera una función como parámetro, no siempre tenemos que crear una función anónima.
−
                          });
+
 
−
 
+
: Podemos pasar una función que ya esté definida:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void recorrerConjunto(String elemento) {
 +
print(elemento);
 
}
 
}
−
</syntaxhighlight>
 
−
: Hasta este punto, lo <u>importante es el parámetro '''elemento'''</u> el cual va a representar cada uno de los elementos de la lista.
 
−
: Es decir, Dart va a ejecutar el código que se encuentra entre llaves, y dentro del código vamos a tener acceso al elemento de la lista. Recordar que el código de la función lo va a ejecutar '''por cada elemento de la lista'''.
 
  
−
* Ahora dentro del código vamos a poder hacer lo que queramos (recordar que se ejecuta por cada elemento de la lista):
 
−
:<syntaxhighlight lang="java" enclose="div" highlight="6" >
 
 
void main() {
 
void main() {
 +
final conjunto = <String>{
 +
'uno',
 +
'dos',
 +
'tres',
 +
};
  
−
  List<int> lista = [1,2,3,4,5,6];
+
conjunto.forEach(recorrerConjunto);
 +
} </syntaxhighlight>
  
−
  lista.forEach((elemento) { // Código de la función  
+
: Fijarse en que escribimos:
−
                            print('Accediendo al elemento $elemento');
+
 
−
                          });
+
:<syntaxhighlight lang="dart" enclose="div">
 +
conjunto.forEach(recorrerConjunto); </syntaxhighlight>
 +
 
 +
: y no:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
conjunto.forEach(recorrerConjunto()); </syntaxhighlight>
 +
 
 +
: No queremos ejecutar <code>recorrerConjunto</code> en ese momento. Queremos proporcionar la función a <code>forEach</code> para que sea este método quien la ejecute posteriormente una vez por cada elemento.
 +
 
 +
* La función que enviemos debe ser compatible con el tipo de función que espera el método.
 +
 
 +
: En este ejemplo, el conjunto contiene elementos de tipo <code>String</code>, por lo que la función recibe un <code>String</code> y devuelve <code>void</code>:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void recorrerConjunto(String elemento) {
 +
print(elemento);
 +
} </syntaxhighlight>
 +
 
 +
<br />
 +
 
 +
===Tear-offs===
 +
 
 +
* Cuando hacemos referencia a una función o método sin utilizar los paréntesis de llamada, Dart crea lo que se denomina un '''tear-off'''.
 +
 
 +
: Por ejemplo:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
conjunto.forEach(recorrerConjunto); </syntaxhighlight>
 +
 
 +
: <code>recorrerConjunto</code> representa la función que queremos proporcionar a <code>forEach</code>.
 +
 
 +
* Dart recomienda no crear una función anónima innecesaria cuando podemos pasar directamente una función existente.
 +
 
 +
: Por tanto, en lugar de:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
lista.forEach(
 +
(elemento) => gestionarElemento(elemento),
 +
); </syntaxhighlight>
 +
 
 +
: es preferible:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
lista.forEach(gestionarElemento); </syntaxhighlight>
 +
 
 +
* Incluso podemos proporcionar directamente una función existente de Dart si es compatible con los parámetros esperados:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
lista.forEach(print); </syntaxhighlight>
 +
 
 +
<br />
 +
 
 +
===Diferentes formas de proporcionar una función a forEach===
 +
 
 +
* Veamos las diferentes posibilidades:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void gestionarElemento(int elemento) {
 +
print('Elemento: $elemento');
 
}
 
}
−
</syntaxhighlight>
 
−
 
−
: Esta forma de definir funciones se usa habitualmente en Flutter.
 
  
−
* Como ya vimos antes las <u>funciones arrows</u>, podemos modificar el código de la función anónima en el caso de ejecutar una única sentencia.
 
−
:<syntaxhighlight lang="java" enclose="div" highlight="5" >
 
 
void main() {
 
void main() {
 +
final lista = <int>[1, 2, 3, 4, 5, 6];
  
−
  List<int> lista = [1,2,3,4,5,6];
+
// Función anónima con bloque.
 +
lista.forEach((elemento) {
 +
print('Función anónima: $elemento');
 +
});
  
−
  lista.forEach((elemento) => print('Accediendo al elemento $elemento'));
+
// Función anónima flecha.
 +
lista.forEach(
 +
(elemento) =>
 +
print('Función flecha: $elemento'),
 +
);
 +
 
 +
// Función previamente definida.
 +
lista.forEach(gestionarElemento);
 +
 
 +
// Función existente compatible.
 +
lista.forEach(print);
 +
} </syntaxhighlight>
 +
 
 +
: Las dos primeras formas son útiles para comprender cómo funcionan las funciones anónimas.
 +
 
 +
: Sin embargo, si únicamente queremos recorrer un <code>Iterable</code>, normalmente emplearemos:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
for (final elemento in lista) {
 +
print(elemento);
 +
} </syntaxhighlight>
 +
 
 +
: Si queremos ejecutar exactamente una función existente para cada elemento, <code>forEach</code> es apropiado:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
lista.forEach(gestionarElemento);
 +
lista.forEach(print); </syntaxhighlight>
 +
 
 +
<br />
 +
 
 +
===Funciones que devuelven funciones===
 +
 
 +
* En Dart, las funciones son objetos. Esto significa que una función también puede devolver otra función.
 +
 
 +
: Veamos un ejemplo:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void Function(int) crearFuncion() {
 +
return (int elemento) {
 +
print('Número: $elemento');
 +
};
 
}
 
}
−
</syntaxhighlight>
 
−
: <u>'''Nota importante:'''</u>Fijarse que en la función anónima estamos empleando la función arrow (=>) por lo que indicamos que sólo vamos a ejecutar una sentencia.
 
−
: Si quisiéramos ejecutar más de una sentencia tendríamos que quitar la función arrow y escribir entre llaves { } el conjunto de instrucciones
 
  
 +
void main() {
 +
final lista = <int>[1, 2, 3];
  
 +
lista.forEach(crearFuncion());
 +
} </syntaxhighlight>
  
 +
: En este caso ocurre algo diferente al ejemplo anterior.
  
−
* A continuación muestro otras formas de ejecutar el bucle forEach:
+
: Primero se ejecuta:
−
:<syntaxhighlight lang="java" enclose="div" highlight="19" >
+
 
−
void gestionarElemLista(int elem){
+
:<syntaxhighlight lang="dart" enclose="div">
−
 
+
crearFuncion() </syntaxhighlight>
−
  print('Elemento de la lista: $elem');
+
 
 +
: Esta función devuelve otra función compatible con la función que espera <code>forEach</code>.
 +
 
 +
: Por ese motivo, en este caso sí aparecen los paréntesis después de <code>crearFuncion</code>.
 +
 
 +
: Este tipo de funciones recibe el nombre de '''funciones de orden superior'''. También están relacionadas con el concepto de ''closures'', que veremos cuando sea necesario.
 +
 
 +
<br />
 +
 
 +
===forEach en mapas===
 +
 
 +
* Los objetos de tipo <code>Map</code> también disponen del método <code>forEach</code>.
 +
 
 +
: En este caso, la función recibe dos parámetros:
 +
 
 +
:* La clave.
 +
:* El valor asociado a dicha clave.
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final mapa = <String, double>{
 +
'base': 1.84,
 +
'alero': 1.95,
 +
'pivot': 2.10,
 +
'escolta': 1.90,
 +
};
 +
 
 +
mapa.forEach((clave, valor) {
 +
print(
 +
'La clave $clave tiene el valor $valor',
 +
);
 +
});
 +
} </syntaxhighlight>
 +
 
 +
: En el caso de <code>Map.forEach</code>, el empleo de una función anónima es totalmente apropiado.
 +
 
 +
* Si queremos recorrer solamente las claves o solamente los valores, podemos emplear <code>for...in</code>:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final mapa = <String, double>{
 +
'base': 1.84,
 +
'alero': 1.95,
 +
'pivot': 2.10,
 +
'escolta': 1.90,
 +
};
 +
 
 +
for (final clave in mapa.keys) {
 +
print('Clave: $clave');
 
}
 
}
  
−
void function; gestionarElemLista2() {
+
for (final valor in mapa.values) {
−
  return (item) {
+
print('Valor: $valor');
−
    print("The number is $item");
 
−
  };
 
 
}
 
}
 +
} </syntaxhighlight>
 +
 +
<br />
 +
 +
===Recorrido de conjuntos===
 +
 +
* Los conjuntos (<code>Set</code>) son objetos <code>Iterable</code>, por lo que para un recorrido sencillo podemos utilizar <code>for...in</code>:
 +
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final conjunto = <String>{
 +
'Ángel',
 +
'Luis',
 +
'Ana',
 +
'María',
 +
};
 +
 +
for (final elemento in conjunto) {
 +
print(elemento);
 +
}
 +
} </syntaxhighlight>
 +
 +
* También podemos utilizar <code>forEach</code> si proporcionamos directamente una función existente:
  
 +
:<syntaxhighlight lang="dart" enclose="div">
 
void main() {
 
void main() {
 +
final conjunto = <String>{
 +
'Ángel',
 +
'Luis',
 +
'Ana',
 +
'María',
 +
};
 +
 +
conjunto.forEach(print);
 +
} </syntaxhighlight>
  
−
  List<int> lista = [1,2,3,4,5,6];
+
<br />
  
−
  lista.forEach((elemento) => gestionarElemLista(elemento));
+
==Método where==
−
  lista.forEach(gestionarElemLista2());
 
  
−
  lista.forEach((elemento) => print('Función anónima : Elemento de la lista: $elemento'));
+
* El método <code>where</code> permite '''filtrar''' los elementos de un <code>Iterable</code>.
−
 
+
 
−
}
+
: Recibe como parámetro una función que debe devolver un valor booleano:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
(elemento) => condición </syntaxhighlight>
 +
 
 +
: Si la función devuelve <code>true</code>, el elemento formará parte del resultado.
 +
 
 +
: Si devuelve <code>false</code>, el elemento será descartado.
 +
 
 +
* Por ejemplo:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final numeros = <int>[
 +
1,
 +
2,
 +
3,
 +
4,
 +
5,
 +
6,
 +
];
 +
 
 +
final pares = numeros.where(
 +
(numero) => numero.isEven,
 +
);
 +
 
 +
print(pares);
 +
} </syntaxhighlight>
 +
 
 +
: <code>where</code> devuelve un <code>Iterable</code>.
 +
 
 +
* Si queremos obtener una lista:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
final pares = numeros
 +
.where((numero) => numero.isEven)
 +
.toList(); </syntaxhighlight>
  
−
</syntaxhighlight>
+
* Si queremos obtener un conjunto:
  
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
final pares = numeros
 +
.where((numero) => numero.isEven)
 +
.toSet(); </syntaxhighlight>
  
 
<br />
 
<br />
  
−
: Veamos otros ejemplos de forEach aplicada a otras estructuras de datos....
+
==Método every==
 +
 
 +
* El método <code>every</code> permite comprobar si '''todos''' los elementos de un <code>Iterable</code> cumplen una determinada condición.
 +
 
 +
: Devuelve un valor de tipo <code>bool</code>.
  
−
* '''forEach en Mapas''':
+
:<syntaxhighlight lang="dart" enclose="div">
−
:<syntaxhighlight lang="java" enclose="div" highlight="12" >
 
 
void main() {
 
void main() {
 +
final numeros = <int>[
 +
10,
 +
20,
 +
30,
 +
40,
 +
];
  
−
  Map<String,double>mapa = {
+
final todosPositivos = numeros.every(
−
    'base' : 1.84,
+
(numero) => numero > 0,
−
    'alero' : 1.95,
+
);
−
    'pivot' : 2.10,
 
−
    'escola' : 1.90
 
−
    };
 
−
 
 
−
  mapa.values.forEach((valor) => print('Valor del mapa: $valor'));  // Accedemos a los valores
 
−
  mapa.keys.forEach((key) => print('La clave $key')); // Accedemos a las claves
 
−
  mapa.forEach((key,valor) => print('La clave $key tiene el valor $valor'));
 
−
}
 
−
</syntaxhighlight>
 
−
: Fijarse como la función anónima del forEach aplciado al Map espera recibir dos parámetros: uno representa la clave y otro el valor asociado a dicha clave.
 
  
 +
print(todosPositivos);
 +
} </syntaxhighlight>
  
 +
: El resultado será <code>true</code> únicamente si todos los elementos cumplen la condición.
  
 
<br />
 
<br />
−
* '''forEach en Conjuntos''':
+
 
−
:<syntaxhighlight lang="java" enclose="div" highlight="12" >
+
==Método map aplicado a List y Set==
 +
 
 +
* El método <code>map</code> permite transformar los elementos de un <code>Iterable</code>.
 +
 
 +
: Por cada elemento de la colección original se ejecuta una función que genera un nuevo valor.
 +
 
 +
: Como resultado obtenemos un nuevo <code>Iterable</code>.
 +
 
 +
* Por ejemplo:
 +
 
 +
:<syntaxhighlight lang="dart" enclose="div">
 
void main() {
 
void main() {
 +
final numeros = <int>[
 +
1,
 +
2,
 +
3,
 +
4,
 +
];
 +
 +
final cuadrados = numeros.map(
 +
(numero) => numero * numero,
 +
);
 +
 +
print(cuadrados);
 +
} </syntaxhighlight>
  
−
  var conjunto = {'Angel','Luis','Ana','María'};
+
* Habitualmente convertiremos el resultado a una lista mediante <code>toList()</code>:
−
 
 
−
  conjunto.forEach((valor) => print('El elemento de la lista: $valor'));
 
  
−
}</syntaxhighlight>  
+
:<syntaxhighlight lang="dart" enclose="div">
 +
final cuadrados = numeros
 +
.map((numero) => numero * numero)
 +
.toList(); </syntaxhighlight>
  
 +
* Este método es especialmente útil en Flutter, ya que permite crear una colección de ''widgets'' a partir de una colección de datos.
  
 +
: Por ejemplo, conceptualmente podemos transformar una lista de cadenas en una lista de objetos:
 +
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final nombres = <String>[
 +
'Ana',
 +
'Luis',
 +
'María',
 +
];
 +
 +
final mensajes = nombres
 +
.map((nombre) => 'Hola $nombre')
 +
.toList();
 +
 +
print(mensajes);
 +
} </syntaxhighlight>
 +
 +
* También podemos aplicar <code>map</code> sobre las entradas de un <code>Map</code>:
 +
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final mapa = <String, String>{
 +
'nombre': 'Ángel',
 +
'apellido1': 'Fernández',
 +
'apellido2': 'González',
 +
};
 +
 +
final lista = mapa.entries
 +
.map(
 +
(entrada) => 'HOLA: ${entrada.value}',
 +
)
 +
.toList();
 +
 +
print(lista);
 +
} </syntaxhighlight>
  
 
<br />
 
<br />
 +
 +
==List.generate==
 +
 +
* La clase <code>List</code> dispone del constructor <code>List.generate</code>, que permite generar los elementos de una lista mediante una función.
 +
 +
: La función recibe la posición que se está generando:
 +
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void main() {
 +
final numeros = List<int>.generate(
 +
5,
 +
(posicion) => posicion,
 +
);
 +
 +
print(numeros);
 +
} </syntaxhighlight>
 +
 +
: El resultado será:
 +
 +
:<syntaxhighlight lang="text" enclose="div">
 +
[0, 1, 2, 3, 4] </syntaxhighlight>
 +
 +
* Si queremos generar los números desde 1:
 +
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
final numeros = List<int>.generate(
 +
5,
 +
(posicion) => posicion + 1,
 +
); </syntaxhighlight>
 +
 +
* Podemos indicar mediante <code>growable: false</code> que la lista tenga longitud fija:
 +
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
final numeros = List<int>.generate(
 +
5,
 +
(posicion) => posicion + 1,
 +
growable: false,
 +
); </syntaxhighlight>
 +
 +
<u>'''Importante:'''</u>
 +
 +
: Una lista con <code>growable: false</code> es una '''lista de longitud fija'''. Esto significa que no podemos añadir ni eliminar elementos.
 +
 +
: Sin embargo, esto no significa que la lista sea inmutable. Podemos modificar el valor almacenado en una posición existente.
 +
 +
<br />
 +
 
==Ejercicios propuestos==
 
==Ejercicios propuestos==
  
 
'''Ejercicio 1:'''
 
'''Ejercicio 1:'''
  
−
Crea una función que espere recibir en cualquier orden un número y una lista y devuelva el valor de la lista que se encuentra en la posición enviada.
+
Crea una función que espere recibir mediante parámetros con nombre una posición y una lista de cadenas, y devuelva el valor de la lista que se encuentra en la posición indicada.
−
: Si no se envía posición debe devolver el primer elemento de la lista y la lista siempre debe ser enviada.
+
 
−
: Si la lista enviada no tiene datos debe devolver la cadena 'Sin datos' (emplea algún método de la clase List que te sirva para comprobar si está vacía la lista).
+
:* La lista debe ser enviada obligatoriamente.
−
: Si la posición del elemento de la lista es superior al número de elementos de la lista, debe devolver la cadena 'Indice fuera de rango'.
+
:* Si no se envía la posición, debe utilizarse la posición <code>0</code>.
 +
:* Si la lista no tiene elementos, debe devolver la cadena <code>'Sin datos'</code>.
 +
:* Si la posición es negativa o no existe en la lista, debe devolver la cadena <code>'Índice fuera de rango'</code>.
 +
:* Llama a la función empleando diferentes valores para comprobar su funcionamiento.
  
 +
<br />
  
 
'''Ejercicio 2:'''
 
'''Ejercicio 2:'''
−
Crea un mapa de datos en los que se guarde como valor clave un índice y como dato, un precio.
 
  
−
:* Define inicialmente cuatro entradas del mapa.
+
Crea un mapa de datos en el que se guarde como clave un índice entero y como valor un precio.
−
:* Añade dos nuevos elementos al mapa.
 
−
:* Borra el último elemento del mapa.
 
−
:* Recorre los elementos del mapa empleando una función anónima. Dicha función debe mostrar la clave y el precio de todos los platos que valgan menos de 5 euros.
 
−
:* Crea una función de nombre getNumPlatosMasCaros al que se le pase un mapa (no debe admitir valores nulos) y un precio.
 
−
:: Dicha función debe devolver cuantos platos tienen un precio superior al enviado.
 
−
:: Tanto el precio  como el mapa deben ser enviados y en cualquier orden.
 
−
:: Llama a dicha función para comprobar que funciona.
 
  
 +
:* Define inicialmente cuatro entradas en el mapa.
 +
:* Añade dos nuevas entradas.
 +
:* Borra la última entrada introducida.
 +
:* Recorre el mapa empleando <code>forEach</code>. La función debe mostrar la clave y el precio de todos los platos cuyo precio sea inferior a 5 euros.
 +
:* Crea una función de nombre <code>getNumPlatosMasCaros</code> a la que se le pase mediante parámetros con nombre un mapa y un precio.
 +
:* Los dos parámetros deben ser obligatorios y no admitir valores nulos.
 +
:* La función debe devolver cuántos platos tienen un precio superior al indicado.
 +
:* Emplea <code>where</code> para realizar el filtrado.
 +
:* Llama a la función para comprobar que funciona.
  
 +
<br />
  
 
'''Ejercicio 3:'''
 
'''Ejercicio 3:'''
−
En este ejercicio vas a probar a emplear otro método que permite emplear funciones anónimas, como es el [https://api.flutter.dev/flutter/dart-core/Iterable/where.html método where].
 
−
Veamos en el siguiente volcado, qué necesitamos para usar dicho método, sobre un conjunto (Set).
 
−
Los elementos del conjunto son de tipo String.
 
−
[[Imagen:Flutter_dart_fuhciones_2.JPG|500px|center]]
 
  
−
: En la imagen anterior:
+
Crea un conjunto de nombre <code>equipo</code> que contenga los valores:
−
:* Podemos comprobar como espera una función que devuelva un boolean: '''bool Function'''
 
−
:* Dicha función va a hacer uso de un parámetro de tipo String (recordar que la imagen anterior está aplicada sobre un Set de elementos de tipo String). Quiero esto decir que la función se va a ejecutar por cada elemento del conjunto y el parámetro será cada uno de esos elementos.
 
−
:* En el cuerpo de la función escribiremos el código y haremos un return de una expresión que devueva un booleano. Si la función puede ser implementada con una única instrucción, podemos emplear una función flecha (como vimos antes).
 
−
:* Aquellos elementos que satisfagan la condición, será de vueltos en forma de '[https://api.flutter.dev/flutter/dart-core/Iterable-class.html conjunto iterable]': '''Iterable<String>'''.
 
  
−
: Por lo tanto vamos a poder recorrer dichos elementos empleando el método iterator, como ya [https://wiki.cifprodolfoucha.es/index.php?title=Flutter_DART_variables_-_tipos_de_datos#Listas vimos con las listas y conjuntos] o bien podemos hacer uso del método forEach como vimos anteriormente.
+
:<syntaxhighlight lang="text" enclose="div">
 +
Base
 +
Alero
 +
Pivot
 +
Escolta </syntaxhighlight>
  
−
Teniendo en cuenta todo lo anterior, crea un conjunto de nombre equipo que tenga los valores 'Base','Alero','Pivot','Escolta'.
+
Crea una función que reciba como parámetros el conjunto y una cadena a buscar.
−
Emplea el método where y muestra aquellos elementos del conjunto que tengan como parte de su valor una cadena a buscar.
 
−
Busca por la cadena: as
 
−
Busca por la cadena: s
 
  
 +
:* Emplea el método <code>where</code>.
 +
:* La función debe mostrar los elementos del conjunto que contengan la cadena indicada.
 +
:* Recorre el resultado empleando <code>for...in</code>.
 +
:* Realiza una búsqueda con la cadena <code>'as'</code>.
 +
:* Realiza otra búsqueda con la cadena <code>'s'</code>.
  
 +
<br />
  
 
'''Ejercicio 4:'''
 
'''Ejercicio 4:'''
−
En base a lo aprendido en el ejercicio anterior, crea una lista de números con los siguintes datos: 10.0, 11.22, 15.0, 7.0, 3.44
+
 
−
Busca dentro de la clase List, qué método podrías emplear para que dado un número, comprobar si todos los elementos de la lista son mayores a dicho número.
+
Define una lista de números decimales con los siguientes valores:
−
Crea una función de nombre 'sonMayores' a la que se le pasen dos parámetros en cualquier orden. El primer parámetro es el número empleado para comprobar que todos los elementos de la lista son mayores a él. Si no se envía se debe emplear el valor 0 por defecto. El segundo parámetro es una lista.
+
 
−
Esta función debe devolver la cadena SI / NO en función si todos los elementos de la lista son mayores al número empleado como parámetro.
+
:<syntaxhighlight lang="text" enclose="div">
−
Llama a dicha función con el valor 2 y con el valor 12.
+
10.0, 11.22, 15.0, 7.0, 3.44 </syntaxhighlight>
−
Imprime la cadena: Todos los números de la lista son mayores a XX: YY siendo XX el número y YY la cadena SI / NO.
+
 
−
+
Crea una función de nombre <code>sonMayores</code>:
−
+
 
 +
:* Debe recibir mediante parámetros con nombre un número y una lista de números decimales.
 +
:* La lista debe ser obligatoria.
 +
:* Si no se envía el número, debe utilizarse el valor <code>0</code>.
 +
:* Emplea el método <code>every</code> para comprobar si todos los elementos de la lista son mayores que el número indicado.
 +
:* La función debe devolver la cadena <code>'SI'</code> o <code>'NO'</code>.
 +
:* Llama a la función empleando los valores <code>2</code> y <code>12</code>.
 +
:* Muestra una frase con el formato:
 +
 
 +
:<syntaxhighlight lang="text" enclose="div">
 +
Todos los números de la lista son mayores a XX: YY </syntaxhighlight>
 +
 
 +
: siendo <code>XX</code> el número utilizado e <code>YY</code> el resultado <code>SI</code> o <code>NO</code>.
 +
 
 +
<br />
  
 
'''Ejercicio 5:'''
 
'''Ejercicio 5:'''
−
La clase List tiene un [https://api.dart.dev/stable/2.13.4/dart-core/List/List.generate.html método generate].
 
−
Emplea dicho método para generar una lista de 100 números aleatorios.
 
  
−
Variación 1: Haz que empiece en el número 1 y acabe en el 100 y que no se puedan añadir nuevos elementos.
+
Emplea <code>List.generate</code> para realizar los siguientes ejercicios:
−
Variación 2: Crea una lista en la que dado un número se guarde en la lista el cuadrado del mismo, y que se puedan añadir nuevos elementos.
 
  
 +
:* Genera una lista de 100 números enteros aleatorios comprendidos entre 1 y 100. Emplea la clase Random.
 +
:* Genera una lista con los números comprendidos entre 1 y 100 y haz que tenga longitud fija, de forma que no puedan añadirse ni eliminarse elementos.
 +
:* Dado un número, genera una lista con los cuadrados de todos los números comprendidos entre 1 y dicho número. En este caso, la lista debe permitir añadir nuevos elementos.
  
 +
<br />
  
−
'''Ejercicios propuestos:'''
+
'''Ejercicio adicional:'''
−
Comprueba que métodos dispones en las clase List, Map, Set que se puedan emplear con funciones anónimas y haz un ejemplo de cada uno de ellos.
 
  
 +
Investiga otros métodos disponibles en <code>List</code>, <code>Set</code>, <code>Map</code> o <code>Iterable</code> que reciban una función como parámetro.
  
 +
:* Busca varios ejemplos.
 +
:* Indica qué tipo de función espera cada método.
 +
:* Realiza un pequeño ejemplo de uso.
 +
:* Algunos métodos que puedes investigar son <code>any</code>, <code>firstWhere</code>, <code>fold</code>, <code>removeWhere</code> o <code>updateAll</code>.
  
 +
<br />
  
  
−
<br />
+
===Soluciones de los ejercicios propuestos===
−
===Soluciones Ejercicios propuestos===
 
  
 
'''Solución Ejercicio 1:'''
 
'''Solución Ejercicio 1:'''
−
Crea una función que espere recibir en calquier orden un número y una lista y devuelva el valor de la lista que se encuentra en la posición enviada.
 
−
: Si no se envía posición debe devolver el primer elemento de la lista y la lista siempre debe ser enviada.
 
−
: Si la lista enviada no tiene datos debe devolver la cadena 'Sin datos' (emplea algún método de la clase List que te sirva para comprobar si está vacía la lista).
 
−
: Si la posición del elemento de la lista es superior al número de elementos de la lista, debe devolver la cadena 'Indice fuera de rango'.
 
  
−
:<syntaxhighlight lang="java" enclose="div" highlight="" >
+
:<syntaxhighlight lang="dart" enclose="div">
−
String devolverElementoLista(
+
String devolverElementoLista({
−
  {int pos=0, required List listaRecibir})
+
int posicion = 0,
−
{
+
required List<String> lista,
−
  if(listaRecibir.isEmpty) return 'Sin datos';
+
}) {
−
  if(pos >= listaRecibir.length) return 'Indice fuera de rango.';
+
if (lista.isEmpty) {
−
 
+
return 'Sin datos';
−
  return listaRecibir[pos];
+
}
 +
 
 +
if (
 +
posicion < 0 ||
 +
posicion >= lista.length
 +
) {
 +
return 'Índice fuera de rango';
 +
}
 +
 
 +
return lista[posicion];
 
}
 
}
  
 
void main() {
 
void main() {
 +
final lista = <String>[
 +
'Uno',
 +
'Dos',
 +
'Tres',
 +
'Cuatro',
 +
];
  
−
  final lista = ['Uno','Dos','Tres','Cuatro'];
+
print(
−
 
+
devolverElementoLista(
−
  print(devolverElementoLista(listaRecibir: lista));
+
lista: lista,
−
  print(devolverElementoLista(listaRecibir: lista,pos: 2));
+
),
−
  print(devolverElementoLista(listaRecibir: lista,pos: 4));
+
);
−
 
 
  
 +
print(
 +
devolverElementoLista(
 +
posicion: 2,
 +
lista: lista,
 +
),
 +
);
 +
 +
print(
 +
devolverElementoLista(
 +
lista: lista,
 +
posicion: 4,
 +
),
 +
);
 +
 +
print(
 +
devolverElementoLista(
 +
posicion: -1,
 +
lista: lista,
 +
),
 +
);
 
}
 
}
−
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
<br />
  
 
'''Solución Ejercicio 2:'''
 
'''Solución Ejercicio 2:'''
−
Crea un mapa de datos en los que se guarde como valor clave un índice y como dato, un precio.
 
−
 
−
:* Define inicialmente cuatro entradas del mapa.
 
−
:* Añade dos nuevos elementos al mapa.
 
−
:* Borra el último elemento del mapa.
 
−
:* Recorre los elementos del mapa empleando una función anónima. Dicha función debe mostrar la clave y el precio de todos los platos que valgan menos de 5 euros.
 
−
:* Crea una función de nombre getNumPlatosMasCaros al que se le pase un mapa (no debe admitir valores nulos) y un precio.
 
−
:: Dicha función debe devolver cuantos platos tienen un precio superior al enviado.
 
−
:: Tanto el precio  como el mapa deben ser enviados y en cualquier orden.
 
−
:: Llama a dicha función para comprobar que funciona.
 
−
:<syntaxhighlight lang="java" enclose="div" highlight="" >
 
  
−
int getNumPlatosMasCaros({required Map mapa, required double precio}){
+
:<syntaxhighlight lang="dart" enclose="div">
−
 
+
int getNumPlatosMasCaros({
−
  var contador=0;
+
required Map<int, double> mapa,
−
  mapa.values.forEach((preciosmapa) => preciosmapa > precio ? contador++ : 0);
+
required double precio,
−
 
+
}) {
−
  return contador;
+
return mapa.values
−
 
+
.where(
 +
(precioMapa) => precioMapa > precio,
 +
)
 +
.length;
 
}
 
}
  
 
void main() {
 
void main() {
 +
final precios = <int, double>{
 +
1: 10.34,
 +
2: 3.24,
 +
3: 24.99,
 +
4: 17.35,
 +
};
 +
 +
precios.addAll({
 +
5: 9.23,
 +
6: 2.22,
 +
});
 +
 +
print(precios);
 +
 +
final ultimaClave = precios.keys.last;
 +
precios.remove(ultimaClave);
 +
 +
precios.forEach((clave, valor) {
 +
if (valor < 5) {
 +
print(
 +
'La clave $clave tiene un precio de $valor',
 +
);
 +
}
 +
});
 +
 +
const precioReferencia = 10.0;
  
−
  Map<int,double> precios = {
+
final numPlatos = getNumPlatosMasCaros(
−
    1 : 10.34,
+
mapa: precios,
−
    2 : 3.24,
+
precio: precioReferencia,
−
    3 : 24.99,
+
);
−
    4 : 17.35
 
−
  };
 
  
−
  precios.addAll({5 : 9.23, 6: 2.22});  // Añade dos nuevos elememtos al mapa
+
print(
−
  print(precios);
+
'Número de platos con un precio superior '
−
 
+
'a $precioReferencia euros: $numPlatos',
−
  int key = precios.keys.elementAt(precios.length-1); 
+
);
−
  precios.remove(key);  // Borra el último elemento del mapa
 
−
 
 
−
  precios.forEach((clave,valor) => valor < 5 ? print('La clave $clave tiene un precio de $valor') : '');
 
−
 
 
−
  print('Número de platos mayores a 10 euros:${getNumPlatosMasCaros(mapa: precios, precio: 10)}');
 
−
 
 
 
}
 
}
 +
</syntaxhighlight>
  
−
</syntaxhighlight>
+
<br />
  
 +
'''Solución Ejercicio 3:'''
  
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
void buscarCadena(
 +
Set<String> conjunto,
 +
String cadena,
 +
) {
 +
final encontrados = conjunto.where(
 +
(elemento) => elemento.contains(cadena),
 +
);
  
−
'''Solución Ejercicio 3:'''
+
for (final elemento in encontrados) {
−
Crea un conjunto de nombre equipo que tenga los valores 'Base','Alero','Pivot','Escolta'.
+
print('Encontrado: $elemento');
−
Emplea el método where y muestra aquellos elementos del conjunto que tengan como parte de su valor una cadena a buscar.
+
}
−
Busca por la cadena: as
+
}
−
Busca por la cadena: s
 
  
−
:<syntaxhighlight lang="java" enclose="div" highlight="" >
 
 
void main() {
 
void main() {
 +
final equipo = <String>{
 +
'Base',
 +
'Alero',
 +
'Pivot',
 +
'Escolta',
 +
};
 +
 +
var cadenaBusqueda = 'as';
 +
 +
print(
 +
'Cadena de búsqueda: $cadenaBusqueda',
 +
);
 +
 +
buscarCadena(
 +
equipo,
 +
cadenaBusqueda,
 +
);
  
−
  final equipo = {'Base','Alero','Pivot','Escolta'};
+
cadenaBusqueda = 's';
−
  var cadenaBusqueda = 'as';
+
 
−
 
+
print(
−
  print('Cadena de búsqueda:$cadenaBusqueda');
+
'Cadena de búsqueda: $cadenaBusqueda',
−
  equipo.where((elemento) => elemento.indexOf(cadenaBusqueda) > 0)
+
);
−
    .forEach((elemento) => print('Encontrado:$elemento'));
+
 
−
 
+
buscarCadena(
−
  cadenaBusqueda='s';
+
equipo,
−
  print('Cadena de búsqueda:$cadenaBusqueda');
+
cadenaBusqueda,
−
  equipo.where((elemento) => elemento.indexOf(cadenaBusqueda) > 0)
+
);
−
    .forEach((elemento) => print('Encontrado:$elemento'));
 
−
 
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
<br />
  
 +
'''Solución Ejercicio 4:'''
  
−
'''Solución Ejercicio 4:'''
+
:<syntaxhighlight lang="dart" enclose="div">
−
En base a lo aprendido en el ejercicio anterior, crea una lista de números con los siguintes datos: 10.0, 11.22, 15.0, 7.0, 3.44
+
String sonMayores({
−
Busca dentro de la clase List, qué método podrías emplear para que dado un número, comprobar si todos los elementos de la lista son mayores a dicho número.
+
double numero = 0,
−
Crea una función de nombre 'sonMayores' a la que se le pasen dos parámetros en cualquier orden. El primer parámetro es el número empleado para comprobar que todos los elementos de la lista son mayores a él. Si no se envía se debe emplear el valor 0 por defecto. El segundo parámetro es una lista.
+
required List<double> lista,
−
Esta función debe devolver la cadena SI / NO en función si todos los elementos de la lista son mayores al número empleado como parámetro.
+
}) =>
−
Llama a dicha función con el valor 2 y con el valor 12.
+
lista.every(
−
Imprime la cadena: Todos los números de la lista son mayores a XX: YY siendo XX el número y YY la cadena SI / NO.
+
(elemento) => elemento > numero,
 +
)
 +
? 'SI'
 +
: 'NO';
 +
 
 +
void main() {
 +
final listaNumeros = <double>[
 +
10.0,
 +
11.22,
 +
15.0,
 +
7.0,
 +
3.44,
 +
];
 +
 
 +
var numeroReferencia = 2.0;
 +
 
 +
print(
 +
'Todos los números de la lista son mayores '
 +
'a $numeroReferencia: '
 +
'${sonMayores(
 +
numero: numeroReferencia,
 +
lista: listaNumeros,
 +
)}',
 +
);
 +
 
 +
numeroReferencia = 12.0;
  
−
:<syntaxhighlight lang="java" enclose="div" highlight="" >
+
print(
−
String sonMayores({numero = 0, required List lista}){
+
'Todos los números de la lista son mayores '
−
 
+
'a $numeroReferencia: '
−
  return lista.every((elem) => elem > numero) ? 'SI' : 'NO';
+
'${sonMayores(
 +
lista: listaNumeros,
 +
numero: numeroReferencia,
 +
)}',
 +
);
 
}
 
}
 +
</syntaxhighlight>
  
 +
<br />
 +
 +
'''Solución Ejercicio 5:'''
 +
 +
:<syntaxhighlight lang="dart" enclose="div">
 +
import 'dart:math';
  
 
void main() {
 
void main() {
 +
final random = Random();
  
−
  final listaNumeros = [10.0, 11.22, 15.0, 7.0, 3.44];
+
// 100 números aleatorios entre 1 y 100.
−
 
+
final numerosAleatorios = List<int>.generate(
−
  var numeroReferencia = 2;
+
100,
−
 
+
(_) => random.nextInt(100) + 1,
−
  print('Todos los números de la lista son mayores a $numeroReferencia: ${sonMayores(numero: numeroReferencia, lista : listaNumeros)}');
+
);
  
−
  numeroReferencia = 12;
+
print(numerosAleatorios);
−
  print('Todos los números de la lista son mayores a $numeroReferencia: ${sonMayores(numero: numeroReferencia, lista : listaNumeros)}');
 
−
 
 
−
}
 
  
−
</syntaxhighlight>
+
// Números desde 1 hasta 100.
 +
// Lista de longitud fija.
 +
final numerosSecuenciales = List<int>.generate(
 +
100,
 +
(posicion) => posicion + 1,
 +
growable: false,
 +
);
  
 +
print(numerosSecuenciales);
  
 +
// Cuadrados desde 1 hasta numero.
 +
// La lista permite añadir elementos.
 +
const numero = 10;
  
 +
final numerosAlCuadrado = List<int>.generate(
 +
numero,
 +
(posicion) {
 +
final valor = posicion + 1;
  
−
'''Solución Ejercicio 5:'''
+
return valor * valor;
−
La clase List tiene un [https://api.dart.dev/stable/2.13.4/dart-core/List/List.generate.html método generate].
+
},
−
Emplea dicho método para generar una lista de 100 números aleatorios.
+
growable: true,
−
 
+
);
−
Variación 1: Haz que empiece en el número 1 y acabe en el 100 y que no se puedan añadir nuevos elementos.
 
−
Variación 2: Crea una lista en la que dado un número se guarde en la lista el cuadrado del mismo, y que se puedan añadir nuevos elementos.
 
  
−
:<syntaxhighlight lang="java" enclose="div" highlight="" >
+
print(numerosAlCuadrado);
  
−
void main() {
+
numerosAlCuadrado.add(121);
  
−
  var numerosSecuenciales = List<int>.generate(100,(posicion) => posicion);
+
print(numerosAlCuadrado);
−
  print(numerosSecuenciales);
 
−
 
 
−
  var numerosSecuencialesEmpiezanUno = List<int>.generate(100,(posicion) => posicion+1, growable: false);
 
−
  print(numerosSecuencialesEmpiezanUno);
 
−
 
 
−
  var numerosAlCuadrado = List<int>.generate(100,(posicion) => posicion*posicion, growable: true);
 
−
  print(numerosAlCuadrado);
 
−
 
 
−
 
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
−
 
  
  
Línea 510: Línea 1182:
 
[https://wiki.cifprodolfoucha.es/index.php?title=Programaci%C3%B3n_de_dispositivos_m%C3%B3viles_con_FLUTTER Enlace a la página principal del curso]
 
[https://wiki.cifprodolfoucha.es/index.php?title=Programaci%C3%B3n_de_dispositivos_m%C3%B3viles_con_FLUTTER Enlace a la página principal del curso]
  
−
 
+
<br />
−
 
+
```
−
 
 
−
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2021).
 

Revisión actual del 07:34 24 sep 2026

Introducción

  • En Dart, las funciones permiten agrupar código que puede ejecutarse desde diferentes partes de un programa.
 : Como este curso está orientado a alumnado que ya sabe programar en algún lenguaje, no profundizaremos en los conceptos generales de las funciones, sino en las características específicas que ofrece Dart.
  • Es recomendable indicar el tipo de los parámetros que recibe una función y el tipo del dato que devuelve.
  • Veamos un ejemplo de función con parámetros:
double sumarValores(double val1, double val2) {
return val1 + val2;
}

void main() {
final num1 = 10.32;
final num2 = 1.35;

print(sumarValores(num1, num2));
}


Paso de parámetros

  • Al igual que en otros lenguajes, podemos pasar datos a las funciones empleando parámetros.
Como ya vimos anteriormente, podemos definir parámetros que admitan valores nulos utilizando los tipos nullable, como por ejemplo String?, int? o double?.


Paso por valor

  • En Dart, los argumentos de una función se pasan por valor.
Las variables de Dart contienen referencias a objetos. Cuando enviamos una variable como argumento de una función, se copia el valor almacenado en dicha variable.
Si dicho valor es una referencia a un objeto, se copia la referencia. Por tanto, tanto la variable original como el parámetro pueden hacer referencia al mismo objeto.
Esto provoca dos comportamientos que debemos diferenciar:
  • Si reasignamos el parámetro dentro de la función, no modificamos la variable utilizada en la llamada.
  • Si utilizamos la referencia recibida para modificar el estado de un objeto mutable, el cambio será visible fuera de la función, ya que ambas referencias permiten acceder al mismo objeto.
class Pelota {
  double? peso;
  String? id;
}

void modificarValores(
  Pelota pelota,
  int valor,
  String cadena,
) {
  valor = 10;
  cadena = 'NUEVA CADENA';

  pelota.peso = 100.2;
  pelota.id = 'P2';
}

void main() {
  var valorPrueba = 5;
  var cadena = 'VIEJA CADENA';

  final pelota = Pelota();

  pelota.id = 'P1';
  pelota.peso = 50.34;

  modificarValores(
    pelota,
    valorPrueba,
    cadena,
  );

  print('Valor numérico: $valorPrueba');
  print('Cadena: $cadena');
  print('Objeto: ${pelota.id} -- ${pelota.peso}');
}


Después de ejecutar la función:
  • valorPrueba sigue valiendo 5.
  • cadena sigue conteniendo 'VIEJA CADENA'.
  • Las propiedades del objeto pelota sí han cambiado.
Por tanto, no es correcto decir que Dart pasa unos tipos por valor y otros por referencia. Todos los argumentos se pasan por valor; cuando trabajamos con objetos, el valor copiado es una referencia al objeto.


Parámetros opcionales posicionales


  • Podemos definir parámetros posicionales opcionales escribiéndolos entre corchetes [ ].
void imprimirMensaje([String mensaje = 'Nada enviado']) {
print(mensaje);
}

void main() {
imprimirMensaje();
imprimirMensaje('Nuevo mensaje');
}
En la primera llamada no enviamos ningún valor, por lo que el parámetro utiliza su valor por defecto.
  • Si el parámetro admite valores nulos, no es necesario establecer un valor por defecto:
void imprimirMensaje([String? mensaje]) {
print(mensaje);
}

void main() {
imprimirMensaje();
imprimirMensaje('Nuevo mensaje');
}
Si no enviamos ningún valor, mensaje tendrá el valor null.
  • Si una función tiene parámetros posicionales obligatorios y opcionales, los opcionales deben aparecer al final:
void imprimirMensaje(int numero, [String? mensaje]) {
print('$numero - $mensaje');
}


Parámetros con nombre

  • Dart permite definir parámetros con nombre.
Para ello se escriben los parámetros entre llaves { }.
void imprimirMensaje({
String? mensaje,
}) {
print(mensaje);
}
  • Al realizar la llamada indicamos el nombre del parámetro:
void main() {
imprimirMensaje(
mensaje: 'Hola',
);
}
  • Una ventaja de los parámetros con nombre es que podemos indicar los argumentos por su nombre, haciendo las llamadas más fáciles de leer.


Parámetros con nombre opcionales

  • Los parámetros con nombre son opcionales por defecto.
Si el parámetro puede valer null, podemos declararlo como nullable:
void imprimirMensaje1({
String? mensaje,
}) {
print(mensaje);
}

void main() {
imprimirMensaje1();
imprimirMensaje1(
mensaje: 'Hola',
);
}
  • Otra posibilidad es proporcionar un valor por defecto:
void imprimirMensaje2({
String mensaje = 'Valor por defecto',
}) {
print(mensaje);
}

void main() {
imprimirMensaje2();

imprimirMensaje2(
mensaje: 'Nuevo mensaje',
);
}


Parámetros con nombre obligatorios

  • Podemos indicar que un parámetro con nombre es obligatorio utilizando la palabra clave required.
void imprimirMensaje({
required String mensaje,
}) {
print(mensaje);
}

void main() {
imprimirMensaje(
mensaje: 'Mensaje',
);
}
En este caso estamos obligados a enviar el argumento correspondiente a mensaje.
Como el parámetro es obligatorio y su tipo es String, no necesitamos asignarle un valor por defecto ni permitir que sea null.


Combinación de parámetros posicionales y con nombre

  • Podemos combinar parámetros posicionales y parámetros con nombre.
Los parámetros posicionales se escriben antes que los parámetros con nombre:
void imprimirMensaje(
  int numero, {
    String? mensaje,
  }
) {
  print(
    'El número es $numero y el mensaje es $mensaje',
  );
}

void main() {
  imprimirMensaje(
    10,
    mensaje: 'Mensaje',
  );
}


Funciones flecha o arrow functions

  • Cuando una función está formada por una única expresión, podemos utilizar la sintaxis de función flecha mediante el operador =>.
Por ejemplo, la siguiente función:
double sumar(double a, double b) {
return a + b;
}
puede escribirse como:
double sumar(double a, double b) => a + b;
  • Después de => se escribe una expresión.
El resultado de dicha expresión será el valor devuelto por la función cuando esta tenga un tipo de retorno distinto de void.
double calcularMedia(
double num1,
double num2,
) =>
(num1 + num2) / 2;
  • También podemos emplearla con una expresión que no produzca un valor útil, como una llamada a print:
void imprimirMediaNumero({
required int num1,
required int num2,
}) =>
print((num1 + num2) / 2);

void main() {
imprimirMediaNumero(
num1: 10,
num2: 15,
);
}
  • Si necesitamos ejecutar varias instrucciones, debemos emplear un bloque de código con llaves:
void mostrarDatos(int numero) {
print('Número recibido:');
print(numero);
}


Funciones anónimas

  • Una función anónima es una función que no tiene un nombre asociado.
Las funciones anónimas son especialmente útiles como argumentos de métodos que esperan recibir otra función.
Este tipo de métodos aparece frecuentemente cuando trabajamos con colecciones y también en Flutter mediante callbacks.
  • Una función anónima puede tener un bloque de código:
(elemento) {
print(elemento);
}
  • Si únicamente necesitamos una expresión, podemos emplear la sintaxis flecha:
(elemento) => print(elemento)


Funciones anónimas y forEach

  • El método forEach de un Iterable espera recibir una función que será ejecutada una vez por cada elemento.
Por ejemplo:
void main() {
final lista = <int>[1, 2, 3, 4, 5, 6];

lista.forEach((elemento) {
print('Accediendo al elemento $elemento');
});
}
El parámetro elemento representa, en cada ejecución, uno de los elementos de la lista.
  • Podemos expresar lo mismo mediante una función flecha:
void main() {
final lista = <int>[1, 2, 3, 4, 5, 6];

lista.forEach(
(elemento) => print(
'Accediendo al elemento $elemento',
),
);
}

Nota importante:

Aunque estas formas son válidas y sirven para comprender el funcionamiento de las funciones anónimas, para realizar un recorrido sencillo de un Iterable, Dart recomienda normalmente utilizar for...in en lugar de forEach con una función anónima.
Por ejemplo:
void main() {
final lista = <int>[1, 2, 3, 4, 5, 6];

for (final elemento in lista) {
print(
'Accediendo al elemento $elemento',
);
}
}


Pasar una función existente como parámetro

  • Cuando un método espera una función como parámetro, no siempre tenemos que crear una función anónima.
Podemos pasar una función que ya esté definida:
void recorrerConjunto(String elemento) {
print(elemento);
}

void main() {
final conjunto = <String>{
'uno',
'dos',
'tres',
};

conjunto.forEach(recorrerConjunto);
}
Fijarse en que escribimos:
conjunto.forEach(recorrerConjunto);
y no:
conjunto.forEach(recorrerConjunto());
No queremos ejecutar recorrerConjunto en ese momento. Queremos proporcionar la función a forEach para que sea este método quien la ejecute posteriormente una vez por cada elemento.
  • La función que enviemos debe ser compatible con el tipo de función que espera el método.
En este ejemplo, el conjunto contiene elementos de tipo String, por lo que la función recibe un String y devuelve void:
void recorrerConjunto(String elemento) {
print(elemento);
}


Tear-offs

  • Cuando hacemos referencia a una función o método sin utilizar los paréntesis de llamada, Dart crea lo que se denomina un tear-off.
Por ejemplo:
conjunto.forEach(recorrerConjunto);
recorrerConjunto representa la función que queremos proporcionar a forEach.
  • Dart recomienda no crear una función anónima innecesaria cuando podemos pasar directamente una función existente.
Por tanto, en lugar de:
lista.forEach(
(elemento) => gestionarElemento(elemento),
);
es preferible:
lista.forEach(gestionarElemento);
  • Incluso podemos proporcionar directamente una función existente de Dart si es compatible con los parámetros esperados:
lista.forEach(print);


Diferentes formas de proporcionar una función a forEach

  • Veamos las diferentes posibilidades:
void gestionarElemento(int elemento) {
print('Elemento: $elemento');
}

void main() {
final lista = <int>[1, 2, 3, 4, 5, 6];

// Función anónima con bloque.
lista.forEach((elemento) {
print('Función anónima: $elemento');
});

// Función anónima flecha.
lista.forEach(
(elemento) =>
print('Función flecha: $elemento'),
);

// Función previamente definida.
lista.forEach(gestionarElemento);

// Función existente compatible.
lista.forEach(print);
}
Las dos primeras formas son útiles para comprender cómo funcionan las funciones anónimas.
Sin embargo, si únicamente queremos recorrer un Iterable, normalmente emplearemos:
for (final elemento in lista) {
print(elemento);
}
Si queremos ejecutar exactamente una función existente para cada elemento, forEach es apropiado:
lista.forEach(gestionarElemento);
lista.forEach(print);


Funciones que devuelven funciones

  • En Dart, las funciones son objetos. Esto significa que una función también puede devolver otra función.
Veamos un ejemplo:
void Function(int) crearFuncion() {
return (int elemento) {
print('Número: $elemento');
};
}

void main() {
final lista = <int>[1, 2, 3];

lista.forEach(crearFuncion());
}
En este caso ocurre algo diferente al ejemplo anterior.
Primero se ejecuta:
crearFuncion()
Esta función devuelve otra función compatible con la función que espera forEach.
Por ese motivo, en este caso sí aparecen los paréntesis después de crearFuncion.
Este tipo de funciones recibe el nombre de funciones de orden superior. También están relacionadas con el concepto de closures, que veremos cuando sea necesario.


forEach en mapas

  • Los objetos de tipo Map también disponen del método forEach.
En este caso, la función recibe dos parámetros:
  • La clave.
  • El valor asociado a dicha clave.
void main() {
final mapa = <String, double>{
'base': 1.84,
'alero': 1.95,
'pivot': 2.10,
'escolta': 1.90,
};

mapa.forEach((clave, valor) {
print(
'La clave $clave tiene el valor $valor',
);
});
}
En el caso de Map.forEach, el empleo de una función anónima es totalmente apropiado.
  • Si queremos recorrer solamente las claves o solamente los valores, podemos emplear for...in:
void main() {
final mapa = <String, double>{
'base': 1.84,
'alero': 1.95,
'pivot': 2.10,
'escolta': 1.90,
};

for (final clave in mapa.keys) {
print('Clave: $clave');
}

for (final valor in mapa.values) {
print('Valor: $valor');
}
}


Recorrido de conjuntos

  • Los conjuntos (Set) son objetos Iterable, por lo que para un recorrido sencillo podemos utilizar for...in:
void main() {
final conjunto = <String>{
'Ángel',
'Luis',
'Ana',
'María',
};

for (final elemento in conjunto) {
print(elemento);
}
}
  • También podemos utilizar forEach si proporcionamos directamente una función existente:
void main() {
final conjunto = <String>{
'Ángel',
'Luis',
'Ana',
'María',
};

conjunto.forEach(print);
}


Método where

  • El método where permite filtrar los elementos de un Iterable.
Recibe como parámetro una función que debe devolver un valor booleano:
(elemento) => condición
Si la función devuelve true, el elemento formará parte del resultado.
Si devuelve false, el elemento será descartado.
  • Por ejemplo:
void main() {
final numeros = <int>[
1,
2,
3,
4,
5,
6,
];

final pares = numeros.where(
(numero) => numero.isEven,
);

print(pares);
}
where devuelve un Iterable.
  • Si queremos obtener una lista:
final pares = numeros
.where((numero) => numero.isEven)
.toList();
  • Si queremos obtener un conjunto:
final pares = numeros
.where((numero) => numero.isEven)
.toSet();


Método every

  • El método every permite comprobar si todos los elementos de un Iterable cumplen una determinada condición.
Devuelve un valor de tipo bool.
void main() {
final numeros = <int>[
10,
20,
30,
40,
];

final todosPositivos = numeros.every(
(numero) => numero > 0,
);

print(todosPositivos);
}
El resultado será true únicamente si todos los elementos cumplen la condición.


Método map aplicado a List y Set

  • El método map permite transformar los elementos de un Iterable.
Por cada elemento de la colección original se ejecuta una función que genera un nuevo valor.
Como resultado obtenemos un nuevo Iterable.
  • Por ejemplo:
void main() {
final numeros = <int>[
1,
2,
3,
4,
];

final cuadrados = numeros.map(
(numero) => numero * numero,
);

print(cuadrados);
}
  • Habitualmente convertiremos el resultado a una lista mediante toList():
final cuadrados = numeros
.map((numero) => numero * numero)
.toList();
  • Este método es especialmente útil en Flutter, ya que permite crear una colección de widgets a partir de una colección de datos.
Por ejemplo, conceptualmente podemos transformar una lista de cadenas en una lista de objetos:
void main() {
final nombres = <String>[
'Ana',
'Luis',
'María',
];

final mensajes = nombres
.map((nombre) => 'Hola $nombre')
.toList();

print(mensajes);
}
  • También podemos aplicar map sobre las entradas de un Map:
void main() {
final mapa = <String, String>{
'nombre': 'Ángel',
'apellido1': 'Fernández',
'apellido2': 'González',
};

final lista = mapa.entries
.map(
(entrada) => 'HOLA: ${entrada.value}',
)
.toList();

print(lista);
}


List.generate

  • La clase List dispone del constructor List.generate, que permite generar los elementos de una lista mediante una función.
La función recibe la posición que se está generando:
void main() {
final numeros = List<int>.generate(
5,
(posicion) => posicion,
);

print(numeros);
}
El resultado será:
[0, 1, 2, 3, 4]
  • Si queremos generar los números desde 1:
final numeros = List<int>.generate(
5,
(posicion) => posicion + 1,
);
  • Podemos indicar mediante growable: false que la lista tenga longitud fija:
final numeros = List<int>.generate(
5,
(posicion) => posicion + 1,
growable: false,
);

Importante:

Una lista con growable: false es una lista de longitud fija. Esto significa que no podemos añadir ni eliminar elementos.
Sin embargo, esto no significa que la lista sea inmutable. Podemos modificar el valor almacenado en una posición existente.


Ejercicios propuestos

Ejercicio 1:

Crea una función que espere recibir mediante parámetros con nombre una posición y una lista de cadenas, y devuelva el valor de la lista que se encuentra en la posición indicada.

  • La lista debe ser enviada obligatoriamente.
  • Si no se envía la posición, debe utilizarse la posición 0.
  • Si la lista no tiene elementos, debe devolver la cadena 'Sin datos'.
  • Si la posición es negativa o no existe en la lista, debe devolver la cadena 'Índice fuera de rango'.
  • Llama a la función empleando diferentes valores para comprobar su funcionamiento.


Ejercicio 2:

Crea un mapa de datos en el que se guarde como clave un índice entero y como valor un precio.

  • Define inicialmente cuatro entradas en el mapa.
  • Añade dos nuevas entradas.
  • Borra la última entrada introducida.
  • Recorre el mapa empleando forEach. La función debe mostrar la clave y el precio de todos los platos cuyo precio sea inferior a 5 euros.
  • Crea una función de nombre getNumPlatosMasCaros a la que se le pase mediante parámetros con nombre un mapa y un precio.
  • Los dos parámetros deben ser obligatorios y no admitir valores nulos.
  • La función debe devolver cuántos platos tienen un precio superior al indicado.
  • Emplea where para realizar el filtrado.
  • Llama a la función para comprobar que funciona.


Ejercicio 3:

Crea un conjunto de nombre equipo que contenga los valores:

Base
Alero
Pivot
Escolta

Crea una función que reciba como parámetros el conjunto y una cadena a buscar.

  • Emplea el método where.
  • La función debe mostrar los elementos del conjunto que contengan la cadena indicada.
  • Recorre el resultado empleando for...in.
  • Realiza una búsqueda con la cadena 'as'.
  • Realiza otra búsqueda con la cadena 's'.


Ejercicio 4:

Define una lista de números decimales con los siguientes valores:

10.0, 11.22, 15.0, 7.0, 3.44

Crea una función de nombre sonMayores:

  • Debe recibir mediante parámetros con nombre un número y una lista de números decimales.
  • La lista debe ser obligatoria.
  • Si no se envía el número, debe utilizarse el valor 0.
  • Emplea el método every para comprobar si todos los elementos de la lista son mayores que el número indicado.
  • La función debe devolver la cadena 'SI' o 'NO'.
  • Llama a la función empleando los valores 2 y 12.
  • Muestra una frase con el formato:
Todos los números de la lista son mayores a XX: YY
siendo XX el número utilizado e YY el resultado SI o NO.


Ejercicio 5:

Emplea List.generate para realizar los siguientes ejercicios:

  • Genera una lista de 100 números enteros aleatorios comprendidos entre 1 y 100. Emplea la clase Random.
  • Genera una lista con los números comprendidos entre 1 y 100 y haz que tenga longitud fija, de forma que no puedan añadirse ni eliminarse elementos.
  • Dado un número, genera una lista con los cuadrados de todos los números comprendidos entre 1 y dicho número. En este caso, la lista debe permitir añadir nuevos elementos.


Ejercicio adicional:

Investiga otros métodos disponibles en List, Set, Map o Iterable que reciban una función como parámetro.

  • Busca varios ejemplos.
  • Indica qué tipo de función espera cada método.
  • Realiza un pequeño ejemplo de uso.
  • Algunos métodos que puedes investigar son any, firstWhere, fold, removeWhere o updateAll.



Soluciones de los ejercicios propuestos

Solución Ejercicio 1:

String devolverElementoLista({
	int posicion = 0,
	required List<String> lista,
}) {
	if (lista.isEmpty) {
		return 'Sin datos';
	}

	if (
		posicion < 0 ||
		posicion >= lista.length
	) {
		return 'Índice fuera de rango';
	}

	return lista[posicion];
}

void main() {
	final lista = <String>[
		'Uno',
		'Dos',
		'Tres',
		'Cuatro',
	];

	print(
		devolverElementoLista(
			lista: lista,
		),
	);

	print(
		devolverElementoLista(
			posicion: 2,
			lista: lista,
		),
	);

	print(
		devolverElementoLista(
			lista: lista,
			posicion: 4,
		),
	);

	print(
		devolverElementoLista(
			posicion: -1,
			lista: lista,
		),
	);
}


Solución Ejercicio 2:

int getNumPlatosMasCaros({
	required Map<int, double> mapa,
	required double precio,
}) {
	return mapa.values
		.where(
			(precioMapa) => precioMapa > precio,
		)
		.length;
}

void main() {
	final precios = <int, double>{
		1: 10.34,
		2: 3.24,
		3: 24.99,
		4: 17.35,
	};

	precios.addAll({
		5: 9.23,
		6: 2.22,
	});

	print(precios);

	final ultimaClave = precios.keys.last;
	precios.remove(ultimaClave);

	precios.forEach((clave, valor) {
		if (valor < 5) {
			print(
				'La clave $clave tiene un precio de $valor',
			);
		}
	});

	const precioReferencia = 10.0;

	final numPlatos = getNumPlatosMasCaros(
		mapa: precios,
		precio: precioReferencia,
	);

	print(
		'Número de platos con un precio superior '
		'a $precioReferencia euros: $numPlatos',
	);
}


Solución Ejercicio 3:

void buscarCadena(
	Set<String> conjunto,
	String cadena,
) {
	final encontrados = conjunto.where(
		(elemento) => elemento.contains(cadena),
	);

	for (final elemento in encontrados) {
		print('Encontrado: $elemento');
	}
}

void main() {
	final equipo = <String>{
		'Base',
		'Alero',
		'Pivot',
		'Escolta',
	};

	var cadenaBusqueda = 'as';

	print(
		'Cadena de búsqueda: $cadenaBusqueda',
	);

	buscarCadena(
		equipo,
		cadenaBusqueda,
	);

	cadenaBusqueda = 's';

	print(
		'Cadena de búsqueda: $cadenaBusqueda',
	);

	buscarCadena(
		equipo,
		cadenaBusqueda,
	);
}


Solución Ejercicio 4:

String sonMayores({
	double numero = 0,
	required List<double> lista,
}) =>
	lista.every(
		(elemento) => elemento > numero,
	)
		? 'SI'
		: 'NO';

void main() {
	final listaNumeros = <double>[
		10.0,
		11.22,
		15.0,
		7.0,
		3.44,
	];

	var numeroReferencia = 2.0;

	print(
		'Todos los números de la lista son mayores '
		'a $numeroReferencia: '
		'${sonMayores(
			numero: numeroReferencia,
			lista: listaNumeros,
		)}',
	);

	numeroReferencia = 12.0;

	print(
		'Todos los números de la lista son mayores '
		'a $numeroReferencia: '
		'${sonMayores(
			lista: listaNumeros,
			numero: numeroReferencia,
		)}',
	);
}


Solución Ejercicio 5:

import 'dart:math';

void main() {
	final random = Random();

	// 100 números aleatorios entre 1 y 100.
	final numerosAleatorios = List<int>.generate(
		100,
		(_) => random.nextInt(100) + 1,
	);

	print(numerosAleatorios);

	// Números desde 1 hasta 100.
	// Lista de longitud fija.
	final numerosSecuenciales = List<int>.generate(
		100,
		(posicion) => posicion + 1,
		growable: false,
	);

	print(numerosSecuenciales);

	// Cuadrados desde 1 hasta numero.
	// La lista permite añadir elementos.
	const numero = 10;

	final numerosAlCuadrado = List<int>.generate(
		numero,
		(posicion) {
			final valor = posicion + 1;

			return valor * valor;
		},
		growable: true,
	);

	print(numerosAlCuadrado);

	numerosAlCuadrado.add(121);

	print(numerosAlCuadrado);
}




Enlace a la página principal de la UD2

Enlace a la página principal del curso


```