Saltar al contenido principal

Control de Flujo y Estructuras Modernas

El control de flujo define la secuencia en la que se ejecutan las instrucciones de un programa. Dart incluye las estructuras condicionales y de repetición clásicas, pero además incorpora características de lenguajes modernos de tipado estático como Records, Pattern Matching y Switch Expressions.


Estructuras Condicionales​

If-Else​

El bloque if evalúa una condición booleana. Si es verdadera, ejecuta su respectivo bloque de código; de lo contrario, puede saltar a un bloque else if o un bloque por defecto else.

if_else.dart
void main() {
double promedio = 3.8;

if (promedio >= 4.5) {
print('Felicidades, estás en el cuadro de honor de Icesi.');
} else if (promedio >= 3.0) {
print('Aprobaste la materia satisfactoriamente.');
} else {
print('Reprobaste la materia. Debes presentar examen remedial.');
}
}

Switch clásico​

Se utiliza para comparar una sola variable contra múltiples valores posibles.

switch_class.dart
void main() {
String estadoMatricula = 'pendiente';

switch (estadoMatricula) {
case 'activo':
print('El estudiante puede ingresar a clases.');
break;
case 'pendiente':
print('El estudiante debe legalizar su matrícula.');
break;
case 'inactivo':
print('El estudiante ha sido retirado del sistema.');
break;
default:
print('Estado no reconocido.');
}
}

Bucles y Ciclos de Repetición​

Los bucles permiten ejecutar un bloque de código repetidas veces mientras se cumpla una condición dada.

For y For-In​

El bucle for tradicional se usa cuando conoces la cantidad exacta de iteraciones. El bucle for-in es excelente para iterar directamente sobre los elementos de una colección (como listas o sets).

for_loops.dart
void main() {
// For clásico
for (int i = 1; i <= 3; i++) {
print('Iteración número: $i');
}

// For-in para colecciones
List<String> lenguajes = ['Dart', 'Kotlin', 'Swift'];
for (var lenguaje in lenguajes) {
print('Lenguaje móvil: $lenguaje');
}
}

While y Do-While​

  • while: Evalúa la condición antes de ejecutar el bloque. Si la condición inicial es falsa, el cuerpo nunca se ejecuta.
  • do-while: Ejecuta el bloque al menos una vez antes de verificar la condición.
while_loops.dart
void main() {
int contador = 0;

// While
while (contador < 3) {
print('Contador While: $contador');
contador++;
}

// Do-While
int intentos = 0;
do {
print('Intento número: ${intentos + 1}');
intentos++;
} while (intentos < 1);
}

Records​

Introducidos en Dart 3, los Records (registros) son colecciones anónimas y heterogéneas de campos agregados en un solo valor. A diferencia de las listas o mapas, los registros tienen un tamaño fijo en tiempo de compilación y pueden mezclar tipos de datos de forma segura.

Pueden definirse con campos posicionales o con nombres:

records.dart
void main() {
// Registro con campos posicionales
var estudiante = ('Santiago', 21, 4.2);
print(estudiante.$1); // Imprime: Santiago (primer elemento posicional)
print(estudiante.$3); // Imprime: 4.2

// Registro con campos nombrados
var curso = (nombre: 'Desarrollo Móvil', cupo: 25, activo: true);
print(curso.nombre); // Acceso directo por su nombre
print(curso.cupo); // Imprime: 25
}

Pattern Matching y Desestructuración​

El Pattern Matching (coincidencia de patrones) permite verificar si un valor coincide con una estructura determinada y, si es así, extraer (desestructurar) sus partes internas directamente en variables locales.

pattern_matching.dart
void main() {
// Desestructurar un Record
var (nombre, edad) = ('Carlos', 23);
print('Nombre: $nombre | Edad: $edad');

// Desestructuración en listas
var lista = [1, 2, 3];
var [primero, segundo, _] = lista; // El guion bajo (_) ignora el elemento
print('Primero: $primero, Segundo: $segundo');
}

Switch Expressions (Expresiones Switch)​

A partir de Dart 3, switch puede utilizarse como una expresión que retorna un valor directamente. Esto reduce la verbosidad de los switches clásicos al eliminar las palabras clave case, break y default, sustituyéndolas por funciones de flecha (=>) y el comodín _ (wildcard) como caso por defecto.

switch_expressions.dart
enum Nivel { pregrado, posgrado, doctorado }

void main() {
Nivel nivelEstudio = Nivel.posgrado;

// Switch Expression asignado a una variable
String costoMatricula = switch (nivelEstudio) {
Nivel.pregrado => 'Valor base estándar',
Nivel.posgrado => 'Valor base + 20%',
Nivel.doctorado => 'Valor base + 50%',
};

print('Costo asignado: $costoMatricula');
}

Combinación con Pattern Matching​

Podemos usar expresiones switch complejas para validar la forma y el contenido de Records u otros objetos:

switch_patterns.dart
void main() {
// Intentamos procesar la respuesta de una petición
var respuestaServidor = (codigo: 200, mensaje: 'OK');

String estado = switch (respuestaServidor) {
(codigo: 200, mensaje: String msg) => 'Éxito: $msg',
(codigo: 404, mensaje: _) => 'Recurso no encontrado',
(codigo: int c, mensaje: _) when c >= 500 => 'Error crítico del servidor',
_ => 'Código de estado desconocido',
};

print(estado); // Imprime: Éxito: OK
}

Ejercicios Prácticos​

Pon a prueba tu lógica y tu habilidad para estructurar código moderno en Dart.

Ejercicio 1: Desestructurando Resultados de Funciones​

Crea una función llamada obtenerUbicacionIcesi que devuelva un Record con la latitud (double), longitud (double) y el nombre del bloque (String). Luego, en la función main, llama a esta función y utiliza Pattern Matching para extraer las variables individualmente e imprimirlas.

Ver solución detallada
ejercicio1.dart
(double, double, String) obtenerUbicacionIcesi() {
// Retorna un registro posicional
return (3.3426, -76.5304, 'Bloque C');
}

void main() {
// Desestructuración del retorno de la función en variables individuales
var (lat, lng, bloque) = obtenerUbicacionIcesi();

print('Latitud: $lat');
print('Longitud: $lng');
print('Lugar: $bloque');
}

Ejercicio 2: Clasificador de Calificaciones Moderno​

Crea una expresión switch que reciba un Record con la estructura (double nota, int asistencia) y retorne el estado final del estudiante en formato String. Las reglas son:

  • Si la asistencia es menor a 80, no importa la nota, devuelve "Reprobado por inasistencia".
  • Si la nota es mayor o igual a 4.5 y la asistencia es mayor o igual a 90, devuelve "Aprobado con honores".
  • Si la nota es mayor o igual a 3.0, devuelve "Aprobado".
  • Para cualquier otro caso, devuelve "Reprobado".
Ver solución detallada
ejercicio2.dart
void main() {
// Cambia estos valores para probar diferentes escenarios
var recordEstudiante = (nota: 4.6, asistencia: 95);

String resultado = switch (recordEstudiante) {
(nota: _, asistencia: int a) when a < 80 => 'Reprobado por inasistencia',
(nota: double n, asistencia: int a) when n >= 4.5 && a >= 90 => 'Aprobado con honores',
(nota: double n, asistencia: _) when n >= 3.0 => 'Aprobado',
_ => 'Reprobado',
};

print('Resultado académico: $resultado');
}