Este cheatsheet cubre los aspectos esenciales de Dart, el lenguaje de programación de Google optimizado para construir aplicaciones rápidas en cualquier plataforma.
- DartPad: 📝 Entorno de desarrollo online para Dart: https://dartpad.dev/
- SDK de Dart: 📦 Incluye la máquina virtual de Dart (Dart VM), bibliotecas, el compilador, y otras herramientas.
dart(línea de comandos): ⚙️ Herramienta principal del SDK. Se usa para ejecutar, compilar, analizar, y formatear código Dart.pub(gestor de paquetes): 📦 Herramienta para gestionar dependencias en Dart (similar a npm en Node.js).pubspec.yaml: 📄 Archivo que define las dependencias y metadatos de un proyecto Dart.- Comentarios:
// Comentario de una línea/* Comentario de bloque *//// Comentario de documentación
-
Declaración de variables:
var nombre = "Juan"; // Inferencia de tipo (String) String apellido = "Pérez"; // Tipo explícito dynamic edad = 30; // Tipo dinámico (puede cambiar) - Evitar en lo posible Object miObjeto = "Hola"; // Object es la clase base de todos los objetos.
-
Tipos de datos básicos:
-
int: Enteros.int edad = 30; int hex = 0x1A; // Hexadecimal
-
double: Números de punto flotante (doble precisión).double precio = 99.99; double exponente = 1.42e5; // Notación científica
-
String: Cadenas de texto.String nombre = "Ana"; String mensaje = 'Hola, mundo!'; String multilinea = ''' Este es un string multilinea. '''; String interpolacion = "Mi nombre es $nombre"; // Interpolación String concatenacion = "Hola" + " " + "Mundo";
-
bool: Booleanos (trueofalse).bool esValido = true;
-
num: Superclase deintydouble.
-
-
finalyconst:-
final: La variable solo se puede asignar una vez (similar aconsten JavaScript).final nombre = "Pedro"; // nombre = "Otro"; // Error: no se puede reasignar
-
const: Constante en tiempo de compilación. Debe inicializarse con un valor que se conozca en tiempo de compilación. Implícitamentefinal.const PI = 3.14159; const colores = ['rojo', 'verde', 'azul']; // Lista constante
-
-
Valores por defecto: Las variables no inicializadas tienen el valor
nullpor defecto (incluso las numéricas). -
Literales:
42(int)3.14(double)'Hola'(String)true(bool)[1, 2, 3](List){'a': 1, 'b': 2}(Map)#miSimbolo(Symbol)null
-
Aritméticos:
+,-,*,/,~/(división entera),%(módulo). -
De igualdad y relacionales:
==,!=,>,<,>=,<=. -
Lógicos:
&&(AND),||(OR),!(NOT). -
De asignación:
=,+=,-=,*=,/=,~/=,%=. -
Incremento y decremento:
++,--(pre y post). -
Condicional (ternario):
condicion ? expresion1 : expresion2. -
De comprobación de tipo:
is,is!.if (variable is String) { /* ... */ }
-
Operador de cascada (
..): Permite realizar múltiples operaciones sobre el mismo objeto.var lista = [] ..add(1) ..add(2) ..add(3);
-
Operador de acceso condicional (
?.): Accede a una propiedad o método solo si el objeto no esnull.String? nombre; print(nombre?.length); // Imprime null (no da error)
-
Operador de propagación (
...y...?):var lista1 = [1, 2, 3]; var lista2 = [0, ...lista1, 4]; // [0, 1, 2, 3, 4] List<int>? lista3 = null; var lista4 = [0, ...?lista3, 4]; //Con ? para evitar error si es null.
-
Operador de coalescencia nula (
??):
String? nombre;
String nombreCompleto = nombre ?? "Invitado"; // Si nombre es null, usa "Invitado"- Operador de asignación si es nulo (
??=):int? a; a ??= 5; // Asigna 5 a 'a' solo si 'a' es null.
- Operadores bitwise:
&,|,^,~,<<,>>,>>>. - Llamada a función con !:
funcion!()Llama una función que puede ser null, lanzando error si lo es.
-
if...else:if (condicion) { // ... } else if (otraCondicion) { // ... } else { // ... }
-
switch:switch (variable) { case valor1: // ... break; case valor2: // ... break; default: // ... }
-
for:for (var i = 0; i < 5; i++) { print(i); } // for...in (para iterar sobre elementos de una colección) var numeros = [1, 2, 3]; for (var numero in numeros) { print(numero); }
-
while:var i = 0; while (i < 5) { print(i); i++; }
-
do...while:var i = 0; do { print(i); i++; } while (i < 5);
-
breakycontinue:
-
Declaración de función:
// Función con tipo de retorno y parámetros int sumar(int a, int b) { return a + b; } // Función sin tipo de retorno explícito (void implícito) void saludar(String nombre) { print("Hola, $nombre!"); } // Función con retorno inferido (=>) int multiplicar(int a, int b) => a * b;
-
Parámetros opcionales:
-
Posicionales opcionales (
[]):void saludar(String nombre, [String? saludo]) { print("${saludo ?? 'Hola'}, $nombre!"); } saludar("Juan"); // Hola, Juan! saludar("Juan", "Buenos días"); // Buenos días, Juan!
-
Nombrados opcionales (
{}):void presentar({String? nombre, int? edad}) { print("Nombre: ${nombre ?? 'Desconocido'}, Edad: ${edad ?? 'Desconocida'}"); } presentar(nombre: "Ana", edad: 30); presentar(edad: 25); presentar();
-
Parámetros con valores por defecto:
void saludar(String nombre, {String saludo = "Hola"}) { print("$saludo, $nombre!"); } saludar("Juan"); // Hola, Juan! saludar("Juan", saludo: "Buenos días"); // Buenos días, Juan!
-
Combinar parámetros obligatorios con opcionales
-
-
Funciones anónimas (lambdas):
var miFuncion = (int a, int b) { return a + b; }; // Función anónima con retorno inferido var otraFuncion = (int x, int y) => x * y;
-
Funciones como objetos de primera clase: Las funciones se pueden asignar a variables, pasar como argumentos a otras funciones, y retornar desde funciones.
-
Función main(): Punto de entrada de la aplicación.
void main() { print("Hola, mundo!"); }
-
Funciones de orden superior: Funciones que toman otras como parámetro, o las retornan.
-
Declaración de clase:
class Persona { String? nombre; // Propiedades (campos) int? edad; // Constructor Persona(this.nombre, this.edad); // Método void saludar() { print("Hola, soy $nombre y tengo $edad años."); } // Constructor con parámetros nombrados Persona({this.nombre, this.edad}); // Constructor con valores por defecto // Persona({this.nombre = "Invitado", this.edad = 0}); // Named constructor Persona.invitado() { nombre = 'Invitado'; edad = 0; } // Factory constructor factory Persona.fromJson(Map<String, dynamic> json) { return Persona(json['nombre'], json['edad']); } }
-
Creación de objetos:
var persona1 = Persona("Juan", 30); var persona2 = Persona(nombre: "Ana", edad: 25); // Usando parámetros nombrados persona1.saludar();
-
Getters y setters:
class Rectangulo { double ancho = 0; double alto = 0; // Getter double get area { return ancho * alto; } // Setter (opcional) set area(double valor) { ancho = valor / 2; alto = valor / 2; } } var rect = Rectangulo(); rect.ancho = 10; rect.alto = 5; print(rect.area); // 50 (usa el getter) rect.area = 100; // Usa el setter
- Se puede usar
=>para getters y setters cortos:double get area => ancho * alto;
-
Herencia:
class Empleado extends Persona { String? puesto; Empleado(String nombre, int edad, this.puesto) : super(nombre, edad); @override // Sobre escritura void saludar() { super.saludar(); // Llama al método de la clase padre print("Soy un empleado con puesto $puesto"); } }
-
Clases abstractas:
abstract class Animal { void hacerSonido(); // Método abstracto (sin implementación) } class Perro extends Animal { @override void hacerSonido() { print("Guau!"); } }
-
Interfaces (implícitas): En Dart, todas las clases definen implícitamente una interfaz.
class Volador { void volar() {} } // Implementa la interfaz Volador class Pato implements Volador { @override void volar() { print("Pato volando..."); } }
-
Mixins: Permiten reutilizar código de múltiples clases sin herencia múltiple.
mixin Caminante { void caminar() { print("Caminando..."); } } mixin Nadador { void nadar() { print("Nadando..."); } } class Pato with Caminante, Nadador {} var pato = Pato(); pato.caminar(); pato.nadar();
-
Extension methods: Añaden funcionalidad a clases existentes (incluso de bibliotecas externas).
extension StringExtension on String { String aMayusculas() { return toUpperCase(); } } void main() { print("hola".aMayusculas()); // HOLA }
-
Clases estáticas:
class Matematicas { static double PI = 3.14159; static double cuadrado(double x) { return x * x; } } void main() { print(Matematicas.PI); // Acceder a un miembro estático print(Matematicas.cuadrado(5)); }
-
Operador cascada: Permite hacer operaciones múltiples sobre el mismo objeto.
-
Listas (List):
var numeros = [1, 2, 3, 4, 5]; // Lista literal var frutas = <String>['manzana', 'plátano']; // Lista con tipo explícito var listaVacia = []; // Lista vacía var otraListaVacia = <String>[]; numeros.add(6); // Agregar un elemento numeros.addAll([7, 8]); // Agregar varios numeros.removeAt(0); // Eliminar por índice print(numeros.length); // Longitud print(numeros[2]); // Acceder a un elemento por índice print(numeros.first); print(numeros.last); print(numeros.isEmpty); print(numeros.contains(3)); print(numeros.indexOf(4)); // Iterar for (var numero in numeros) { print(numero); } numeros.forEach((numero) => print(numero)); var cuadrados = numeros.map((numero) => numero * numero).toList(); // Transformar var filtrados = numeros.where((numero) => numero > 3).toList(); //Filtrar numeros.sort(); // Ordenar numeros.shuffle(); // Mezclar. var sublista = numeros.sublist(1,4); var invertida = numeros.reversed.toList();
-
Conjuntos (Set): Colecciones de elementos únicos (no ordenados).
var miSet = {1, 2, 3, 2, 1}; // {1, 2, 3} - elimina duplicados var otroSet = <String>{'rojo', 'verde', 'azul'}; var setVacio = <String>{}; miSet.add(4); miSet.addAll({5,6}); miSet.remove(1); print(miSet.length); // Tamaño print(miSet.contains(3)); // true // Iterar for (var elemento in miSet) { print(elemento); } print(miSet.union(otroSet)); print(miSet.intersection(otroSet)); print(miSet.difference(otroSet));
-
Mapas (Map): Colecciones de pares clave-valor.
var miMapa = { // Mapa literal 'nombre': 'Juan', 'edad': 30, 'ciudad': 'Madrid' }; var otroMapa = <String, dynamic>{ //Con tipado 'nombre': 'Ana', 'edad': 25 }; var mapaVacio = {}; var otroMapaVacio = <String, int>{}; print(miMapa['nombre']); // Acceder a un valor por clave miMapa['pais'] = 'España'; // Agregar o modificar un valor miMapa.remove('edad'); print(miMapa.length); // Número de pares clave-valor print(miMapa.containsKey('ciudad')); // true print(miMapa.containsValue('Juan')); print(miMapa.keys); //Iterable con las claves print(miMapa.values); //Iterable con los valores // Iterar for (var clave in miMapa.keys) { print('$clave: ${miMapa[clave]}'); } miMapa.forEach((clave, valor) => print('$clave: $valor')); var mapaTransformado = miMapa.map((key, value) => MapEntry(key, value.toString().toUpperCase())); //Transformar
-
Runes: Representan los code points Unicode de un String.
-
Symbols: Objetos usados para representar operadores o identificadores.
-
Future: Representa un valor potencial (o error) que estará disponible en algún momento en el futuro.Future<String> obtenerDatos() { return Future.delayed(Duration(seconds: 2), () => "Datos recibidos"); }
-
asyncyawait: Simplifican el trabajo con Futures.Future<void> miFuncionAsincrona() async { try { String datos = await obtenerDatos(); // Espera a que el Future se complete print(datos); } catch (error) { print("Error: $error"); } }
-
Stream: Secuencia de datos asíncronos (como unFutureque puede devolver múltiples valores).Stream<int> contador(int max) async* { for (int i = 1; i <= max; i++) { yield i; // Emite un valor await Future.delayed(Duration(seconds: 1)); // Espera 1 segundo } } void main() { var miStream = contador(5); miStream.listen((numero) { print(numero); // 1, 2, 3, 4, 5 (cada segundo) }); }
async*: Se usa para funciones que retornan un Stream.yield: Emite un valor en el Stream.
- Then/catchError: Alternativa a async/await
obtenerDatos().then((valor){
print(valor);
}).catchError((error){
print(error);
});- A partir de Dart 2.12, la null safety es obligatoria.
- Los tipos no pueden ser
nullpor defecto. - Para permitir que una variable sea
null, se usa el operador?.
String? nombre; // 'nombre' puede ser null
int? edad = null;
print(nombre?.length); // Usar ?. para acceder a propiedades/métodos de forma segura
String valor = nombre ?? "Valor por defecto"; //Operador ??
void miFuncion({String? parametroOpcional}){} // Parámetro opcional que puede ser null.- Late variables:
late String descripcion; // Se inicializará después, antes de ser usada.
- Operador ! (bang operator):
String? posibleNulo = "Hola"; String noNulo = posibleNulo!; // Asegura que no es nulo (lanza error si lo es)
-
Excepciones (try, catch, on, finally, throw):
try { // Código que puede lanzar una excepción } on FormatException catch (e) { // Capturar un tipo específico de excepción print("Error de formato: $e"); } catch (e, s) { // Capturar cualquier excepción print("Error: $e"); print(s); // Stack trace } finally { // Código que se ejecuta siempre (haya o no excepción) } // Lanzar una excepción throw Exception("Algo salió mal");
-
Enumeraciones (enum):
enum Color { rojo, verde, azul } void main() { var miColor = Color.rojo; print(miColor); // Color.rojo switch (miColor) { case Color.rojo: print("Rojo"); break; case Color.verde: print("Verde"); break; case Color.azul: print("Azul"); break; } }
-
Generics: Permiten escribir código que funciona con diferentes tipos de datos, de forma segura.
class Caja<T> { T? contenido; Caja(this.contenido); T? obtenerContenido() => contenido; } var cajaEnteros = Caja<int>(5); var cajaString = Caja<String>('Hola');
-
Typedefs: Define un alias para un tipo de función.
-
Metadatos: Anotaciones como
@deprecated,@override. Se pueden crear anotaciones personalizadas. -
Mixins: Permiten reutilizar código de múltiples clases.
Este cheatsheet cubre una amplia gama de características de Dart, desde los fundamentos hasta conceptos más avanzados. ¡Espero que te sea de gran ayuda!