Dart¶
Última edición el día 2020-11-20 a las 15:46.
Esta es la documentación que he recopilado para trabajar dart. Dart, junto con Flutter podemos desarrollar aplicaciones Nativas para Android.
Índice
Elementos básicos del lenguaje¶
Instalación de Dart¶
Nos vamos al sitio: https://dart.dev/ hacemos clic en «get Dart» arriba a la derecha y buscamos la instalación de nuestro sistema. Para confirmar que esta instalado ejecutamos dart –version en una terminal.
Comentarios¶
Los comentarios se pueden realizar de dos tipos:
Comentarios de una sola línea:
// Comentario de una sola linea
Comentarios multilínea:
/*
Esto es un comentario multilínea
Aquí podemos establecer mas de
una linea
*/
Documentado especial:
/// Documentado especial [nombre] marcado para identificar mejor y asignar datos por ejemplo [nombre]: guillermo. esto permanecerá
Estructura en Python¶
La estructura de Dart recuerda a la de lenguajes antecesores como C o C++ pero sin la necesidad de establecer tipo de dato ni importar librerías estandares:
main(){
print('Hola mundo');
}
Palabras reservadas de Dart¶
En Dart existen las siguientes palabras reservadas:
abstract
as
asset
async
async*
await
break
case
catch
class
const
contine
default
deferred
do
dynamic
else
enum
export
external
extends
factory
FALSE
final
finally
for
get
if
implements
import
in
is
library
new
null
operator
try
rethrow
return
set
static
super
switch
sync*
library
this
throw
TRUE
try
typedef
var
void
while
with
yield
yield*
Variables y tipos de datos¶
Variables¶
Aunque en Dart declaramos la función main sin establecer un tipo de dato no pasa lo mismo con las variables y funciones.
Ejemplo:
int a = 10;
Final¶
En lugar de utilizar constantes en Dart se utiliza mayoritariamente Final para definir este tipo de dato ya que ahorra mas memoria:
final altura = 2.132;
Tipos de datos primitivos¶
Los tipos de datos mas comunes son los siguientes:
Tipo de dato |
Denominación |
Ejemplo |
---|---|---|
String |
Cadena de texto |
“cadena”, «cadena» |
int |
Número Entero |
20, 5, -3, 0 |
double |
Número con decimales |
20.53, 12.5, -18.353 |
bool |
Verdadero o falso |
True, False |
list |
Arreglo de datos | [1, 5, «Hola», True] |
|
map |
Objeto con orden de tipo clave:valor |
{“nombre”:”Pedro”, edad: 45} |
Ejemplos:
String cadena = "Dia de paga";
int entero = 27;
double decimal = 2.332;
bool booleano = true;
List<dynamic> lista = [1, 5, 13, "Papiro"];
// con los mapas se define el tipo de dato tanto de la clave como del valor:
Map<String, dynamic> mapa = {"profesion": "programador", "hobbie":"DIY", "edad": 32};
Tipo de datos que puede tener una lista o un Mapa¶
Cuando trabajamos con listas o mapas no solo declaramos la variable de tipo List
o Map
. También es necesario declarar que tipo de datos va a contener esa lista.
Ejemplos de uso:
// Lista que contiene todo tipo de datos:
List<dynamic> lista = [12, "galletas", true];
// Lista solo de strings:
List<String> nombres = ["Luis", "Amelia", "Gabriel"];
// En los mapas declaramos el tipo de dato que tendrá la clave y el tipo de dato que tendrá el valor:
Map<String, dynamic> = {"nombre": "Guillermo", "edad": 33};
Entrada y Salida de datos¶
En Dart la entrada y salida de datos se presenta de la siguiente forma:
Entrada de datos:
// Importamos la librería de entrada de datos de la consola:
import 'dart:io';
main(){
// Imprimir en la terminal:
stdout.writeln('¿Cómo te llamas?');
// leer información de la consola y guardarlo en una variable:
String nombre = stdin.readLineSync();
// Devolver respuesta por consola:
stdout.writeln('Tu nombre es: ' + nombre);
// Podemos hacer la misma agregando la variable en una cadena utilizando el simbolo dolar:
stdout.writeln('Tu nombre es $nombre');
}
Salida de datos:
import 'dart:io';
main(){
// La salida de datos normal
print('Hola que tal');
// La salida de datos con dart:io
stdout.writeln('Hola que tal');
// imprimir una variable:
String nombre = "Guillermo";
print("Hola $nombre");
}
Operadores¶
Operadores Aritméticos¶
Los operadores aritméticos que se presentan en python son los mismos que en la mayoría de lenguajes,
+, -, *, /, %
Estos podemos utilizarlos del siguiente modo:
// asignación:
suma = 2 + 2;
// salida de datos:
print(3 - 2);
// si utilizamos + en cadenas las concatenamos:
cadenas = "cadena uno" + " y " + " cadena dos";
// Y también podemos definir un valor si un elemento es null:
b??= 20;
Operadores Relacionales¶
Los operadores relacionales en python son los mismos que en la mayoría de lenguajes de programación:
Operador |
símbolo |
---|---|
Mayor que |
> |
Menor que |
< |
Igual que |
== |
Mayor igual que |
>= |
Menor igual que |
<= |
Cuando hablamos del uso de un solo =
nos referimos a la asignación de un valor en una variable.
Como en muchos lenguajes, si imprimimos por consola la relación entre un valor y otro el resultado será True o False:
// Si decimos que 3 es mayor que 2
print(3 > 2);
// el resultado que sale por consola es true.
Operadores Lógicos¶
En Dart se utilizan los mismos operadores lógicos que en la mayoría de lenguajes de programación, sin embargo presentan un aspecto diferente:
Operador |
símbolo |
condición |
---|---|---|
and |
&& |
La condición se cumple si todos son verdaderos |
or |
|| |
La condición se cumple si al menos uno es verdadero |
not |
! |
La condición se cumple si es diferente a lo que se compara |
Ejemplos:
// Resultado False:
print(5 > 7 and 3 < 6);
// Resultado True:
print(5 > 7 or 3 < 6);
// Resultado True
print(6 != 3);
Estructuras de control¶
En python disponemos de estructuras de control como if
, for
y while
.
Condicional if¶
Las condiciones sencillas en dart funcionan del siguiente modo:
main(){
int edad = 18;
if(edad >= 18){
print('Eres mayor de edad, tienes $edad años');
}
}
También tenemos condiciones con una salida alternativa si no se cumple esta:
main(){
int edad = 16;
if(edad >= 18){
print('Eres mayor de edad, tienes $edad años');
}else{
print('Tienes $edad y por tanto eres menor de edad');
}
}
Condicional if-elif¶
Las condiciones compuestas nos ofrecen varios caminos posibles:
main(){
int edad = 68;
if(edad >= 18){
print('Eres mayor de edad, tienes $edad años');
}else if(edad >= 65){
print('Con $edad, eres un anciano');
}else{
print('Tienes $edad y por tanto eres menor de edad');
}
}
Condicional Ternario¶
El condicional ternario abrevia una condición básica if-else para asignar un valor u otro a una variable:
int c = 21;
String resp = c > 25 ? 'C es mayor a 25' : 'C es menor a 25';
print(resp);
Bucle for¶
El bucle for en dart se presenta de un modo muy similar al foreach de otros lenguajes:
Uso con rango definido:
main(){
for(int i = 0; i <= 10; i++){
print('hola mundo! $i');
}
}
Bucle For in¶
El bucle for in esta diseñado para recorrer Listas y Mapas:
main(){
List<String> listado = ['Batman', 'Superman', 'Aquaman'];
// Listar con for clásico:
for(int i = 0; i < listado.length; i++){
print(listado[i]);
}
// For in:
for(String listar in listado){
print(listar);
}
}
Bucle while¶
El bucle While es similar a otros lenguajes:
Ejemplo:
import 'dart:io';
main(){
String continuar = 's';
int contador = 0;
while(continuar == 's'){
contador++;
stdout.writeln('Contador: $contador');
stdout.writeln('desea continuar? (s/n)');
continuar = stdin.readLineSync();
}
}
Ejemplo con bucle infinito:
numero = 10
# al añadir True hacemos un bucle infinito:
while True:
adivina = int(input('Adivinia el número >> '))
if adivina == numero:
print('Acertaste!')
// Con exit() finalizamos el programa
exit()
print('Fallaste!')
Ciclo do-while¶
En Dart disponemos también del ciclo do-while:
import 'dart:io';
main(){
String continuar = 'y';
int contador = 0;
do{
contador++;
stdout.writeln('Contador: $contador');
stdout.writeln('desea continuar? (y/n)');
continuar = stdin.readLineSync();
}while(continuar == 'y');
}
Romper un bucle y continuar¶
Podemos romper un bucle o continuar con las palabras break
y continue
Ejemplo de uso:
main(){
for(int i = 0; i < 10; i++){
if(i == 5){
continue;
}
print(i);
if(i == 2){
break;
}
}
}
condicional switch¶
En Dart tenemos disponible Switch para controlar el flujo de información:
// Importamos el paquete para random:
import 'dart:math';
main(){
// Creamos un entero que tenga un random de 7 números:
int rnd = Random().nextInt(7);
switch(rnd){
case 1:
print('Lunes');
break;
case 2:
print('Martes');
break;
case 3:
print('Miércoles');
break;
case 4:
print('Jueves');
break;
case 5:
print('Viernes');
break;
case 6:
print('Sábado');
break;
case 7:
print('Domingo');
break;
default:
print('No es un día de la semana');
}
}
Estructuras de datos¶
Listas¶
Las listas son un tipo de dato mutable que agrupa un conjuto de valores de distintos tipos:
// Lista de strings:
List<String> personas = ['Pepe', 'Luis'];
print(personas);
// Lista de cadenas:
List<int> numeros = [10, 12];
// Añadir registros:
personas.add('Alfonso');
// Crear una lista vacia:
List<String> apellidos = new List();
// insertar varios registros:
apellidos.addAll(['Lopez', 'Suarez', 'Martinez']);
print(apellidos);
// Agregar en cascada:
apellidos..add('Gutierrez')
..add('Alferez');
// Definir los elementos que tendrá una lista:
List<String> meses = new List(12);
//addAll no nos sirve para listas definidas porque se añade siempre al final y ya tenemos las posiciones asignadas.
// Se haría así:
meses[0] = 'Enero';
meses[1] = 'Febrero';
meses[2] = 'Marzo';
print(meses);
Sets¶
Los sets son similiares a las listas pero con la diferencia de que estas son inmutables.
Ejemplo de lista:
// Sets (similar a la lista pero no acepta valores duplicado como las listas)
Set<String> lenguajes = {'c', 'python', 'dart'};
// añadir elementos:
lenguajes.add('PHP');
print(lenguajes);
Mapas¶
Los mapas en Dart se asemejan a los objetos literales en javascript u otros lenguajes de programación como los diccionarios en Python, etc…
Se construye por pares de datos que son clave y valor {'nombre':'Antonio'}
y pueden anidar todo tipo de datos e incluso más diccionarios.
Ejemplo de uso:
// Prueba con dinamicos e inicializando el objeto:
Map<String, dynamic> persona = new Map();
// agregar elementos:
persona.addAll({"nombre": "Luis", "edad": 32});
print(persona);
Métodos Internos¶
Como en la mayoría de lenguajes de programación, en Python existen funciones predefinidas.
Métodos de cadenas¶
Aquí tenemos los métodos mas utilizados para tratamiento de cadenas de texto:
Para saber la longitud de una cadena con
length
:
main(){
String nombre = "Guillermo";
print(nombre.length);
}
Convertir valor numérico a cadena con
toString()
:
main(){
int edad = 25;
print(edad.toString());
}
Convertir una cadena en una lista
split()
:
main(){
String comprar = "Agua, Galletas, Huevos, Yogurt";
List listaDeLaCompra = comprar.split(", ");
print(listaDeLaCompra);
}
Reemplazar una cadena por otra con
replaceAll()
:
main(){
String comprar = "Agua, Galletas, Huevos, Yogurt";
comprar = comprar.replaceAll(", ", " - ");
print(comprar);
}
Convertir a mayúsculas la cadena con
toUpperCase()
:
main(){
String comprar = "Agua, Galletas, Huevos, Yogurt";
comprar = comprar.toUpperCase();
print(comprar);
}
Convertir a minúsculas la cadena con
toLowerCase()
:
main(){
String comprar = "Agua, Galletas, Huevos, Yogurt";
comprar = comprar.toLowerCase();
print(comprar);
}
Funciones numéricas¶
Estas son las funciones numéricas mas comunes en Dart:
Convertir un valor a entero con
toInt()
:
main(){
double edad = 38.23;
int miEdad = edad.toInt();
print(miEdad);
}
Convertir un valor a decimal con
toDouble()
:
main(){
int edad = 40;
double miEdad = edad.toDouble();
print(miEdad + 17.37);
}
Redondear un valor decimal con
round()
:
main(){
int edad = 40;
double miEdad = edad.toDouble() + 183.33234;
print(miEdad.round());
}
Otras funciones comunes¶
Tenemos una serie de funciones de uso común en python:
Averiguar que tipo de dato contiene una variable con
runtimeType
:
main(){
int edad = 40;
double miEdad = edad.toDouble() + 183.33234;
print(miEdad.runtimeType);
}
Funciones¶
Las funciones en Dart se declaran definiendo el tipo de retorno (int, String, Map) o si no retornan nada (void).
// Ya sabemos que main es una función, de hecho la principal siempre:
main(){
// Para llamar a la función:
saludar();
// Si nuestra funcion retorna algo podemos asignar el retorno a una variable del mismo tipo:
String mensaje = saludo();
print(mensaje);
}
// Para crear una función lo hacemos fuera de main:
void saludar(){ // al no retornar nada le asignamos el valor void
print('hola desde la función');
}
// Si nuestra función retorna algo como una cadena lo tenemos que definir:
String saludo(){
return 'saludo desde retorno';
}
Recibir parametros en una función por consola:
// la palabra args define que nuestro programa va a recibir parámetros de consola:
main(List<String> args) {
print(args);
}
Ahora ejecutamos el archivo dart argumentos.dart argumento1 argumento2
Funciones Lambda o de flecha¶
Este tipo de funciones tienen como objetivo resumir la función en una sola línea haciendo el retorno directamente tras la flecha:
main(){
// Uso de la función de flecha:
int a = 10;
int b = 15;
int resultado = sumarFlecha(a,b);
print(resultado);
}
// Decalramos la funcion de flecha:
int sumarFlecha(int x, int y) => x + y;
Funciones Callback¶
Las funciones callback se invocan dentro de otra función para ejecutar un código inmediatamente dentro de la función llamada:
main(){
// Ejecutamos la función la cual le pasamos un parametro string y un callback:
obtenerUsuario('100',(Map persona){
print(persona);
});
}
//creamos una función:
void obtenerUsuario(String id, Function callback){
Map usuario = {
'id': id,
'nombre': 'Alfredo'
};
// Llamamos la función callback que estamos pasandole por parámetro:
callback(usuario); // Recibirá el usuario.
}
Promesas¶
Las promesas son tareas asíncronas que se van a resolver en un futuro.
Ejemplo de Future:
main(){
// Este es un ejemplo de definir un tipo de future sencillo:
Future timeout = Future.delayed(Duration(seconds: 3), (){ // Este código se va a ejecutar 3 segundos desupés.
print('3 segundos después');
});
// Como el otro codigo queda a la espera, este se ejecutará antes:
print('Fin del main');
}
Retorno de un Future:
main(){
// Definimos la salida como String:
Future<String> timeout = Future.delayed(Duration(seconds: 3), (){ // Este código se va a ejecutar 3 segundos desupés.
return 'Retorno de dato';
});
// Y ahora lo ejecutamos del siguiente modo para que devuelva el dato retornado:
timeout.then((texto)=> print(texto));
}
Async-Await¶
Al igual que en lenguajes como Javascript, Dart dispone de Async y Await de forma nativa.
Supongamos que tenemos un archivo llamado personas.txt:
Veamos como se recuperan los datos con Future:
// Importamos la librería io:
import 'dart:io';
main(){
// Establecemos una ruta donde se encuentra nuestro archivo:
String path = Directory.current.path + '/personas.txt';
// Usamos Then para cumplir la promesa e imprimimos el archivo:
leerArchivo(path).then(print);
}
// Tenemos un Future que retorna un string que recibira como parámetro una ruta:
Future<String> leerArchivo(String path){
// En el creamos un nuevo objeto de tipo file y le pasamos la ruta:
File file = new File(path);
// Devolvemos el archivo en formato string:
return file.readAsString();
}
Y ahora vamos a hacerlo con Async-Await:
import 'dart:io';
// Cada vez que utilicemos el await tenemos que asignar async en la función donde lo hagamos:
main() async{
String path = Directory.current.path + '/personas.txt';
// Ahora vamos a cambiar el then por un await:
String texto = await leerArchivo(path);
print(texto);
}
// Transformamos nuestro Future en un async:
leerArchivo(String path) async{
File file = new File(path);
return file.readAsString();
}
Clases¶
Las clases en Dart tienen una estructura similar al de otros lenguajes,
import 'dart:io';
main(){
// Creamos un objeto a partir de la clase:
final persona = new Persona();
// Para añadir datos a sus atributos:
persona.nombre = 'Guillermo';
persona.edad = 32;
persona.bio = 'Soy el tipo ese que programa por estudiar';
// Al utilizar override para pasar la clase a cadena podemos imprimir el objeto:
print(persona);
}
// Creamos una clase:
class Persona {
// Atributos
String nombre;
int edad;
String bio;
// Get y sets
// Constructores
// Métodos
// El @override es un decorador y sirve para subscribir un método:
@override
String toString(){
return '$nombre $edad $bio';
}
}
Atributos Privados¶
Para hacer que los atributos sean privados en Dart el modificador que utilizamos es _
class Persona {
// Para hacer un atributo privado ponemos el caracter _
String _nombre;
int _edad;
String _bio;
// Get y sets
// Constructores
// Métodos
@override
String toString(){
return '$_nombre $_edad $_bio';
}
}
Getters y Setters¶
Con Get y Set podemos trabajar con atributos privados fuera de clase, osea cuando hemos creado un objeto.
class Persona {
String nombre;
int edad;
String _bio;
// Get y sets
// Los gets recuperan información:
String get bio {
return _bio;
}
// Para establecer el valor al atributo privado utilizamos set:
set bio(String texto) {
_bio = texto;
}
// Constructores
// Métodos
@override
String toString(){
return '$nombre $edad $_bio';
}
}
Constructores básicos¶
En Dart para las clases como en cualquier lenguaje tenemos los constructores comunes:
Creamos un archivo llamado Persona.dart:
class Persona {
String nombre;
int edad;
String _bio;
// Get y sets
String get bio {
return _bio;
}
set bio(String texto) {
_bio = texto;
}
// Constructores
// Constructor básico lleva por defecto el nombre de la clase:
Persona(int edad, String nombre){ // Normalmente le pasamos argumentos a un constructor
print('Constructor');
// Podemos asignar un valor mediante el setter o utilizar el atributo privado:
bio = 'Hola desde el constructor';
this._bio = 'Utilizando el privado';
// Le asignamos los argumentos a sus atributos:
this.edad = edad;
this.nombre = nombre;
}
// Métodos
@override
String toString(){
return '$nombre $edad $_bio';
}
}
Y ahora creamos nuestro archivo main.dart:
import 'persona.dart';
main(){
// Ahora le tenemos que pasar los dos parametros al constructor:
final persona = new Persona(32, 'Guillermo');
persona.bio = "Soy un tipejo";
print(persona.bio);
}
Veamos como se pasan argumentos posicionales:
import 'persona.dart';
main(){
// Y como es posicional le asignamos el nombre:
final persona = new Persona(edad:32, nombre:'Guillermo');
persona.bio = "Soy un tipejo";
print(persona.bio);
}
Constructores con nombre¶
Los constructores con nombre son constructores adicionales que se generan en las clases
Ejemplo de clase:
class Persona{
String nombre;
int edad;
String _bio;
// Get y sets
String get bio {
return _bio;
}
set bio(String texto) {
_bio = texto;
}
Persona({this.edad, this.nombre}){ // Podemos hacer opcional los parametros también
print('Constructor');
}
// Vamos a crear un constructor con nombre:
Persona.persona30( this.nombre ){
this.edad = 30;
}
@override
String toString(){
return '$nombre $edad $_bio';
}
}
Crear objeto en main:
import 'persona.dart';
main(){
final persona = new Persona(edad:32, nombre:'Guillermo');
// Nos creamos un nuevo objeto con el constructor nombre:
final persona2 = new Persona.persona30('Alfredo');
persona.bio = "Soy un tipejo";
print(persona.bio);
print(persona2);
}
Atributos y métodos estáticos¶
Al utilizar el modificador static
podemos acceder a los atributos y métodos que lo posean sin necesidad de crear un objeto.
class Herramientas{
// Convertimos en estatico el listado:
static const List<String> listado = ['Martillo', 'Llave Inglesa','Desarmador'];
// Metodo estatico:
static void imprimirListado() => listado.forEach(print);
}
main() {
// Y así podemos acceder al listado sin crear un objeto y lo recorremos con el metodo forEach:
Herramientas.listado.forEach(print);
// Vamos a llamar al metodo de forma estatica:
Herramientas.imprimirListado();
// Y además podemos añadir o editar información:
Herramientas.listado.add('Tenazas'); // Esta línea dará error si lo tenemos declarado como const (que sería lo más normal)
Herramientas.listado.forEach(print);
// Vamos a llamar al metodo de forma estatica:
Herramientas.imprimirListado();
}
Constructor Factory¶
Un constructor factory retorna otro constructor y se suele utilizar para patrones singleton.
Ejemplo de uso:
class Rectangulo{
int base;
int altura;
int area;
String tipo; // Base = altura, es un cuadrado.
// El constructor factory retorna algo de tipo Rectangulo:
factory Rectangulo(int base, int altura){
if(base == altura){
return Rectangulo.cuadrado(base);
}else{
return Rectangulo.rectangulo(base, altura);
}
}
// Creamos un constructor con nombre para el cuadrado:
Rectangulo.cuadrado(int base){
this.base = base;
this.altura = base;
this.area = base * altura;
this.tipo = 'Cuadrado';
}
// Creamos un constructor con nombre para el rectangulo:
Rectangulo.rectangulo(int base, int altura){
this.base = base;
this.altura = altura;
this.area = base * altura;
this.tipo = 'Rectángulo';
}
}
main(List<String> args) {
// Creamos el objeto y le pasamos los valores:
final figura = new Rectangulo(10, 15);
// Si es igual nos mostrará cuadrado y sino rectangulo:
print(figura.tipo);
}
Patrón Singleton en Dart¶
El patrón Singleton es muy útil para crear clases que solo se instancian una vez.
class MiServicio{
// Creamos los atributos de forma privada que creará un objeto con el constructor privado:
static final MiServicio _singleton = new MiServicio._privado();
// Generamos un constructor de factory que retornará el objeto privado singleton:
factory MiServicio(){
return _singleton;
}
// En el patrón singleton utilizamos un constructor privado:
MiServicio._privado();
String url = 'https://abc';
String key = 'ABC123';
}
Herencia¶
Hay varias cosas interesantes que trae consigo Dart en referencia a la herencia.
Uso de extends¶
Para heredar de una clase padre utilizamos extends
en la clase hija:
// Creamos una clase:
class Vehiculo {
bool encendido = false;
void encender(){
encendido = true;
print('Vehiculo encendido');
}
void apagar(){
encendido = false;
print('Vehículo apagado');
}
}
// Ahora vamos a crear una clase que herede de Vehículo:
class Coupe extends Vehiculo{
int kilometraje = 0;
}
main() {
// Creamos un objeto:
final ford = new Coupe();
// Y comprobamos que tenemos todas los atributos y métodos de Vehiculo:
ford.encender();
ford.apagar();
}
Clase abstracta¶
Las clases abstractas se utilizan en programación para diseñar la estructura de una clase padre que servirá de referencia para las clases hijas
Ejemplo de uso:
// Definimos la clase abstracta y de esta no se puede crear objeto ya que es un cascarón de otra clase hija:
abstract class Vehiculo {
bool encendido = false;
void encender(){
encendido = true;
print('Vehiculo encendido');
}
void apagar(){
encendido = false;
print('Vehículo apagado');
}
// Creamos un método vacio que tendremos que inicializar en el hijo:
bool encenderMotor();
}
class Coupe extends Vehiculo{
int kilometraje = 0;
// Para implementar un metodo vacio del padre usamos el decorador override:
@override // Este decorador no es obligatorio en esta ocasión pero es una buena práctica cuando sobreescribimos un método ponerlo.
bool encenderMotor(){
print('Motor OK!');
return true;
}
}
main() {
final ford = new Coupe();
ford.encender();
ford.apagar();
}
Super constructor¶
El super constructor lo utilizamos para modificar la funcionalidad del constructor padre en el constructor hijo
class Persona{
String nombre;
int edad;
Persona(this.nombre, this.edad);
void imprimirNombre() => print('Nombre: $nombre, Edad: $edad');
}
// Ahora creamos una clase que hereda de Persona:
class Cliente extends Persona{
String direccion;
List ordenes = [];
// Como el padre tenemos un constructor que recibe parámetros tenemos que llamar un super constructor:
Cliente(int edadActual, String nombreActual):
super(nombreActual, edadActual); // Ahora dentro del constructor utilizamos el super constructor y le pasamos los parametros que recibe el constructor hijo
}
main() {
// Creamos un objeto con el hijo:
final pedro = new Cliente(32, 'Pedro');
pedro.imprimirNombre();
}
Sobreescribir métodos¶
Para sobreescribir la funcionalidad de un método en dart utilizamos el decorador @override
class Persona{
String nombre;
int edad;
Persona(this.nombre, this.edad);
void imprimirNombre() => print('Nombre: $nombre, Edad: $edad');
}
class Cliente extends Persona{
String direccion;
List ordenes = [];
Cliente(int edadActual, String nombreActual):
super(nombreActual, edadActual);
// Override lo usamos para sobreescribir metodos del padre:
@override
void imprimirNombre(){
super.imprimirNombre(); // Ponemos esta linea si queremos imprimir el metodo padre original.
print('Cliente: $nombre ($edad)');
}
}
main() {
final pedro = new Cliente(32, 'Pedro');
pedro.imprimirNombre();
}
Mixins¶
Los mixins son muy parecidos a los extends pero tienen unas características especiales
// Creamos un mixin que es similar a una clase abstracta pero no tiene constructor a diferencia de esta ni se puede instanciar.
mixin Logger{ // Un mixin sirve únicamente como una clase que sirve para que se herede de esta sus atributos y métodos:
void imprimir(String texto){
final hoy = DateTime.now();
print('$hoy :::: $texto');
}
}
// Creamos una clase que herede el mixin:
abstract class Astro with Logger {
String nombre;
Astro(){
imprimir('--- Init del Astro ---');
}
void existo(){
imprimir('-- Soy un ser celestial y existo --');
}
}
// Creamos una clase que herede de Astro:
class Asteroide extends Astro{
String nombre;
Asteroide(this.nombre){
imprimir('Soy $nombre');
}
}
main() {
final ceres = new Asteroide('Ceres');
}
Manejo de errores¶
El manejo de errores en Dart se hace con catchError
Ejemplo de uso:
main(){
Future<String> timeout = Future.delayed(Duration(seconds: 3), (){ // Este código se va a ejecutar 3 segundos desupés.
// provocamos un error con throw:
if(1 == 1){
throw 'Auxilio!, exploto el programa!!!';
}
return 'Retorno de dato';
});
// Utilizamos catchError para capturar cualquier error que suceda mientras se ejecuta el future:
timeout.then((texto)=> print(texto)).catchError((error)=> print(error));
}
Programación Reactiva: Streams¶
Un Stream es un flujo de datos, es similar a un observable que vigila el flujo de datos de manera constante enviando y recibiendo cambios en tiempo real.
Ejemplo de uso de Stream:
// Cargamos la librería async:
import 'dart:async';
main(){
// Creamos un objeto de tipo Stream:
final streamController = StreamController();
// Para utilizarlo le añadimos el listen() que es como un subscribe():
streamController.stream.listen((data){
print('Despegando! $data');
});
// Ahora vamos a agregarle información al stream:
streamController.sink.add('Apollo 11');
print('Fin del main');
}
Uso de onError, onDone y cancelOnError:
import 'dart:async';
main(){
final streamController = StreamController();
// Ajustamos el stream con una función lambda:
streamController.stream.listen(
(data) => print('Despegando! $data'),
onError: (err) => print('Error! $err'), //Manejamos un error cuando este suceda.
onDone: () => print('Mision cumplida'), // Esta opción se desplegará una vez se finalice el stream sin errores
cancelOnError: true, // Si tenemos en true esta opción se cancelará el programa y apollo 12 no despegará.
);
streamController.sink.add('Apollo 11');
// Podemos lanar un error con los stream:
streamController.sink.addError('Houston, Tenemos un problema');
// Si el error esta manejado con onError podremos seguir con el flujo de datos:
streamController.sink.add('Apollo 12');
print('Fin del main');
}
Tipados y Broadcast para transmisiones paralelas:
import 'dart:async';
main(){
// Podemos asignar el tipado para que devuelva un tipo de dato estrictamente:
final streamController = new StreamController<String>.broadcast(); // El broadcast permite multiples subscripciones
streamController.stream.listen(
(data) => print('Despegando! $data'),
onError: (err) => print('Error! $err'),
onDone: () => print('Mision cumplida'),
cancelOnError: true
);
// gracias al broadcast podemos volver a generar otro stream similar:
streamController.stream.listen(
(data) => print('Vamos! $data'),
onError: (err) => print('Fallo! $err'),
onDone: () => print('Funcionó'),
cancelOnError: true
);
streamController.sink.add('Apollo 11');
streamController.sink.addError('Houston, Tenemos un problema');
streamController.sink.add('Apollo 12');
print('Fin del main');
}