dart-poo-clases-constructores

POO en Dart: clases, constructores y privacidad

  • 4 min

La programación orientada a objetos es una forma de organizar el código alrededor de clases y objetos.

Llegamos a uno de los pilares fundamentales. Dart es un lenguaje Orientado a Objetos.

¿Y qué significa esto en la práctica? Que casi todo lo que toques en Dart es un objeto. Y cuando demos el salto a Flutter, verás que cada componente visual (un botón, un texto, una lista) es una Clase.

Si dominas las clases en Dart, entender cómo se construye una interfaz en Flutter será pan comido. Si no, te parecerá un laberinto de paréntesis. Vamos a aclararlo.

Clases y objetos

El concepto es el clásico de la programación:

  • Clase: Es el plano, la plantilla o el molde. Define qué características tendrá el objeto.
  • Objeto: Es la instancia real creada a partir de ese molde. Es la casa construida usando el plano.

Definición básica

Para definir una clase usamos la palabra reservada class. Por convención, los nombres de las clases usan PascalCase (Primera letra mayúscula).

class Robot {
  // Propiedades (Atributos)
  String nombre;
  double bateria;

  // Constructor (Ya lo explicamos abajo)
  Robot(this.nombre, this.bateria);

  // Métodos (Funciones dentro de la clase)
  void saludar() {
    print("Hola, soy $nombre y tengo $bateria% de batería.");
  }
}
Copied!

Para usarla (instanciarla), simplemente llamamos al constructor. Nota que en Dart moderno la palabra new es opcional y habitualmente no se usa.

void main() {
  var r2d2 = Robot("R2D2", 100.0); // Creamos el objeto
  r2d2.saludar();
}
Copied!

Constructores: la forma Dart

Aquí es donde Dart se diferencia de Java o C# y se vuelve mucho más eficiente.

El problema de la verbosidad

En otros lenguajes, un constructor típico se ve así (mucha repetición):

// Forma explícita, válida pero repetitiva
class Robot {
  String nombre;

  Robot(String nombre) : nombre = nombre;
}
Copied!

La solución: constructores simplificados

Dart nos permite inyectar el valor del parámetro directamente en la propiedad usando this..

// ✅ Forma Dart (Syntactic Sugar)
class Robot {
  String nombre;
  
  // Asigna directamente el argumento a la propiedad
  Robot(this.nombre); 
}
Copied!

Constructores con parámetros nombrados

Los parámetros nombrados del artículo anterior se usan muchísimo en las clases. De hecho, casi todos los widgets de Flutter usan este tipo de constructor.

class Boton {
  String texto;
  String color;

  // Constructor con parámetros nombrados
  // Usamos 'required' porque las propiedades no pueden ser nulas
  Boton({required this.texto, this.color = "Azul"});
}

void main() {
  // ¡Mira qué legible!
  var miBoton = Boton(texto: "Aceptar", color: "Verde");
}
Copied!

Constructores con nombre

Dart no soporta “sobrecarga de métodos” (tener dos funciones con el mismo nombre pero distintos parámetros) de la forma tradicional.

Para solucionar esto y permitir crear objetos de diferentes formas, existen los Constructores con Nombre.

La sintaxis es Clase.nombreConstructor().

class Robot {
  String nombre;
  double bateria;

  // Constructor principal
  Robot(this.nombre, this.bateria);

  // Constructor con Nombre: "origen"
  // Crea un robot básico con un nombre y batería al 100%
  Robot.origen(this.nombre) : bateria = 100.0;
  
  // Constructor con Nombre: "desconocido"
  Robot.desconocido() 
      : nombre = "Desconocido", 
        bateria = 0;
}

void main() {
  var robot1 = Robot("C3PO", 50);
  var robot2 = Robot.origen("BB8"); // Usa el constructor nombrado
}
Copied!

El constructor más famoso que usarás es .fromJson(). Se utiliza para crear objetos a partir de datos recibidos de internet (APIs). Lo veremos en el módulo de conexión a datos.

Encapsulamiento: propiedades privadas

En Dart no existen las palabras clave public, private o protected. La visibilidad se controla a nivel de librería (generalmente, a nivel de archivo).

Para hacer que una propiedad o método sea privado, simplemente comienza su nombre con un guion bajo _.

class CuentaBancaria {
  // Propiedad pública
  String titular;
  
  // Propiedad privada (solo accesible dentro de este archivo)
  double _saldo; 

  CuentaBancaria(this.titular, this._saldo);

  // Método público para acceder de forma controlada
  void verSaldo() {
    print("El saldo es $_saldo");
  }
}
Copied!

Si intentas acceder a _saldo desde otro archivo (main.dart), Dart te dirá que no existe.

Getters y setters

Si tenemos una propiedad privada, ¿cómo la leemos o modificamos desde fuera? Usando get y set.

class Cuadrado {
  double _lado;

  Cuadrado(this._lado);

  // Getter: Permite leer una propiedad calculada o privada
  double get area => _lado * _lado;

  // Setter: Permite validar datos al asignar
  set lado(double valor) {
    if (valor < 0) throw 'El lado no puede ser negativo';
    _lado = valor;
  }
}

void main() {
  var c = Cuadrado(5);
  print(c.area); // 25 (Se usa como propiedad, sin paréntesis)
  
  c.lado = 10;   // Usamos el setter
  // c.lado = -5; // Lanzaría nuestra excepción
}
Copied!