Blog>

Manuel Hernández-Ros
21 Sep 2022

¡Introducción a Dart! El lenguaje de programación de Flutter

Tiempo de lectura 11 minutos
  • dart
  • flutter
  • movilidad
  • programación

Sumario En esta entrada explicaremos los conceptos más básicos y necesarios para poder comenzar a desarrollar con Dart

Si no has leído la entrada sobre Flutter te recomendamos que le eches un vistazo para que entiendas un poco más el contexto de este artículo y sobre el lenguaje de programación Dart.

Introducción

En este artículo lo que vamos a explicar es como programar en Dart, el lenguaje que se utiliza en Flutter. Vamos a mostrar todo lo básico para poder entender cómo funciona y así poder entender y crear aplicaciones sin problema.

Para que puedas probar a la vez te recomiendo acceder a https://dartpad.dev/. Una web que permite escribir en Dart y probar el código en vivo. Así puedes probar todo lo que vamos a explicar a continuación, además de poder ver el resultado o los errores que se van teniendo.

Variables

Como cualquier otro lenguaje de programación Dart tiene variables, y qué mejor forma de empezar una introducción a Dart que empezando con los tipos básicos de las variables.

Tipos básicos:

Ejemplo:

String name     = "manuel";
  String lastname = 'Hernanez-Ros';
  String strCompound = '$name _ $lastname';
  
  int age = 24; 
  
  double heigth = 1.71; 
  
  bool isDeveloper = true; 

  List<String> favouriteFoods = ['Pizza', 'Lemon Pie', 'Red Velvet Pie']; 
  
  Map<String, bool> countriesVisited = {
    'Italy' : true, 
    'France' : true,
    'EEUU' : false,
  }; 
  
  // ejemplo de dynamic
  Map<String, dynamic> dynamicExample = {
    'int' : 1, 
    'String' : '2', 
    'bool': false,
  };

Declaración de variables y Null safety.

A partir de la versión 2 de Flutter se incorporó Null Safety. Esto lo que nos permite es especificar si una variable va a poder tener un valor nulo o no con una interrogación en la declaración de la variable ‘?’.

Por ejemplo: la variable String name siempre va a deber tener un valor, y si intentamos ponerle un null o no la inicializamos el compilador va a fallar, indicando que no puede tener valor null esa variable. Sin embargo, si la inicializamos como String? Name, sí que puede tener valores null.

A la hora de declarar variables podemos decir que una variable sea estática, especificar directamente el tipo de la variable o no, etc. Por lo que las posibilidades que tenemos son las siguientes:

Ejemplo:

class Example{
	
  final String name = 'name'; // atributo de clase
  
  void main() {  
    var name = 'Manuel';

    String lastName = 'Hernandez Ros';

    int? age; // ahora está a null

    if(age == null) {
      age = 24; 
    }

    late String color; 

    color = 'yellow'; 

    const double height = 1.71;
  }
}

Funciones

A la hora de definir una función, primero tenemos que definir si va a retornar algún valor, y luego el nombre de la función con sus parámetros.

Por otro lado, los parámetros de las funciones se pueden definir de diferentes maneras:

Ejemplo:

class Person{
  
  void _private(String text) => print(text);
  
  setName(String name){}
  
  setFullName(String name, String lastName, [String secondName = 'none']){}
    
  setFullName_2({required String name, required String lastName, String? secondName}){}
  
  void main(){
    setName('name');
    
    setFullName('name', 'lastName'); // second name opcional
    
    setFullName_2(
      name: 'name', 
      secondName: 'secondName', 
      lastName: 'lastName'); 
    // Como son nombrados, pueden tener un orden diferente
  }
}

Clases

A la hora de definir una clase es prácticamente igual a otros lenguajes de programación. Con la palabra clave class y seguido del nombre de esa clase. Donde nos empezamos a encontrar algunas diferencias es en los constructores de esa clase y los getter y setter. Además, todo lo comentado de los parámetros de las funciones anteriormente se le pueden aplicar a los constructores.

Ejemplo:

class Person{
  String name; 
  String lastName; 
  int age; 
  
  String? secondName; 
  
  String get fullName {
    return '$name $secondName $lastName';
  }
  
  set fullName(String fullName) { // falta la comprobación de secondName
    List<String> splittedName = fullName.split(' ');
    name = splittedName[0];
    lastName = splittedName[1];
  }
  
  Person({
    required this.name, 
    required this.lastName, 
    required this.age, 
    this.secondName
  });
  
  Person.fromMap(Map<String, dynamic> map):
    this.name = map["name"],
    this.lastName = map["lastName"],
    this.age = map["age"],
    this.secondName = map["secondName"] ?? 'none';
}


void main(){  
  Person manuel = Person(
    name: "Manuel",
    lastName: "Hernandez-Ros",
    age: 24,
  );
  
  Map<String, dynamic> map = {
     "name": "Manuel",
     "lastName": "Hernandez-Ros",
     "age": 24,
  };
  
  manuel = Person.fromMap(map);
}

Clases abstractas

El uso de clases abstractas es una estrategia común en la programación orientada a objetos. Una clase abstracta es una clase que no se puede instanciar, sin embargo, puede tener subclases, de forma que sirve para dar estructura e implementación a sus subclases.

Esto lo que nos permite es definir una serie de atributos o funciones que son necesarios para un conjunto de elementos que se pueden parecer, así como una interfaz. En Dart para definir una clase abstracta se tiene que definir con la palabra clave abstract.

Para que una clase tenga los atributos y funciones de una clase abstracta tiene que extender de ella. Después del nombre de la clase se añade la palabra clave extends y en ese momento ya extiende de ella.

Mixin

Por otro lado, contamos con otra posibilidad que es el mixin. Este es un concepto más complicado así que vamos a utilizar la siguiente imagen para explicarlo.

Contamos con que las clases Animal, Mammal, Bird y Fish son clases abstractas, y estas tres últimas respectivamente extienden de Animal. Luego por otro lado tenemos las acciones que pueden realizar cada uno de los animales de abajo, y que además se comparten, aunque no extiendan de lo mismo. Aquí es donde entra el Mixin, lo que nos permite es dar estas “acciones” a los animales pero que no tengan que ver con las clases de encima. Podemos ver todo lo anterior. Puedes ver esto también explicado en el siguiente artículo: https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3

abstract class Animal {
  
   String voice = ""; 

   void talk() {
     print(voice);
   } 

}

// todas las siguientes clases implementan los metodos y tienen el atributo voice.

abstract class Mammal extends Animal {}
abstract class Fish extends Animal {}
abstract class Bird extends Animal {}

abstract class Walk {}
abstract class Swim {}
abstract class Fly {}

abstract class Dolphin extends Mammal with Swim {}
abstract class Bat extends Mammal with Fly, Walk {}
abstract class Cat extends Mammal with Walk {}

abstract class Dove extends Fish with Walk, Fly {}
abstract class Duck extends Fish with Walk, Fly, Swim {}

abstract class Shark extends Fish with Swim {}
abstract class FlyingFish extends Fish with Swim, Fly{}


void main(){
  
  Duck duck = Duck(); 
  
  duck.talk();
}

Asíncronía

Muchas veces programando, nos encontramos con una función que puede tardar mucho en ejecutarse, o es una llamada a otro servicio que no depende de nosotros y no sabemos cuánto puede tardar en recoger la respuesta (incluso una llamada a una base de datos o al sistema local de ficheros puede tardar en cargar). Para ello, existen las funciones asíncronas, funciones que se ejecutan en otro hilo, sin bloquear el hilo principal, y que se recibirá la respuesta cuando el método haya acabado. En la siguiente imagen puedes ver un ejemplo:

Podemos ver en la imagen que la tarea 1 la ejecuta sin problema, sin embargo, la tarea 2 es una tarea asíncrona, por lo que la pone en espera y mientras se ejecutan otras tareas como la 3. Y ya cuando la espera ha acabado, vuelve a poner en ejecución la tarea 2 que se había quedado pendiente. En dart, estas funciones se implementan con Futures, Awaits, y Async:

Ejemplo:

void main() async { // obligatorio el async porque contiene un await y puede hacer esperar. 
  
  String name = await getName(); 
  
  getName().then((name) => print(name)); // then del future. 
}


Future<String> getName() async { // obligatorio que devuelva future
  return "name"; 
}

¡Esperemos que con esto entiendas un poco más cómo funciona Dart y los aspectos más importantes!

Igualmente, en futuros artículos sobre Flutter vamos a trabajar mucho sobre Dart, por lo que vas a ver muchos ejemplos y vas a trabajar mucho con él en caso de que quieras hacer tus aplicaciones. Al final te acabarás acostumbrando 😉

Autor

Manuel Hernández-Ros
Manuel Hernández-Ros

Desarrollador en dev&del

Desarrollador full stack y apasionado del desarrollo móvil.

Le encantan los videojuegos. Si no le ves programando seguro que está jugando.

¿Estás interesado?

Déjanos tus datos y contactaremos contigo lo antes posible