latest

Introducción al lenguaje Dart. Básico. Parte 3

Continuando con el curso de Flutter y, en particular, con la Introducción Básica al lenguaje Dart, en la Parte 2 ya se indicó que esta introducción estaba pensada para personas que han programado con anterioridad en lenguajes orientados a objetos como Java, C# o C++. Por este motivo, "damos por sentado" muchos conceptos básicos que el lector conoce por su experiencia previa en estos lenguajes.

Operadores aritméticos y condicionales

Los operadores aritméticos y condicionales son básicamente los mismos de lenguajes como Java o C#, por tanto, no se va a hacer mayor hincapié en este apartado.

Sentencias de control de flujo

Las sentencias if-else, el operador ternario ()?():()y la sentencia condicional múltiple switch se construyen exactamente igual que en Java, siguiendo la misma sintaxis y palabras clave. Lo mismo ocurre con los bucles for, while y do-while.

Por poner un ejemplo...

void main() {
	var idiomaSeleccionado = "ES";
    	switch(idiomaSeleccionado){    
     		case 'EN': 
            	print('Inglés seleccionado');
                break;
            case 'IT':
            	print('Italiano seleccionado');
                break;
            case 'ES':
            	print('Español seleccionado');
                break;
            default:
            	throw('El idioma seleccionado no existe');
    }
}

En el ejemplo de switch de arriba se ha usado una sentencia throw para lanzar una excepción en caso de que el idioma seleccionado no esté contemplado por el sistema.

Os animamos a que probéis por vosotros mismos las sentencias de control de flujo desde el editor y consola en la nube de DartPad.

Funciones en Dart

La definición de funciones en Dart es ciertamente más flexible que en Java gracias a los parámetros opcionales, tanto los de tipo posicional, como los nombrados. Vamos a empezar con un ejemplo sencillo de definición de función y su utilización.

void main(){
	mostrarMenuPorRol("administrador");
}

mostrarMenuPorRol(rol){
	print("Dispositivos conectados en laboratorio de "
    	"investigación accesibles por usuario");
	switch(rol){
    	case "administrador":
        	print("\nMenú del administrador:\n"
            "1. Mostrar Usuarios\n"
            "2. Añadir Usuario\n"
            "3. Modificar Usuario\n"
            "4. Borrar Usuario\n"
            "5. Mostrar todos los Dispositivos\n");
            break;
        case "usuario":
        	print("\nMenú del usuario:\n"
            "1. Mostrar Dispositivos\n"
         	"2. Encender Dispositivo\n"
            "3. Apagar Dispositivo\n");
        	break;
        default:
        	throw("El rol no está contemplado en el sistema");
    }
}

Bueno, aunque Dart nos permite hacer cosas como lo mostrado arriba, donde no se indica el tipo de datos del parámetro rol en la definición de la función mostrarMenuPorRol(), lo adecuado es indicarlo, así como también el tipo de retorno de la función. En este particular caso es un String y void para el retorno, respectivamente.

void main(){
	mostrarMenuPorRol("administrador");
}

void mostrarMenuPorRol(String rol){
	print("Dispositivos conectados en laboratorio de "
    	"investigación accesibles por usuario");
	switch(rol){
    	case "administrador":
        	print("\nMenú del administrador:\n"
            "1. Mostrar Usuarios\n"
            "2. Añadir Usuario\n"
            "3. Modificar Usuario\n"
            "4. Borrar Usuario\n"
            "5. Mostrar todos los Dispositivos\n");
            break;
        case "usuario":
        	print("\nMenú del usuario:\n"
            "1. Mostrar Dispositivos\n"
         	"2. Encender Dispositivo\n"
            "3. Apagar Dispositivo\n");
        	break;
        default:
        	throw("El rol no está contemplado en el sistema");
    }
}

En el siguiente ejemplo os mostramos otro aspecto interesante de Dart en lo relativo a la definición de funciones. Se trata de un estilo reducido de definición de funciones usando => que evita escribir las llaves cuando podemos implementar la función en una única sentencia de código.

void main(){
	mostrarMenuParaUsuario();
}

void mostrarMenuParaUsuario() => print("\nMenú del usuario:\n" "1. Mostrar Dispositivos\n"  "2. Encender Dispositivo\n"  "3. Apagar Dispositivo\n");
El ejemplo de arriba sería equivalente a la siguiente definición de función. Observad que la función esta comentada con un comentario multilínea (igual que en Java).
/*void mostrarMenuParaUsuario(){
  print("\nMenú del usuario:\n" 
    "1. Mostrar Dispositivos\n" 
    "2. Encender Dispositivo\n" 
    "3. Apagar Dispositivo\n");
}*/

Una de las opciones más interesantes de Dart en lo relativo a la definición de funciones es el empleo de parámetros opcionales. El siguiente ejemplo ilustra de manera detallada cómo utilizar parámetros opcionales posicionales.

void main(){
	// Sin parámetros opcionales:
    direccionNacional("Carretas",4,"7ºD","28004","Madrid");
    // Con algún parámetro opcional posicional (no todos):
    direccionNacional("Cóndor",3,"2ºA","28800","Móstoles",6);
    direccionNacional("Limón",3,"3ºB","15800","Toledo",null,"Esc A");
    // Con todos los parámetros opcionales posicionales:
    direccionNacional("Góngora",1,"Bajo","28900","Getafe",2,"Esc B");
    // Todos los parámetros opcionales posicionales pero falta alguno obligatorio:
    direccionNacional("Góngora",1,null,"28900","Getafe",2,"Esc B");
}
    
void direccionNacional(String calle, num numero, String puerta, 
String codigoPostal, String ciudad, [num portal, String escalera]){
	String direccion="$calle, Número:$numero, ";
    if(portal!=null && escalera!=null){
    	direccion += "Portal:$portal, Escalera:$escalera, ";	
    } else if(portal!=null){
    	direccion += "Portal:$portal, ";	
    } else if(escalera!=null){
    	direccion += "Escalera:$escalera, ";	
    } 
    direccion += "Puerta:$puerta, CP:$codigoPostal, $ciudad" "\n";
    print(direccion);
}

Los parámetros opcionales posicionales se colocan siempre al final, después de los parámetros obligatorios y encerrados entre corchetes. Como puede verse en las llamadas a la función direccionNacional(), podemos obviar todos los parámetros opcionales; o poner sólo alguno de ellos, en ese caso, si omitimos alguno de los parámetros posicionales que van delante del que queremos usar tenemos que pasar argumentos null para respetar su posición en la definición de la función. De manera similar si no tenemos alguno de los parámetros obligatorios hay que respetar su posición pasando null. No obstante, no parece buena idea omitir parámetros considerados obligatorios. De nuevo os recomendamos probar estos ejemplos y "trastear" usando el editor en la nube DartPad.

Los parámetros opcionales nombrados, ofrecen todavía un enfoque más flexible que los posicionales, con ellos podemos evitar pasar argumentos null a los parámetros opcionales que no se usen durante las llamadas a las funciones. Vamos a reconvertir el ejemplo anterior pero empleando parámetros opcionales nombrados.

void main(){
	// Sin parámetros opcionales:
    direccionNacional("Carretas",4,"7ºD","28004","Madrid");
    // Con algún parámetro opcional posicional (no todos):
    direccionNacional("Cóndor",3,"2ºA","28800","Móstoles",portal:6);
    direccionNacional("Limón",3,"3ºB","15800","Toledo",escalera:"Esc A");
    // Con todos los parámetros opcionales posicionales:
    direccionNacional("Góngora",1,"Bajo","28900","Getafe",escalera:"Esc B",portal:2);
    // Todos los parámetros opcionales posicionales pero falta alguno obligatorio:
    direccionNacional("Góngora",1,null,"28900","Getafe",portal:2,escalera:"Esc B");
}
    
void direccionNacional(String calle, num numero, String puerta, 
String codigoPostal, String ciudad, {num portal, String escalera="sin escalera"}){
  	StringBuffer direccion = StringBuffer("$calle, Número:$numero, ");
    if(portal!=null && escalera!=null){
    	direccion.write("Portal:$portal, Escalera:$escalera, ");	
    } else if(portal!=null){
    	direccion.write("Portal:$portal, ");	
    } else if(escalera!=null){
    	direccion.write("Escalera:$escalera, ");	
    } 
    direccion.write("Puerta:$puerta, CP:$codigoPostal, $ciudad" "\n");
    print(direccion.toString());
}

Vamos a analizar despacio el código anterior. Los parámetros posicionales nombrados también se colocan al final de la cabecera de definición de la función, después de los parámetros obligatorios. Se encierran entre llaves {} pudiendo especificar algún valor por defecto para cada argumento en caso de que no sea explícitamente dado, si bien, no es obligatorio. En el ejemplo se ha proporcionado un valor por defecto para la variable escalera, concretamente "sin escalera".

También cambia la sintaxis de estos parámetros opcionales en las llamadas a las función direccionNacional. Como podéis comprobar se indica el nombre del parámetro opcional nombrado seguido de ":" y el valor de dicho parámetro, e.j.: escalera:"Esc B". Como puede comprobarse, ya no es necesario respetar la posición entre los parámetros opcionales nombrados ni interponer parámetros opcionales con valor a null.

Por último, en este ejemplo hemos optado por usar un objeto de tipo StringBuffer para unir cadenas, en lugar del operador de concatenación "+" del ejemplo anterior. Puede verse que StringBuffer se usa prácticamente igual que en Java.

Y hasta aquí los conceptos básicos en Dart. En las siguientes partes del curso de Flutter nos adentraremos en los conceptos intermedios de programación en Dart, hablando de POO en Dart, constructores, herencia, sobreescritura...

Author image
Iván González is postdoctoral researcher at the Castilla-La Mancha University.
Ciudad Real (Spain)