Programación · Lenguajes de programación
Tutorial de Dart para principiantes
Respuesta corta
Dart es un lenguaje orientado a objetos de tipado estático creado por Google, conocido sobre todo como el lenguaje del framework de apps Flutter. Tiene null safety sólida, compila a código nativo y a JavaScript o WebAssembly, y ofrece hot reload rápido durante el desarrollo. Esta página recorre la instalación, los tipos, la null safety, el control de flujo, las funciones, las colecciones, las clases, el código asíncrono y el manejo de errores.
Qué es Dart y para qué se usa
Dart es un lenguaje de programación optimizado para clientes, desarrollado por Google. Tiene tipado estático con inferencia de tipos, null safety sólida y compila tanto a código máquina nativo (para móvil, escritorio y servidores) como a JavaScript o WebAssembly (para la web). Durante el desarrollo se ejecuta en una máquina virtual con hot reload, de modo que los cambios aparecen en una app en ejecución en pocos segundos.
La mayoría de la gente aprende Dart para crear apps con Flutter, el toolkit de interfaces de Google para Android, iOS, web, Windows, macOS y Linux desde una sola base de código. Dart también se usa para herramientas de línea de comandos y servicios de backend.
Instala Dart y ejecuta Hola, mundo
Instala el SDK de Dart desde dart.dev (o instala Flutter, que incluye Dart). Todo programa en Dart empieza en una función main de nivel superior.
// hello.dart
void main() {
print('Hello, world!');
}dart run hello.dart
# Hello, world!
dart compile exe hello.dart # builds a native executable
dart create my_app # creates a new console projectTambién puedes probar todos los ejemplos de esta página en DartPad, el editor en el navegador de dartpad.dev.
Variables y tipos básicos
Usa var para que el compilador infiera el tipo, final para una variable que se asigna una sola vez y const para una constante en tiempo de compilación. Siempre puedes escribir el tipo de forma explícita.
void main() {
var name = 'Dart'; // inferred as String
int year = 2011; // explicit type
final now = DateTime.now(); // set once, at run time
const pi = 3.14159; // compile-time constant
double ratio = 2; // int literal becomes 2.0
bool ok = year > 2000;
print('$name since $year, pi=${pi.toStringAsFixed(2)}, ok=$ok, ratio=$ratio');
// Dart since 2011, pi=3.14, ok=true, ratio=2.0
print(now.year > 2000); // true
}| Tipo | Ejemplo | Notas |
|---|---|---|
int | 42 | entero; de 64 bits en plataformas nativas |
double | 3.14 | coma flotante de 64 bits |
num | 7 o 7.5 | supertipo de int y double |
bool | true | en las condiciones solo se permiten true y false |
String | 'hi' | UTF-16; admite la interpolación $x y ${expr} |
dynamic | cualquier valor | desactiva la comprobación estática; evítalo en la mayor parte del código |
En la web, los valores int se representan como números de JavaScript, así que allí los enteros muy grandes pueden perder precisión.
Null safety sólida
Desde Dart 3, todo el código Dart usa null safety sólida. Una variable de tipo String nunca puede ser null; para permitir null, escribe String?. El compilador revisa cada uso, de modo que los errores de null se detectan antes de ejecutar el programa.
int lengthOf(String? s) {
// return s.length; // compile error: s might be null
if (s == null) return 0;
return s.length; // promoted to String after the check
}
void main() {
String? nickname;
print(nickname?.length); // null (null-aware access)
print(nickname ?? 'guest'); // guest (default if null)
nickname ??= 'dee'; // assign only if null
print(lengthOf(nickname)); // 3
late String config; // initialized later, checked at run time
config = 'loaded';
print(config); // loaded
}Control de flujo
Dart tiene los habituales if, for, while y do-while, además de un bucle for-in sobre cualquier iterable. Desde Dart 3, switch admite coincidencia de patrones y puede usarse como expresión.
void main() {
var scores = [72, 95, 48];
for (var s in scores) {
if (s >= 90) {
print('$s excellent');
} else if (s >= 50) {
print('$s pass');
} else {
print('$s fail');
}
}
for (var i = 0; i < 3; i++) {
print('i = $i');
}
var day = 6;
var kind = switch (day) {
6 || 7 => 'weekend',
>= 1 && <= 5 => 'weekday',
_ => 'invalid',
};
print(kind); // weekend
}Funciones
Las funciones son objetos de primera clase. Los parámetros pueden ser posicionales, posicionales opcionales entre [] o con nombre entre {}. Los parámetros con nombre son opcionales salvo que se marquen como required. La sintaxis de flecha => es una abreviatura para una función que devuelve una sola expresión.
int add(int a, int b) => a + b;
String greet(String name, {String greeting = 'Hello', required bool loud}) {
var text = '$greeting, $name';
return loud ? text.toUpperCase() : text;
}
String label(String text, [int? width]) =>
width == null ? text : text.padRight(width, '.');
void main() {
print(add(2, 3)); // 5
print(greet('Ana', loud: false)); // Hello, Ana
print(greet('Ana', greeting: 'Hi', loud: true)); // HI, ANA
print(label('id', 5)); // id...
var twice = (int x) => x * 2; // anonymous function
print([1, 2, 3].map(twice).toList()); // [2, 4, 6]
}Colecciones: List, Set, Map y records
Las colecciones básicas son List (ordenada), Set (elementos únicos) y Map (pares clave-valor). Los literales de colección admiten if, for y el operador de propagación .... Los records, añadidos en Dart 3, agrupan varios valores sin declarar una clase.
void main() {
var nums = [3, 1, 2]; // List<int>
nums.add(4);
nums.sort();
print(nums); // [1, 2, 3, 4]
var tags = {'dart', 'flutter', 'dart'};
print(tags.length); // 2
var ages = {'Ann': 31, 'Bo': 25}; // Map<String, int>
ages['Cy'] = 40;
print(ages['Bo']); // 25
var showAll = false;
var menu = ['Home', if (showAll) 'Admin', for (var n in nums) 'Item $n'];
print(menu); // [Home, Item 1, Item 2, Item 3, Item 4]
(String, int) pair = ('Ann', 31); // a record
var (who, age) = pair; // destructuring
print('$who is $age'); // Ann is 31
}Clases y async/await
Dart es orientado a objetos: todo valor es un objeto. Las clases admiten constructores con parámetros de inicialización (this.x), constructores con nombre, getters, mixins e interfaces. El código asíncrono usa Future y Stream con async y await, lo que mantiene las apps de Flutter receptivas mientras esperan archivos o la red.
class Point {
final double x, y;
const Point(this.x, this.y);
Point.origin() : x = 0, y = 0; // named constructor
double get sum => x + y; // getter
@override
String toString() => 'Point($x, $y)';
}
Future<String> fetchUser() async {
await Future.delayed(Duration(milliseconds: 100));
return 'ana';
}
Future<void> main() async {
print(Point(1, 2).sum); // 3.0
print(Point.origin()); // Point(0.0, 0.0)
var user = await fetchUser();
print('user: $user'); // user: ana
}Manejo de errores
Lanza una excepción con throw y trátala con try, on, catch y finally. Usa on SomeType para capturar un tipo concreto. Dart distingue entre Exception (un problema esperado que el programa puede manejar) y Error (un fallo de programación, como una aserción fallida o un error de rango) que normalmente no debes capturar.
int parseAge(String s) {
var n = int.tryParse(s); // null instead of throwing
if (n == null || n < 0) {
throw FormatException('Not a valid age: $s');
}
return n;
}
void main() {
for (var input in ['42', 'abc']) {
try {
print(parseAge(input));
} on FormatException catch (e) {
print('Error: ${e.message}');
} finally {
print('checked $input');
}
}
}
// 42 / checked 42 / Error: Not a valid age: abc / checked abcUn programa completo y pequeño: total del carrito de compras
Este programa modela productos con una clase, guarda un carrito en un mapa e imprime un recibo. Combina clases, mapas, formato de cadenas y métodos de colecciones.
class Product {
final String name;
final double price;
const Product(this.name, this.price);
}
void main() {
const apple = Product('Apple', 0.5);
const bread = Product('Bread', 2.25);
final cart = <Product, int>{apple: 6, bread: 2};
var total = 0.0;
cart.forEach((product, qty) {
final line = product.price * qty;
total += line;
print('${product.name.padRight(6)} x$qty ${line.toStringAsFixed(2)}');
});
final count = cart.values.reduce((a, b) => a + b);
print('$count items, total ${total.toStringAsFixed(2)}');
}
// Apple x6 3.00
// Bread x2 4.50
// 8 items, total 7.50Siguientes pasos
- El recorrido por el lenguaje Dart y los tutoriales en dart.dev.
- Effective Dart, la guía oficial de estilo y diseño.
- La documentación de Flutter en flutter.dev si quieres crear apps.
- pub.dev, el repositorio de paquetes de Dart y Flutter.
Preguntas frecuentes
¿Dart sirve solo para Flutter?
No. Dart es un lenguaje de propósito general y permite crear herramientas de línea de comandos, servidores y apps web. Flutter es simplemente el motivo más popular para aprenderlo.
¿Dart es compilado o interpretado?
Existen ambos modos. Durante el desarrollo, Dart se ejecuta en una VM con compilación just-in-time y hot reload; para la versión final se compila de forma anticipada a código máquina nativo, o a JavaScript o WebAssembly para la web.
¿Cuál es la diferencia entre final y const en Dart?
Una variable final se asigna una sola vez, posiblemente en tiempo de ejecución. Un valor const debe conocerse en tiempo de compilación y es inmutable en profundidad, lo que permite a Flutter reutilizar widgets constantes.
¿Dart se parece a Java o a JavaScript?
Su sintaxis de estilo C y sus clases resultan familiares a quien programa en Java, C# o JavaScript. A diferencia de JavaScript, Dart tiene tipado estático y null safety sólida.
¿Qué hace el operador ?? en Dart?
a ?? b devuelve a si no es null y, en caso contrario, b. El operador relacionado a ??= b asigna b a a solo cuando a es null.