Flutter DART Funciones

De MediaWiki
Ir a la navegación Ir a la búsqueda

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.
Como comenté, este curso está orientada a alumnos que ya saben programar en algún lenguaje..
  • 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 otros lenguajes podemos pasar datos a las funciones empleando los parámetros.
Como ya vimos, podemos definir los parámetros para que acepten valores nulos.


Por valor/referencia

  • 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 :) )
Los valores que sean objetos de una clase serán pasados por referencia.
class Pelota {
  double? peso;
  String? id;
}

void modificarValores(Pelota pelota, int valor){
  
  valor = 10;
  pelota.peso = 100.2;
  pelota.id = 'P2';
  
}
void main() {
  
  int valorPrueba = 5;
  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}');

}



Parámetros optativos

  • Podemos definir un parámetro como optativo mediante el empleo de [ ] de la forma siguiente:
void imprimirMensaje([String mensaje='Nada enviado']){
  
  print(mensaje);
}

void main() {
  
  imprimirMensaje();   // No enviamos el dato, imprime el valor por defecto
  
  imprimirMensaje('Nuevo Mensaje');  
}
  • 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 imprimirMensaje([String? mensaje]){

}


  • En caso de que a función leve parámetros obrigatorios e optativos, deben de definirse primeiro os obrigatorios:
void imprimirMensaje(int param1, [String? mensaje]){

}



Orden de envío de los datos

  • 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.


  • La forma de hacer esto es definiendo el conjunto de parámetros de la función entre llaves.



Parámetros obligatorios/opcionales

  • Al rodear los parámetros (o parte de ellos) entre llaves, estamos obligando a emplear el nombre de los parámetros cuando llamamos a la función, pero no estamos obligados a enviar dicho dato.
Tenemos que indicar si acepta valores nulos o si tiene un valor por defecto.
/**
 * Parámetro acepta valores nulos
 */
void imprimirMensaje1({String? mensaje}){ 
  
  print(mensaje);
}

/**
 * Parámetro tiene un valor por defecto
 */
void imprimirMensaje2({String mensaje='Valor por defecto'}){ 
  
  print(mensaje);
}


void main() {
  
  imprimirMensaje1();  // No obliga a enviar dato, el parámetro pasa a tener el valor null
  imprimirMensaje2();  // No obliga a enviar dato, coge el valor por defecto
  
}


  • Pero existe una tercera opción. Podemos indicar que ciertos parámetros sean obligatorios empleando la palabra clave required antes del tipo de dato.
De esta forma ya no tenemos que indicar que el parámetro acepte valores nulos o tenga un valor por defecto.
Más información: https://dart.dev/null-safety/understanding-null-safety#required-named-parameters
Veamos un ejemplo:
void imprimirMensaje({required String mensaje}){
  
  print(mensaje);
}

void main() {
  
  imprimirMensaje(mensaje: 'Mensaje');
  
}
Nota: Podemos tener parámetros sin nombre y parámetros con nombre, pero debemos de tener en cuenta que primero tienen que estar definidos los parámetros sin nombre.
void imprimirMensaje(int numero, {String? mensaje}){
  
  print("El número tiene $numero y el mensaje $mensaje");
}

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



Funciones flecha o Lambda

  • Son las arrow function o funciones lambda.
Son funciones que retornan una expresión y que no pueden tener bloque de código.
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.


Su sintaxis sería la siguiente:
return_type function_name(arguments) => expression;


Por ejemplo:
double sumar(double a, double b) => a+b;

void main() {

  print(sumar(10.22,22.34));  
}


  • Si queremos que ejecuta una instrucción cualquiera:
void imprimirMediaNumero({required int num1, required int num2}) => print((num1+num2)/2);


void main() {

  imprimirMediaNumero(num1: 10, num2: 15);
}



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.
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.
Veamos en el ejemplo que vimos antes que tipo de datos va a ser E:
Flutter dart fuhciones 1.JPG
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.
La implementación de la funcuón anónima sería la siguiente:
void main() {

  List<int> lista = [1,2,3,4,5,6];

  lista.forEach((elemento) { // Código de la función 
                           });
  
}
Hasta este punto, lo importante es el parámetro elemento 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):
void main() {

  List<int> lista = [1,2,3,4,5,6];

  lista.forEach((elemento) { // Código de la función 
                             print('Accediendo al elemento $elemento');
                           });
}
Esta forma de definir funciones se usa habitualmente en Flutter.
  • Como ya vimos antes las funciones arrows, podemos modificar el código de la función anónima en el caso de ejecutar una única sentencia.
void main() {

  List<int> lista = [1,2,3,4,5,6];

  lista.forEach((elemento) => print('Accediendo al elemento $elemento'));
}
Nota importante: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



  • A continuación muestro otras formas de ejecutar el bucle forEach:
void gestionarElemLista(int elem){
  
  print('Elemento de la lista: $elem');
}

void function; gestionarElemLista2() {
  return (item) {
    print("The number is $item");
  };
}

void gestionarElemLista3(int elemLista){
  print('El número es $elemLista');
}


void main() {

  List<int> lista = [1,2,3,4,5,6];

  lista.forEach((elemento) => gestionarElemLista(elemento));
  lista.forEach(gestionarElemLista2());

  lista.forEach((elemento) => print('Función anónima : Elemento de la lista: $elemento'));

  lista.forEach(gestionarElemLista3);    // NO PONEMOS PARÉNTESIS

  
}



Veamos otros ejemplos de forEach aplicada a otras estructuras de datos....
  • forEach en Mapas:
void main() {

  Map<String,double>mapa = {
    'base' : 1.84,
    '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'));  
}
Fijarse como la función anónima del forEach aplicado al Map espera recibir dos parámetros: uno representa la clave y otro el valor asociado a dicha clave.



  • forEach en Conjuntos:
void main() {

  var conjunto = {'Angel','Luis','Ana','María'};
  
  conjunto.forEach((valor) => print('El elemento de la lista: $valor'));

}



  • Cuando empleamos los métodos que esperan una función como parámetro (como por ejemplo forEach), podemos definir la función sin emplear una función anónima de la forma:
Set<String> conjunto = {'uno','dos','tres'};
  conjunto.forEach(recorrerConjunto);
}
void recorrerConjunto(String n){
  print(n);
}


Como veis, en el método forEach indicamos sólo el nombre de la función y defino la función con el parámetro que necesita el método forEach.



Método map aplicado a List y Set


  • Esta función es muy útil en Flutter, ya que vamos a generar un conjunto de Widget en base a los datos que haya en una lista o conjunto.
Lo que hace esta función es, por cada uno de los elementos de la lista/conjunto crear un nuevo objeto que irá añdiéndolos a un objeto Iterable. Al finalizar estarán todos los nuevos objetos creados en dicho objeto Iterable. Lo que se hace normalmente es llamar al método toList() para convertir dicho objeto Iterable en una lista.
Como resultado, tendremos una nueva lista con una serie de nuevos objetos basados en los datos de la lista inicial.
  • Veamos un ejemplo:
  Map<String,String> mapa={};

  mapa.addAll({'nombre' : 'angel', 'apellido1' : 'fernandez', 'apellido2' : 'gonzalez'});
  List<String> lista = mapa.entries.map((e) => ('HOLA:${e.value}') ).toList();  // Creamos por cada elemento del conjunto una cadena con el formato 'HOLA:elem_conjunto'. Convertimos todo a una lista.
  print(lista);





Ejercicios propuestos

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.

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'.


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.


Ejercicio 3: En este ejercicio vas a probar a emplear otro método que permite emplear funciones anónimas, como es el 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.

Flutter dart fuhciones 2.JPG
En la imagen anterior:
  • 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 'conjunto iterable': Iterable<String>.
Por lo tanto vamos a poder recorrer dichos elementos empleando el método iterator, como ya vimos con las listas y conjuntos o bien podemos hacer uso del método forEach como vimos anteriormente.

Teniendo en cuenta todo lo anterior, crea un conjunto de nombre equipo que tenga los valores 'Base','Alero','Pivot','Escolta'. 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


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. 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. Llama a dicha función con el valor 2 y con el valor 12. 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.


Ejercicio 5: La clase List tiene un 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. Variación 2: Crea una lista en la que dado un número se guarde en la lista el cuadrado de todos los números desde 1 hasta el mismo, y que se puedan añadir nuevos elementos.


Ejercicios propuestos: 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.




Soluciones Ejercicios propuestos

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'.
String devolverElementoLista(
  {int pos=0, required List listaRecibir})
{
  if(listaRecibir.isEmpty) return 'Sin datos';
  if(pos >= listaRecibir.length) return 'Indice fuera de rango.';
  
  return listaRecibir[pos];
}

void main() {

  final lista = ['Uno','Dos','Tres','Cuatro'];
  
  print(devolverElementoLista(listaRecibir: lista));
  print(devolverElementoLista(listaRecibir: lista,pos: 2));
  print(devolverElementoLista(listaRecibir: lista,pos: 4));
  

}


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.
int getNumPlatosMasCaros({required Map mapa, required double precio}){
  
  var contador=0;
  mapa.values.forEach((preciosmapa) => preciosmapa > precio ? contador++ : 0);
  
  return contador;
  
}

void main() {

  Map<int,double> precios = {
    1 : 10.34,
    2 : 3.24,
    3 : 24.99,
    4 : 17.35
  };  

  precios.addAll({5 : 9.23, 6: 2.22});  // Añade dos nuevos elememtos al mapa
  print(precios);
  
  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)}');
  
}


Solución Ejercicio 3: Crea un conjunto de nombre equipo que tenga los valores 'Base','Alero','Pivot','Escolta'. 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

void main() {

  final equipo = {'Base','Alero','Pivot','Escolta'};
  var cadenaBusqueda = 'as';
  
  print('Cadena de búsqueda:$cadenaBusqueda');
  equipo.where((elemento) => elemento.indexOf(cadenaBusqueda) > 0)
    .forEach((elemento) => print('Encontrado:$elemento'));
  
  cadenaBusqueda='s';
  print('Cadena de búsqueda:$cadenaBusqueda');
  equipo.where((elemento) => elemento.indexOf(cadenaBusqueda) > 0)
    .forEach((elemento) => print('Encontrado:$elemento'));
  
}


Solución 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. 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. Llama a dicha función con el valor 2 y con el valor 12. 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.

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


void main() {

  final listaNumeros = [10.0, 11.22, 15.0, 7.0, 3.44];
  
  var numeroReferencia = 2;
  
  print('Todos los números de la lista son mayores a $numeroReferencia: ${sonMayores(numero: numeroReferencia, lista : listaNumeros)}');

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



Solución Ejercicio 5: La clase List tiene un 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. Variación 2: Crea una lista en la que dado un número se guarde en la lista el cuadrado de todos los números desde 0 hasta el mismo, y que se puedan añadir nuevos elementos.

void main() {

  var numerosSecuenciales = List<int>.generate(100,(posicion) => posicion);
  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);
  
  
}




Enlace a la página principal de la UD2

Enlace a la página principal del curso




-- Ángel D. Fernández González -- (2021).