Introducción a Dart
::
Esta página proporciona una breve introducción al lenguaje Dart a través de ejemplos de sus características principales.
Para obtener más información sobre el lenguaje Dart, visita las páginas detalladas de temas individuales que figuran en Lenguaje en el menú del lado izquierdo.
Para conocer la cobertura de las bibliotecas principales de Dart, consulta la documentación de la biblioteca core ↗. También puedes probar el codelab de hoja de trucos de Dart, para obtener una introducción más práctica.
Hola Mundo
Cada aplicación requiere la función global main()
, donde comienza la ejecución. Las funciones que no devuelven explícitamente un valor tienen el tipo de retorno void
. Para mostrar texto en la consola, puedes usar la función global print()
:
void main() {
print('Hola, Mundo!');
}
Lee más sobre la función main()
↗ en Dart, incluidos los parámetros opcionales para los argumentos en la línea de comandos.
Variables
Incluso en el código type-safe ↗ de Dart, puedes declarar la mayoría de las variables sin especificar explícitamente su tipo usando var
. Gracias a la inferencia de tipos, los tipos de estas variables están determinados por sus valores iniciales:
var name = 'Voyager I';
var year = 1977;
var antennaDiameter = 3.7;
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
var image = {
'tags': ['saturn'],
'url': '//path/to/saturn.jpg'
};
Lee más sobre las variables en Dart, incluidos los valores predeterminados, las palabras clave final
y const
, y los tipos estáticos.
Declaraciones de control de flujo
Dart admite las declaraciones de control de flujo habituales:
if (year >= 2001) {
print('21st century');
} else if (year >= 1901) {
print('20th century');
}
for (final object in flybyObjects) {
print(object);
}
for (int month = 1; month <= 12; month++) {
print(month);
}
while (year < 2016) {
year += 1;
}
Lee más sobre las declaraciones de control de flujo en Dart, incluyendo break
y continue
↗, switch
y case
↗ y assert
↗.
Tipos en Funciones
Recomendamos ↗ especificar los tipos de argumentos de cada función y el valor de retorno:
int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
var result = fibonacci(20);
Una sintaxis abreviada =>
(flecha) es útil para funciones que contienen una sola declaración. Esta sintaxis es especialmente útil cuando se pasan funciones anónimas como argumentos:
flybyObjects.where((name) => name.contains('turn')).forEach(print);
Además de mostrar una función anónima (el argumento de where()
), este código muestra que puedes usar una función como argumento: la función global print()
es un argumento para forEach()
.
Lee más ↗ sobre las funciones en Dart, incluidos los parámetros opcionales, los valores de los parámetros predeterminados y el alcance léxico.
Comentarios
Los comentarios de Dart generalmente comienzan con //
.
// Este es un comentario normal de una sola línea.
/// Este es un comentario de documentación, utilizado para documentar
/// bibliotecas, clases y sus miembros. Herramientas como IDE y dartdoc
/// tratan los comentarios de documentos de manera especial.
/* Comentarios como estos también son compatibles. */
Lee más ↗ sobre los comentarios en Dart, incluido cómo funcionan las herramientas de documentación.
Importaciones
Para acceder a las API definidas en otras bibliotecas, usa import
.
// Importa una bibliotecas del core
import 'dart:math';
// Importa una biblioteca desde un paquete externo
import 'package:test/test.dart';
// Importa un archivo
import 'path/to/my_other_file.dart';
Lee más ↗ sobre las bibliotecas y la visibilidad en Dart, incluidos los prefijos de bibliotecas, show
y hide
, y la carga diferida a través de la palabra clave deferred
.
Clases
Aquí tienes un ejemplo de una clase con tres propiedades, dos constructores y un método. Una de las propiedades no se puede establecer directamente, por lo que se define mediante un método getter (en lugar de una variable). El método utiliza interpolación de cadenas para imprimir los equivalentes de cadenas de las variables dentro de cadenas literales.
class Spacecraft {
String name;
DateTime? launchDate;
// Propiedad no final y de solo lectura.
int? get launchYear => launchDate?.year;
// Constructor, con azúcar sintáctico para asignaciones a miembros.
Spacecraft(this.name, this.launchDate) {
// El código de inicialización va aquí.
}
// Constructor con nombre que reenvía al predeterminado.
Spacecraft.unlaunched(String name) : this(name, null);
// Método.
void describe() {
print('Spacecraft: $name');
// La promoción de tipos no funciona en getters.
var launchDate = this.launchDate;
if (launchDate != null) {
int years = DateTime.now().difference(launchDate).inDays ~/ 365;
print('Launched: $launchYear ($years years ago)');
} else {
print('Unlaunched');
}
}
}
Lee más ↗ sobre cadenas, incluida la interpolación de cadenas, literales, expresiones y el método toString()
.
Puedes usar la clase Spacecraft
de esta manera:
var voyager = Spacecraft('Voyager I', DateTime(1977, 9, 5));
voyager.describe();
var voyager3 = Spacecraft.unlaunched('Voyager III');
voyager3.describe();
Lee más ↗ sobre las clases en Dart, incluidas las listas de inicializadores, new
y const
opcionales, redirección de constructores, constructores factory
, getters, setters y mucho más.
Enums
Las enumeraciones son una forma de enumerar un conjunto predefinido de valores o instancias de una manera que garantiza que no pueda haber otras instancias de ese tipo.
Aquí hay un ejemplo de una enum
simple que define una lista simple de tipos de planetas predefinidos:
enum PlanetType { terrestrial, gas, ice }
Aquí hay un ejemplo de una declaración de enumeración mejorada de una clase que describe planetas, con un conjunto definido de instancias constantes, es decir, los planetas de nuestro propio sistema solar.
/// Enum que enumera los diferentes planetas de nuestro
/// sistema solar y algunas de sus propiedades.
enum Planet {
mercury(planetType: PlanetType.terrestrial, moons: 0, hasRings: false),
venus(planetType: PlanetType.terrestrial, moons: 0, hasRings: false),
// ···
uranus(planetType: PlanetType.ice, moons: 27, hasRings: true),
neptune(planetType: PlanetType.ice, moons: 14, hasRings: true);
/// Un constructor generador constante.
const Planet(
{required this.planetType, required this.moons, required this.hasRings});
/// Todas las variables de instancia son finales.
final PlanetType planetType;
final int moons;
final bool hasRings;
/// Las enumeraciones mejoradas admiten getters y otros métodos
bool get isGiant =>
planetType == PlanetType.gas || planetType == PlanetType.ice;
}
Puedes usar la enumeración Planet
de esta manera:
final yourPlanet = Planet.earth;
if (!yourPlanet.isGiant) {
print('Tu planeta no es un "planeta gigante".');
}
Lee más ↗ sobre enumeraciones en Dart, incluidos requisitos de enumeración mejorados, propiedades introducidas automáticamente, acceso a nombres de valores enumerados, compatibilidad con declaraciones switch
, y mucho más.
Herencia
Dart tiene herencia única.
class Orbiter extends Spacecraft {
double altitude;
Orbiter(super.name, DateTime super.launchDate, this.altitude);
}
Leer má ↗ sobre la extensión de clases, la anotación opcional @override
y más.
Mixins
Los mixins son una forma de reutilizar código en múltiples jerarquías de clases. La siguiente es una declaración mixin:
mixin Piloted {
int astronauts = 1;
void describeCrew() {
print('Número de astronautas: $astronauts');
}
}
Para agregar las capacidades de un mixin a una clase, simplemente extiende la clase con el mixin.
class PilotedCraft extends Spacecraft mixin Piloted {
// ···
}
PilotedCraft
ahora tiene el campo astronauts
así como el método describeCrew()
.
Lee más ↗ sobre mixins.
Interfaces y clases abstractas
Todas las clases definen implícitamente una interfaz. Por lo tanto, puedes implementar cualquier clase.
class MockSpaceship implements Spacecraft {
// ···
}
Lee más sobre interfaces implícitas ↗, o sobre la palabra clave interface
↗ explícita.
Puedes crear una clase abstracta para ampliarla (o implementarla) con una clase concreta. Las clases abstractas pueden contener métodos abstractos (con cuerpos vacíos).
class Describable {
void describe();
void describeWithEmphasis() {
print('=========');
describe();
print('=========');
}
}
Cualquier clase que extienda Describable
tiene el método describeWithEmphasis()
, que llama a la implementación del extensor de describe()
.
Lee más ↗ sobre clases y métodos abstractos.
Async
Evita el infierno de las devoluciones de llamadas y haz que tu código sea mucho más legible usando async
y await
.
const oneSecond = Duration(seconds: 1);
// ···
Future<void> printWithDelay(String message) {
await Future.delayed(oneSecond);
print(message);
}
El método anterior es equivalente a:
Future<void> printWithDelay(String message) {
return Future.delayed(oneSecond).then((_) {
print(message);
});
}
Como muestra el siguiente ejemplo, async
y await
ayudan a que el código asincrónico sea fácil de leer.
Future<void> createDescriptions(Iterable<String> objects) async {
for (final object in objects) {
try {
var file = File('$object.txt');
if (await file.exists()) {
var modified = await file.lastModified();
print(
'El archivo para $object ya existe. Fue modificado el $modified.');
continue;
}
await file.create();
await file.writeAsString('Comienza a describir $object en este archivo.');
} on IOException catch (e) {
print('No se puede crear una descripción para $object: $e');
}
}
}
También puedes usar async*
, que te brinda una forma agradable y legible de crear streams.
Stream<String> report(Spacecraft craft, Iterable<String> objects) async* {
for (final object in objects) {
await Future.delayed(oneSecond);
yield '${craft.name} flies by $object';
}
}
Lee más ↗ sobre el soporte de asincronía, incluidas las funciones async
, Future
, Stream
y el bucle asincrónico (await for
).
Excepciones
Para generar una excepción, usa throw
:
if (astronauts == 0) {
throw StateError('Sin astronautas.');
}
Para detectar una excepción, usa una declaración try
con on
o catch
(o ambos):
Future<void> describeFlybyObjects(List<String> flybyObjects) async {
try {
for (final object in flybyObjects) {
var description = await File('$object.txt').readAsString();
print(description);
}
} {
print('No se pudo describir el objeto: $e');
} finally {
flybyObjects.clear();
}
}
Ten en cuenta que el código anterior es asíncrono; try
funciona tanto para código síncrono como para código en una función async
.
Lee más ↗ sobre las excepciones, incluidos los stack traces, rethrow
y la diferencia entre Error
y Exception
.
Conceptos importantes
A medida que continúes aprendiendo sobre el lenguaje Dart, ten en cuenta estos factos y conceptos:
- Todo lo que puedes colocar en una variable es un objeto, y cada objeto es una instancia de una clase. Los números pares, las funciones y
null
son objetos. Con la excepción denull
(si habilitas null-safety ↗), todos los objetos heredan de la claseObject
↗. - Aunque Dart está fuertemente tipado, las anotaciones de tipo son opcionales porque Dart puede inferir tipos. En
var number = 101
, se infiere quenumber
es de tipoint
. - Si habilitas null-safety ↗, las variables no pueden contener
null
a menos que le digas que pueden hacerlo. Puedes hacer que una variable sea nullable (que aceptanull
) poniendo un signo de interrogación (?
) al final de su tipo. Por ejemplo, una variable de tipoint?
podría ser un número entero o podría sernull
. Si sabes que una expresión nunca se evalúa comonull
pero Dart no está de acuerdo, puedes agregar!
para afirmar que no es nula (y generar una excepción si lo es). Un ejemplo:int x = nullableButNotNullInt!
. - Cuando quieras decir explícitamente que se permite cualquier tipo, usa el tipo
Object?
(si has habilitado null-safety),Objeto
o, si debes posponer la verificación de tipos hasta el tiempo de ejecución, el tipo especialdynamic
↗. - Dart admite tipos generics, como
List<int>
(una lista de números enteros) oList<Object>
(una lista de objetos de cualquier tipo). - Dart admite funciones globales (como
main()
), así como funciones vinculadas a una clase u objeto (métodos estáticos y de instancia, respectivamente). También puedes crear funciones dentro de funciones (anidadas o funciones locales). - De manera similar, Dart admite variables globales, así como variables vinculadas a una clase u objeto (variables estáticas y de instancia). Las variables de instancia a veces se conocen como campos o propiedades.
- A diferencia de Java, Dart no tiene las palabras clave
public
,protected
yprivate
. Si un identificador comienza con un guión bajo (_
), es privado para su biblioteca. Para obtener más información, consulta Bibliotecas e importaciones ↗. - Los Identificadores pueden comenzar con una letra o un guión bajo (
_
), seguido de cualquier combinación de esos caracteres más dígitos. - Dart tiene expresiones (que tienen valores de tiempo de ejecución) y declaraciones (que no los tienen). Por ejemplo, la expresión condicional ↗
condition ? expr1 : expr2
tiene un valor deexpr1
oexpr2
. Compara eso con una declaración if-else ↗, que no tiene valor. Una declaración a menudo contiene una o más expresiones, pero una expresión no puede contener directamente una declaración. - Las herramientas Dart pueden informar dos tipos de problemas: advertencias y errores. Las advertencias son sólo indicaciones de que tu código podría no funcionar, pero no impiden que tu programa se ejecute. Los errores pueden ser en tiempo de compilación o en tiempo de ejecución. Un error en tiempo de compilación impide que el código se ejecute; un error en tiempo de ejecución da como resultado una excepción ↗ que se genera mientras se ejecuta el código.
Recursos adicionales
Puedes encontrar más documentación y ejemplos de código en la documentación de la biblioteca del core ↗ y la referencia de la API de Dart ↗. El código de este sitio sigue las convenciones de la guía de estilo de Dart ↗.