cv.cm

Programación · Lenguajes de programación

Tutorial de Dart para principiantes

8 min de lectura

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 project

Tambié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
}
TipoEjemploNotas
int42entero; de 64 bits en plataformas nativas
double3.14coma flotante de 64 bits
num7 o 7.5supertipo de int y double
booltrueen las condiciones solo se permiten true y false
String'hi'UTF-16; admite la interpolación $x y ${expr}
dynamiccualquier valordesactiva 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 abc

Un 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.50

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

Todos los tutoriales de programación