Flutter DART Clases

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introducción

  • Como comenté anteriormente este curso está pensado para alumnos que ya tienen conocimientos previos en algún lenguaje de programación orientado a objetos.
En esta sección se va a explicar las diferencias y características con respecto a otros lenguajes orientados a objetos en lo que se refiere al uso de clases y objetos,


  • En Dart no es necesario realizar el new cuando instanciamos una clase.
Esto se emplea mucho en Flutter ya que todo en él se basa en crear widgets (Clases) y por tanto aparecería de forma continua new´s por todos lados.
Sin embargo, si vamos a emplear clases creadas por nosotros, se recomienda hacer uso de new para diferenciarlo de la llamada a una función.


Veamos un ejemplo, con conceptos de otros lenguajes de programación orientado a objetos como el empleo del método toString:
class Vehiculo{
  
  int? anoCompra; // Necesitamos indicar que admite valores nulos, ya que al instanciar no le damos un valor por defecto
  String? matricula;
  String? marca;
  
  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra'; 
}
void main() {

  final veh1 = new Vehiculo();
  veh1.marca='Opel';
  veh1.matricula='1234GHZ';
  veh1.anoCompra = 2018;
  
  print(veh1);
  
  final veh2 = Vehiculo(); // No necesitamos new, pero se recomienda si son clases nuestras
  veh2.marca='Peugot';
  veh2.matricula='6543HZZ';
  veh2.anoCompra = 2020;
  
  print(veh2);
  
}



Constructores

  • Funcionan igual que en el lenguaje Java.
Veamos un ejemplo de uso:


class Vehiculo{
  
  int? anoCompra; // Necesitamos indicar que admite valores nulos, ya que al instanciar no le damos un valor por defecto
  String? matricula;
  String? marca;
  
  Vehiculo(int anoCompra, String matricula, String marca){
    this.anoCompra = anoCompra;
    this.marca = marca;
    this.matricula = matricula;
  }
  
  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra'; 
}
void main() {

  final veh1 = new Vehiculo(2018,'Opel','1234GHZ');  
  print(veh1);
  
  final veh2 = Vehiculo(2020,'Peugot','6543HZZ'); 
  print(veh2);
  
}
Fijarse que un constructor no es más que un método y por tanto podemos emplear todo lo aprendido con los parámetros (opciones, posicionales,...)
Ejercicio propuesto: Modifica el ejercicio anterior y haz con los parámetros puedan ser enviados en cualquier orden especificando el nombre del parámetro en la llamada. Haz que todos ellos sean obligatorios.



  • En el ejemplo anterior vimos que al definir las propiedades de la clase, éstas deberían admitir valores nulos, ya que cuando instanciamos no les damos unos valores iniciales.
Para solucionarlo.
class Vehiculo{
  
  int anoCompra; 
  String matricula;
  String marca;
  
  Vehiculo(this.anoCompra,this.marca, this.matricula); // Esto hace que automaticamente asigne los valores enviados a las propiedades y obliga a instanciar con los 3 parámetros.
  
  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra'; 
  
}
void main() {

  final veh1 = new Vehiculo(2018,'Opel','1234GHZ');  
  print(veh1);
  
  final veh2 = Vehiculo(2020,'Peugot','6543HZZ'); 
  print(veh2);
  
  
}


  • Si quisiéramos emplear el paso de parámetros empleando sus nombres, debemos de hacer uso de las { }, pero haciendo obligatorios el paso de datos con la palabra reservada required como ya vimos anteriormente.
class Vehiculo{
  
  int anoCompra; 
  String matricula;
  String marca;
  
  Vehiculo({
      required this.anoCompra,
      required this.marca, 
      required this.matricula
    }); 
  
  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra'; 
  
}
void main() {

  final veh1 = new Vehiculo(anoCompra: 2018 ,marca: 'Opel',matricula: '1234GHZ');  
  print(veh1);
  
  final veh2 = Vehiculo(anoCompra: 2020,marca: 'Peugot',matricula: '6543HZZ'); 
  print(veh2);
  
  
}


NOTAS IMPORTANTES:

  • En DART los constructores no se heredan.
  • Para llamar a un constructor que esté definido en una clase superior, debemos emplear la palabra clave super. Ejemplos de uso en este enlace.



Mútliples constructores. Constructores con nombre

  • En DART no existe la sobrecarga de constructores y por tanto no podemos definir múltiples constructores en base a diferente tipo o número de parámetros.
Para solucionarlo, DART permite crear múltiples constructores con diferentes nombres de la forma: nombre_clase.nombre_constructor(parámetros).
class Vehiculo{
  
  int anoCompra; 
  String matricula;
  String marca;
  
  Vehiculo({required this.anoCompra,required this.marca, required this.matricula}); 
  
  Vehiculo.sinAnho({required this.marca, required this.matricula}) : anoCompra = 2000;

  Vehiculo.sinAnhoMarca({required this.matricula}) : anoCompra = 2000, marca='Opel';
  
  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra'; 
  
}
void main() {

  final veh1 = new Vehiculo(anoCompra: 2018,marca: 'Opel',matricula: '1234GHZ');  
  print(veh1);
  
  final veh2 = new Vehiculo.sinAnho(marca: 'Opel',matricula: '2234GHZ');  
  print(veh2);
  
  final veh3 = new Vehiculo.sinAnhoMarca(matricula: '3234GHZ');  
  print(veh3);
  
}
  • Nota importante: Fijarse que estoy haciendo uso de otro constructor en el que indico los valores que van a guardar los atributos de la clase Coche. Como no envío el año de compra ni la marca, tendría que indicar en la definición del atributo, que dichos atributos puede recibir valores nulos o que tienen un valor por defecto.
Pero he empleado otra característica de DART que consiste en poner, después de la definición de los parámetros, dos puntos seguido de las variables que queremos inicializar. Este código se ejecuta antes del constructor. Es lo que se conoce como inicializadores.
Más información: https://dart.dev/guides/language/language-tour#constructors



Constructores Factory

  • Normalmente este tipo de constructores se suelen emplear cuando no queramos instanciar un objeto de la clase, sino que recuperamos una instancia previamente guardada en alguna lista (por ejemplo) en base al parámetro que recibe el constructor (podéis consultar el ejemplo del enlace anterior).
Siempre deben devolver una instancia de la clase (debemos de hacer el return).
Se crean anteponiendo la palabra reservada factory antes del nombre del constructor.
Otro uso que se les suele dar es cuando queremos llamar a otro constructor dentro del código de un constructor.
Suele usarse para leer archivos JSON, en los que creamos un constructor de nombre 'NombreClase.fromJson' al que se le pasa un Mapa con los datos de la clase en el archivo Json, y se devuelve un objeto de la clase con los datos de dicho archivo.
Veamos un ejemplo:
class Vehiculo{
  
  int anoCompra; 
  String matricula;
  String marca;
  
  Vehiculo({required this.anoCompra,required this.marca, required this.matricula}); 
  
  factory Vehiculo.sinAnho({required marca, required matricula}) =>
    Vehiculo(anoCompra: 2020,marca: marca, matricula: matricula );

  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra'; 
  
}
void main() {

  final veh2 = new Vehiculo.sinAnho(marca: 'Opel',matricula: '2234GHZ');  
  print(veh2);
  
  
}




Empleando Mapas

  • En esta sección voy a explicar como, teniendo como fuente de datos un mapa, crear un objeto.
Partimos de un mapa que va a guardar la información de un Coche.
Normalmente los datos que tengan como fuente de datos Internet vendrán en formato JSON:



class Vehiculo{
  
  int anoCompra; 
  String matricula;
  String marca;
  
  Vehiculo({
      required this.anoCompra,
      required this.marca, 
      required this.matricula
    }); 
 
  Vehiculo.fromMap(Map<String,String>dato) :
    this.anoCompra = int.parse(dato['anoCompra']!),
    this.matricula = dato['matricula']!,
    this.marca = dato['marca']!;
  
  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra'; 
  
 
}
void main() {

  final vehiculoJson = {
    'anoCompra' : '2020',
    'matricula' : '1234GHZ',
    'marca' : 'Opel'
  };
  
  Vehiculo veh1 = Vehiculo.fromMap(vehiculoJson);
  print(veh1);
  
}
Fijarse que los datos del mapa que son asignados a los atributos llevan una admiración, indicando a DART que no debe verificar que puedan ser nulos. Tendremos que estar seguros que nunca va a recibir un valor nulo en el mapa, ya que sino, daría una excepción.
Otra forma de evitarlo es emplear un mapa de tipo Map<String,dynamic>dato


  • Podéis emplear este sitio Web para que os genere el código automáticamente a partir de un Json dado.


  • Cabe indicar que podríamos emplear un constructor Factory como vimos en el punto anterior:
class Vehiculo{

  int anoCompra;
  String matricula;
  String marca;

  Vehiculo({
    required this.anoCompra,
    required this.marca,
    required this.matricula
  });

  factory Vehiculo.fromJson(Map<String,String>dato) {
    return Vehiculo(anoCompra: int.parse(dato['anoCompra']!),
                    matricula:dato['matricula']!,
                    marca:dato['marca']!
                   );
  }

  @override
  String toString() => 'El vehículo de la marca $marca con matrícula $matricula se compró en el año $anoCompra';


}

void main() {
  final vehiculoJson = {
    'anoCompra' : '2020',
    'matricula' : '1234GHZ',
    'marca' : 'Opel'
  };

  Vehiculo veh1 = Vehiculo.fromJson(vehiculoJson);
  print(veh1);
}



Herencia

  • La palabra clave para indicar que una clase deriva de otra es extends y para referenciar a un método o propiedad de la clase padre expleamos la palabra clave super.
Veamos un ejemplo de uso:
class Personal {
  String nombreCompleto;

  Personal(this.nombreCompleto);

}
class Alumno extends Personal{
  String numCarnet;

  Alumno({required this.numCarnet, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Alumna/o:$nombreCompleto con número de carnet:$numCarnet';
}

class Profesor extends Personal{
  String nrp;

  Profesor({required this.nrp, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Profesor/a:$nombreCompleto con número de registro personal:$nrp';
}
void main(){

  final alum1 = new Alumno(numCarnet: 'C1A',nombreCompleto: 'Juan Luis Rodriguez');
  final prof1 = new Profesor(nrp: '1234534',nombreCompleto: 'Luisa Sanchez Dragó');

  print(alum1);
  print(prof1);

}



Overriding

  • Tiene el mismo concepto que en otros lenguajes de programación orientado a objetos.
De hecho ya lo estamos haciendo de cierta manera con el método toString.
Veamos un ejemplo en el que defino el método imprimirDatos en la superclase y es heradado por la subclases.
Una de ellas (Alumno) lo sobreescribe y llama al método que está definido en la clase padre, Personal.
class Personal {
  String nombreCompleto;

  Personal(this.nombreCompleto);
  
  void imprimirDatos(){
    print('Nombre completo:$nombreCompleto');
  }

}
class Alumno extends Personal{
  String numCarnet;

  Alumno({required this.numCarnet, required String nombreCompleto}) : super(nombreCompleto);

  @override
  void imprimirDatos(){
    super.imprimirDatos();
    print('Carnet:$numCarnet');
  }
  
  @override
  String toString() => 'Alumna/o:$nombreCompleto con número de carnet:$numCarnet';
}

class Profesor extends Personal{
  String nrp;

  Profesor({required this.nrp, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Profesor/a:$nombreCompleto con número de registro personal:$nrp';
}
void main(){

  final alum1 = new Alumno(numCarnet: 'C1A',nombreCompleto: 'Juan Luis Rodriguez');
  final prof1 = new Profesor(nrp: '1234534',nombreCompleto: 'Luisa Sanchez Dragó');

  print(alum1);
  print(prof1);
  
  alum1.imprimirDatos();
  prof1.imprimirDatos();

}


Librerías. Privado/Público

  • En DART cada archivo .dart puede estar formado por múltiples clases y conformar una librería y podemos agrupar un conjunto de librerías y herramientas en forma de paquetes.
Más información sobre librerías y paquetes: https://dart.dev/guides/libraries/create-library-packages
  • Para poder hacer uso de las clases que se encuentran definiadas en una librería tenemos que hacer uso de la palabra clave import al principio del archivo.


  • Este ejemplo será necesario realizarlo en el Android Studio (no vale vía Web):
Supongamos que tenemos un archivo de nombre Centro.dart con un conjunto de clases definidas:
class Personal {
  String nombreCompleto;

  Personal(this.nombreCompleto);

}
class Alumno extends Personal{
  String numCarnet;

  Alumno({required this.numCarnet, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Alumna/o:$nombreCompleto con número de carnet:$numCarnet';
}

class Profesor extends Personal{
  String nrp;

  Profesor({required this.nrp, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Profesor/a:$nombreCompleto con número de registro personal:$nrp';
}
Y ahora queremos hacer uso de estas clases desde otro archivo DART.
Para ello empleamos la orden import:
import 'Centro.dart';

void main(){

  final alum1 = new Alumno(numCarnet: 'C1A',nombreCompleto: 'Juan Luis Rodriguez');
  final prof1 = new Profesor(nrp: '1234534',nombreCompleto: 'Luisa Sanchez Dragó');

  print(alum1);
  print(prof1);

}



  • En Dart existen una serie de librerías ya creadas y que podemos usar en cualquier momento, haciendo el import correspondiente.
Por ejemplo, la librería math tiene funciones y clases que nos pueden ser útiles a nivel matemático.
import 'dart:math';

void main() {
  
  final semilla = Random();
  for(int cont=0; cont<10;cont++){
    print('Aleatorio generado:${semilla.nextInt(50)}');
  }

}
  • Podemos establecer un 'alias' a la librería con la palabra clave as al hacer el import de la forma:
import 'dart:math' as math;

void main() {
  
  final semilla = math.Random();
  for(int cont=0; cont<10;cont++){
    print('Aleatorio generado:${semilla.nextInt(50)}');
  }

}



Solamente tenemos la posibilidad de hacer que una propiedad, método o clase sea privada anteponiendo un guión bajo a su nombre.
De esta forma, dicha clase, método o propiedad sólo será visible para todo lo que se encuentra dentro del archivo DART.
  • Siguendo el ejemplo anterior, yo no quiero que se pueda instanciar un objeto de la clase Personal.
Para ello puedo definir la clase Personal como privada de la forma (fijarse en los guiones bajos):
class _Personal {
  String nombreCompleto;

  _Personal(this.nombreCompleto);

}
class Alumno extends _Personal{
  String numCarnet;

  Alumno({required this.numCarnet, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Alumna/o:$nombreCompleto con número de carnet:$numCarnet';
}

class Profesor extends _Personal{
  String nrp;

  Profesor({required this.nrp, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Profesor/a:$nombreCompleto con número de registro personal:$nrp';
}
Ahora, desde el archivo DART donde hacemos uso de estas clases, ya no me estará permitido crear una instancia de la clase Personal:
Flutter dart private 1.JPG


  • Y el mismo concepto podría ser aplicado tanto a métodos como a propiedades.
Indicar que en el ejemplo anterior ya no me deja instancar la clase _Personal desde otros archivos DART que hagan uso de la importación de estas clases, pero sí me dejaría hacerlo dentro del mismo archivo.
Recordar que si no queremos permitir que se instancie una clase debemos de definirla como abstracta (lo veremos a continuación).



Getters y Setters

  • Los getters y setters son unos métodos especiales que se llaman sin emplear paréntesis y aparentan ser propiedades de una clase, ya que se llaman de la misma forma: objeto.método (pero sin paréntesis).
Por lo tanto, a nivel de programación van a aparentar que estamos obteniendo o asignando un valor a una propiedad, pero realmente dicha propiedad no existe.
  • Si empleamos el método para obtener información (es decir, el método tiene un return) se denomina getter.
  • Si empleamos el método para asignar un valor a una propiedad que sí exista se denomina setter.


Este punto está relacionado con lo visto anteriormente sobre public/private.
Normalmente los métodos getters y setters se aplican a propiedades definidas como privadas. Empleando estos métodos no accedemos directamente a las propiedades y esto nos va a permitir:
  • Definir propiedades de 'sólo lectura' en la que solamente definimos el método getter.
  • Controlar los valores que se asignan a propiedades para que cumplan unos criterios determinados.
  • Definir propiedades 'ficticias' que realmente no existen, sino que se calculan en base a datos que ya tenemos guardados en la clase. Por ejemplo, si tenemos guardada la propiedad fechaNacimiento, puede crear una propiedad edad que sea igual a la resta de la fecha actual menos la fecha de nacimiento, y que realice dicho cálculo cada vez que acceda a la propiedad.
  • Veamos un ejemplo.
Modificamos el ejemplo anterior y vamos a crear una nueva propiedad en la clase Profesor del ejemplo anterior.
Vamos a añadir una propiedad sueldo de tipo double, pero voy a hacerla privada y voy a crear un método getter y setter para acceder y modificar dicha propiedad.
void main() {

  final prof1 = new Profesor(nrp: '1234534',nombreCompleto: 'Luisa Sanchez Dragó');

  print(prof1);
  print('Sueldo:${prof1.sueldo}'); // Podemos acceder a _sueldo porque estamos en el mismo fichero
  prof1.sueldo = 100.34;  // Llama al método set
  print('Nuevo Sueldo:${prof1.sueldo}'); 
  
  prof1.sueldo = -50;  // El assert lanzará una excepción
  print('Nuevo Sueldo:${prof1.sueldo}'); 
}

class _Personal {
  String nombreCompleto;

  _Personal(this.nombreCompleto);

}

class Profesor extends _Personal{
  String nrp;
  double _sueldo=0;  // Necesitamos que tenga un valor por defecto ya que sino el parámetro tendría que poder guardar valores nulos
  
  double get sueldo => _sueldo;
  set sueldo(double nuevoSueldo){
    assert(nuevoSueldo <= 0,'El sueldo debe ser mayor que 0');  // Lanza una excepción si cumple la condición pero sólo en Debugger. En Web no tiene efecto
    _sueldo = nuevoSueldo <= 0 ? 0 : nuevoSueldo;  
  }
  
  Profesor({required this.nrp, required String nombreCompleto}) : super(nombreCompleto);

  @override
  String toString() => 'Profesor/a:$nombreCompleto con número de registro personal:$nrp';
}



  • Si intentamos aplicar el concepto de propiedad privada a los atributos que conforman el constructor, el código se complica un poco debido a los valores nulos.
Veamos un ejemplo con el número de carnet del alumno.
void main() {

  final alum1 = new Alumno(num: 'C1A',nombreCompleto: 'Juan Luis Rodriguez');

  print(alum1);

}

class _Personal {
  String nombreCompleto;

  _Personal(this.nombreCompleto);

}
class Alumno extends _Personal{
  String _numCarnet;
  
  String get numCarnet => _numCarnet;
  set numCarnet(String numero) { numero.isNotEmpty ? _numCarnet = numero : _numCarnet = '1'; } // Aquí le damos un valor por defecto pero podríamos tener otra lógica diferente

  Alumno({required String num, required String nombreCompleto}) : 
       _numCarnet = num,super(nombreCompleto) {
    numCarnet = num;  // Esto se ejecuta después de asignar el valor a _numCarnet. Lo llamo por si queremos que pase algún tipo de filtro por el valor enviado.     
  }

  @override
  String toString() => 'Alumna/o:$nombreCompleto con número de carnet:$numCarnet';
}




Clases abstractas / Interfaces


Una clase abstracta es una clase que no se puede instanciar y que se define con la palabra clave abstract.
Podemos definir métodos con código o sin código (sólo la definición), cuya implentación tiene que ser realizada en la clase que vaya a heredar de la clase abstracta.
  • Una diferencia con respecto a Java y otros lenguajes es que en DART todas las clases, implícitamente, definen una interface y por tanto, una clase abstracta (o clase normal) también puede ser empleada como interface, teniendo en cuenta que una interface está pensada para sólo tener definidas constantes o métodos sin código (sólo la definición).
void main() {

  final alum1 = new Alumno(numCarnet: 'C1A',nombreCompleto: 'Juan Luis Rodriguez');
  final prof1 = new Profesor(nrp: '1234534',nombreCompleto: 'Luisa Sanchez Dragó');

  print(alum1);
  alum1.pagarCafeteria();
  print(prof1);
  prof1.pagarCafeteria();
  
  // Personal per1 = new Personal('Nombre Completo a enviar'); // ERROR. No se puede instanciar una clase abstracta
  
}

abstract class Personal {
  String nombreCompleto;

  Personal(this.nombreCompleto);
  
  void pagarCafeteria();

}
class Alumno extends Personal{
  String numCarnet;

  Alumno({required this.numCarnet, required String nombreCompleto}) : super(nombreCompleto);

  void pagarCafeteria(){
    print('El alumno $numCarnet paga en efectivo');
  }
  
  @override
  String toString() => 'Alumna/o:$nombreCompleto con número de carnet:$numCarnet';
}

class Profesor extends Personal{
  String nrp;

  Profesor({required this.nrp, required String nombreCompleto}) : super(nombreCompleto);

  void pagarCafeteria(){
    print('El profesor $nrp paga con vale descuento');
  }

  @override
  String toString() => 'Profesor/a:$nombreCompleto con número de registro personal:$nrp';
}
Flutter dart abstract 1.JPG



  • Veamos ahora un caso de uso de interface.
void main() {
  
  Cuadrado cuadrado1 = new Cuadrado(lado: 10);
  Circulo circulo1 = new Circulo(5);

  print('Área del cuadrado: ${cuadrado1.getArea()}');
  print('Área del círculo: ${circulo1.getArea()}');
  
}

abstract class Figura{
  
  static const PI = 3.1416;
  
  double getArea();
  
}

class Cuadrado implements Figura{
  
  double _lado;
  
  Cuadrado({required double lado}) : this._lado = lado;

  double getArea() => _lado*_lado;
  
}
class Circulo implements Figura{
  
  double _radio;
  
  Circulo(this._radio);

  double getArea() => Figura.PI*_radio*_radio;
  
}



  • Recordar que el uso de las interfaces me va a permitir: Utilizar un mecanismo (el de la interface) que nos va a permitir relacionar a clases diferentes. Es decir, todas las clases que implementen una interface van a tener que implementar los mismos métodos (los definidos en la interface) y por lo tanto, podríamos hacer uso del polimorfismo y llamar a los métodos utilizando como referencia a objetos que implementen la interface aunque pertenezcan a clases diferentes.
Veamos una modificación al ejercicio anterior:
void main() {
  
  Cuadrado cuadrado1 = new Cuadrado(lado: 10);
  Circulo circulo1 = new Circulo(5);

  imprimirArea(cuadrado1);
  imprimirArea(circulo1);
  
}

void imprimirArea(Figura figura){
  print('El área del ${figura.runtimeType} es ${figura.getArea()}');
}



Mixins

  • Más información:


  • Los mixins es la forma que tiene DART de reutilizar código entre diferentes clases.
Viene a ser algo parecido a extends - implements pero con la siguiente diferencia:
  • extends: Sólo se puede heredar de una clase (no se permite la herencia múltiple). Automáticamente disponemos de todos los métodos/atributos definidos en la clase padre y podremos sobreescribir los métodos definidos previamente.
  • implements: Una clase puede 'heredar' de múltiples clases (una forma de implementar la herencia múltiple). Obliga a implementar (o sobreescribir) todos los métodos y atributos definidos en la clase que se implementa.
  • mixins: Una clase puede 'heredar' de múltiples clases, pero no tiene que sobreescribir los métodos o atributos definidos y puede hacer uso del código ya implementado en la clase que se implementa.


  • Cualquier clase (real o abstracta) puede ser 'usada' como mixin, pero si modificamos su definición con la palabra clave mixin no podrá ser heredada.
Veamos un ejemplo:
mixin OperacionesListas{
  
  void imprimirLista(List<int> lista){
    lista.forEach((elem) => print('Elmento de la lista:$elem'));
  }
}

mixin OperacionesMapas{
  void imprimirMapa(Map<String,String> mapa){
    mapa.forEach((clave,valor) => print('La $clave tiene el valor:$valor'));
  }
}


class ClaseListas with OperacionesListas{
  
  List<int> lista;
  ClaseListas(this.lista);
  
}

class ClaseListasYMapas with OperacionesListas, OperacionesMapas{
  List<int> lista;
  Map<String,String> mapa;
  
  ClaseListasYMapas(this.lista, this.mapa);
}

void main() {
  
  final claseListas = new ClaseListas([1,2,3,4]);
  final claseListasMapas = new ClaseListasYMapas([10,11,12,13],{'nombre' : 'Jose', 'edad' : '45'});
  
  claseListas.imprimirLista(claseListas.lista);
  claseListasMapas.imprimirLista(claseListasMapas.lista);
  claseListasMapas.imprimirMapa(claseListasMapas.mapa);

}







Enlace a la página principal de la UD2

Enlace a la página principal del curso




-- Ángel D. Fernández González -- (2021).