Constructores
Todo sobre el uso de constructores en Dart.
Los constructores son funciones especiales que crean instancias de clases.
Dart implementa muchos tipos de constructores. A excepción de los constructores por defecto, estas funciones usan el mismo nombre que su clase.
- Constructores generativos
Crea nuevas instancias e inicializa variables de instancia.
- Constructores por defecto
-
Se utiliza para crear una nueva instancia cuando no se ha especificado un constructor. No toma argumentos y no tiene nombre.
- Constructores nombrados
-
Aclara el propósito de un constructor o permite la creación de múltiples constructores para la misma clase.
- Constructores constantes
Crea instancias como constantes en tiempo de compilación.
- Constructores de tipo factory
-
Crea una nueva instancia de un subtipo o devuelve una instancia existente desde la caché.
- Constructor redireccionado
Reenvía llamadas a otro constructor de la misma clase.
Tipos de constructores
#Constructores generativos
#Para instanciar una clase, usa un constructor generativo.
class Point {
// Instance variables to hold the coordinates of the point.
double x;
double y;
// Generative constructor with initializing formal parameters:
Point(this.x, this.y);
}
Constructores por defecto
#Si no declaras un constructor, Dart utiliza el constructor por defecto. El constructor por defecto es un constructor generativo sin argumentos ni nombre.
Constructores nombrados
#Usa un constructor nombrado para implementar múltiples constructores para una clase o para proporcionar claridad adicional:
const double xOrigin = 0;
const double yOrigin = 0;
class Point {
final double x;
final double y;
// Sets the x and y instance variables
// before the constructor body runs.
Point(this.x, this.y);
// Named constructor
Point.origin() : x = xOrigin, y = yOrigin;
}
Una subclase no hereda el constructor nombrado de una superclase. Para crear una subclase con un constructor nombrado definido en la superclase, implementa ese constructor en la subclase.
Constructores constantes
#Si tu clase produce objetos inmutables, haz que estos
objetos sean constantes en tiempo de compilación.
Para hacer que los objetos sean constantes en tiempo de compilación, define un constructor const
con todas las variables de instancia configuradas como final.
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0);
final double x, y;
const ImmutablePoint(this.x, this.y);
}
Los constructores constantes no siempre crean constantes.
Podrían ser invocados en un contexto que no sea const.
Para obtener más información, consulta la sección sobre usar constructores.
Constructores redireccionados
#Un constructor podría redirigir a otro constructor en la misma clase.
Un constructor redireccionado tiene un cuerpo vacío.
El constructor usa this en lugar del nombre de la clase después de dos puntos (:).
class Point {
double x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(double x) : this(x, 0);
}
Constructores de tipo factory
#Cuando te encuentres con uno de los siguientes dos casos al implementar un constructor,
usa la palabra clave factory:
-
El constructor no siempre crea una nueva instancia de su clase. Aunque un constructor de tipo factory no puede devolver
null, podría devolver:- una instancia existente de una caché en lugar de crear una nueva
- una nueva instancia de un subtipo
-
Necesitas realizar un trabajo no trivial antes de construir una instancia. Esto puede incluir verificar argumentos o realizar cualquier otro procesamiento que no se pueda manejar en la lista de inicializadores.
El siguiente ejemplo incluye dos constructores de tipo factory.
- El constructor de tipo factory
Loggerdevuelve objetos desde una caché. - El constructor de tipo factory
Logger.fromJsoninicializa una variable final desde un objeto JSON.
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
factory Logger.fromJson(Map<String, Object> json) {
return Logger(json['name'].toString());
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
Usa un constructor de tipo factory como cualquier otro constructor:
var logger = Logger('UI');
logger.log('Button clicked');
var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);
Constructores de tipo factory redireccionados
#Un constructor de tipo factory redireccionado especifica una llamada a un constructor de otra clase para usar siempre que alguien realice una llamada al constructor redireccionado.
factory Listenable.merge(List<Listenable> listenables) = _MergingListenable
Dado que los constructores de tipo factory ordinarios también pueden crear y devolver instancias de otras clases, las fábricas redireccionadas parecen innecesarias. Sin embargo, las fábricas redireccionadas tienen varias ventajas:
- Una clase abstracta podría proporcionar un constructor constante que use el constructor constante de otra clase.
- Un constructor de tipo factory redireccionado evita la necesidad de que los reenviadores repitan los parámetros formales y sus valores por defecto.
Constructor tear-offs
#Dart te permite proporcionar un constructor como parámetro sin llamarlo. Lo que se denomina tear-off (ya que "desprendes" o "tears off" los paréntesis) sirve como un closure que invoca al constructor con los mismos parámetros.
Si el tear-off es un constructor con la misma firma y tipo de retorno que acepta el método, puedes usar el tear-off como parámetro o variable.
Los tear-offs difieren de las lambdas o funciones anónimas. Las lambdas sirven como un contenedor para el constructor, mientras que un tear-off es el constructor mismo.
Use Tear-Offs
// Use a tear-off for a named constructor:
var strings = charCodes.map(String.fromCharCode);
// Use a tear-off for an unnamed constructor:
var buffers = charCodes.map(StringBuffer.new);
Not Lambdas
// Instead of a lambda for a named constructor:
var strings = charCodes.map((code) => String.fromCharCode(code));
// Instead of a lambda for an unnamed constructor:
var buffers = charCodes.map((code) => StringBuffer(code));
Para más discusión, mira este video de Decoding Flutter sobre tear-offs.
Sintaxis de constructor concisa
#En Dart 3.13 y versiones posteriores, puedes omitir el nombre de la clase
al declarar un constructor generativo o de tipo factory dentro del cuerpo de la clase
usando directamente el modificador new o factory:
newonew nombradofactoryofactory nombrado
A diferencia de los constructores nombrados tradicionales (como Point.origin()),
los constructores nombrados concisos no usan un punto
entre la palabra clave (modificador) y el nombre.
class Point {
double x, y;
// Concise unnamed generative constructor.
new(this.x, this.y);
// Concise named generative constructor.
new origin() : x = 0, y = 0;
// Equivalent to `factory Point.clone(Point other)`.
factory clone(Point other) => Point(other.x, other.y);
}
Esta sintaxis reduce la verbosidad y facilita la refactorización de los nombres de las clases.
La siguiente tabla muestra cómo mapear la sintaxis de constructor tradicional
a la sintaxis de constructor concisa (para una clase llamada LongClassName):
| Sintaxis original de Dart | Sintaxis abreviada |
|---|---|
LongClassName() {} | new() {} |
LongClassName.name() {} | new name() {} |
const LongClassName(); | const new(); |
const LongClassName.name(); | const new name(); |
LongClassName(): this.other(); | new(): this.other(); |
LongClassName.name(): this(); | new name(): this(); |
const LongClassName(): this.other(); |
const new(): this.other(); |
const LongClassName.name(): this(); |
const new name(): this(); |
factory LongClassName() { ... } | factory() { ... } |
factory LongClassName.name() { ... } |
factory name() { ... } |
factory LongClassName() = D; | factory() = D; |
factory LongClassName.name() = D; | factory name() = D; |
const factory LongClassName() = D; | const factory() = D; |
const factory LongClassName.name() = D; |
const factory name() = D; |
Para obtener una sintaxis aún más concisa en la que defines campos y constructores en una sola línea, consulta Constructores primarios (Primary constructors).
Inicialización de variables de instancia
#Dart proporciona varias formas de inicializar variables de instancia. Puedes asignar valores en la declaración, usar parámetros formales de inicialización, o usar una lista de inicializadores.
Inicializar variables de instancia en la declaración
#Inicializa las variables de instancia al declarar las variables.
class PointA {
double x = 1.0;
double y = 2.0;
// The implicit default constructor sets these variables to (1.0,2.0)
// PointA();
@override
String toString() {
return 'PointA($x,$y)';
}
}
Usar parámetros formales de inicialización
#Para simplificar el patrón común de asignar un argumento de constructor a una variable de instancia, Dart cuenta con parámetros formales de inicialización.
En la declaración del constructor, incluye this.<propertyName>
y omite el cuerpo. La palabra clave this se refiere a la instancia actual.
Cuando existe un conflicto de nombres, usa this.
De lo contrario, el estilo de Dart omite el this.
Existe una excepción para el constructor generativo donde
debes anteponer this al nombre del parámetro formal de inicialización.
Como se señaló anteriormente en esta guía, ciertos constructores
y ciertas partes de los constructores no pueden acceder a this. Estos incluyen:
- Constructores de tipo factory
- El lado derecho de una lista de inicializadores
- Argumentos para un constructor de la superclase
Los parámetros formales de inicialización también te permiten inicializar
variables de instancia no anulables o final.
Ambos tipos de variables requieren inicialización o un valor por defecto.
class PointB {
final double x;
final double y;
// Sets the x and y instance variables
// before the constructor body runs.
PointB(this.x, this.y);
// Initializing formal parameters can also be optional.
PointB.optional([this.x = 0.0, this.y = 0.0]);
}
Esto también funciona con variables nombradas.
class PointC {
double x; // must be set in constructor
double y; // must be set in constructor
// Generative constructor with initializing formal parameters
// with default values
PointC.named({this.x = 1.0, this.y = 1.0});
@override
String toString() {
return 'PointC.named($x,$y)';
}
}
// Constructor using named variables.
final pointC = PointC.named(x: 2.0, y: 2.0);
Todas las variables introducidas a partir de los parámetros formales de inicialización son tanto final como únicamente en el alcance (scope) de las variables inicializadas.
Para realizar una lógica que no puedes expresar en la lista de inicializadores, crea un constructor de tipo factory o un método estático con esa lógica. Luego puedes pasar los valores calculados a un constructor normal.
Puedes configurar los parámetros del constructor como anulables para evitar inicializarlos.
class PointD {
double? x; // null if not set in constructor
double? y; // null if not set in constructor
// Generative constructor with initializing formal parameters
PointD(this.x, this.y);
@override
String toString() {
return 'PointD($x,$y)';
}
}
Private named parameters
#En Dart, los campos que comienzan con un guion bajo son privados para su biblioteca. Para inicializar un campo privado usando un parámetro nombrado, puedes escribir el código repetitivo (boilerplate) de asignación manual en la lista de inicializadores:
class Point {
final double _x;
Point({required double x}) : _x = x;
}
También puedes inicializar campos privados directamente
en la lista de parámetros del constructor.
Cuando antepones this._ al parámetro nombrado,
el compilador elimina automáticamente el guion bajo para el llamador,
lo que le permite usar un nombre público y limpio:
class Point {
final double _x;
Point({required this._x});
}
En ambos casos,
el llamador utiliza el nombre público x en el sitio de la llamada:
var p = Point(x: 1.0);
Al igual que los parámetros nombrados regulares, puedes hacer que los parámetros nombrados privados sean opcionales o requeridos. También puedes proporcionar valores por defecto explícitos.
En el siguiente ejemplo, el parámetro _x es opcional y tiene como valor por defecto null.
El parámetro _y también es opcional pero tiene un valor por defecto explícito de
0.0:
class PointPrivate {
final double? _x; // Nullable field
final double _y; // Non-nullable field
PointPrivate({this._x, this._y = 0.0});
@override
String toString() => 'PointPrivate($_x, $_y)';
}
void testPrivate() {
var p = PointPrivate(x: 1.0, y: 2.0);
print(p);
}
Restricciones
#- Sin conflictos: Ni el nombre privado ni el nombre público generado pueden coincidir con ningún otro nombre de parámetro en el mismo constructor.
- Solo formales de inicialización: Los parámetros nombrados en Dart generalmente
no pueden ser privados. Esta capacidad es una excepción que solo se aplica
a los parámetros nombrados que son formales de inicialización (
this._field). No puedes usar identificadores privados para parámetros nombrados normales. - Nombre público válido: El nombre privado debe mapearse a un identificador público válido.
Por ejemplo,
this._othis._2xson inválidos porque no tienen equivalentes públicos válidos.
Uso en listas de inicializadores
#Dentro de la lista de inicializadores del constructor, haz referencia al parámetro usando su nombre privado:
class PointPrivateAssert {
final double _x;
PointPrivateAssert({required this._x}) : assert(_x >= 0);
}
Interacción con superparámetros
#Al extender una clase que utiliza parámetros nombrados privados, las subclases utilizan el nombre público para los superparámetrosSuperparámetroUn parámetro del constructor que pasa automáticamente un argumento al constructor de la superclase. Obtén más información.
En el siguiente ejemplo, la clase Tool define el campo privado _price.
Aunque el campo es privado,
su parámetro nombrado correspondiente es público (price, no _price).
Para pasar el valor,
la subclase Hammer utiliza el identificador público price:
class Tool {
final int _price;
Tool({required this._price});
}
class Hammer extends Tool {
// Forwards to the public 'price' argument
Hammer({required super.price});
}
Usar una lista de inicializadores
#Antes de que se ejecute el cuerpo del constructor, puedes inicializar variables de instancia. Separa los inicializadores con comas.
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, double> json) : x = json['x']!, y = json['y']! {
print('In Point.fromJson(): ($x, $y)');
}
Para validar las entradas durante el desarrollo,
usa assert en la lista de inicializadores.
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
Las listas de inicializadores ayudan a configurar campos final.
El siguiente ejemplo inicializa tres campos final en una lista de inicializadores.
Para ejecutar el código, haz clic en Run.
Herencia de constructores
#Las subclases, o clases hijas, no heredan los constructores de su superclase, o clase de la que heredan directamente. Si una clase no declara un constructor, solo puede usar el constructor por defecto.
Una clase puede heredar los parámetros de una superclase. Estos se denominan superparámetros
Los constructores funcionan de manera algo similar a cómo llamas a una cadena de métodos estáticos. Cada subclase puede llamar al constructor de su superclase para inicializar una instancia, al igual que una subclase puede llamar al método estático de una superclase. Este proceso no "hereda" los cuerpos ni las firmas de los constructores.
Constructores de superclase no predeterminados
#Dart ejecuta los constructores en el siguiente orden:
- lista de inicializadores
- constructor sin nombre y sin argumentos de la superclase
- constructor sin argumentos de la clase principal
Si la superclase carece de un constructor sin nombre y sin argumentos,
llama a uno de los constructores en la superclase.
Antes del cuerpo del constructor (si lo hay),
especifica el constructor de la superclase después de dos puntos (:).
En el siguiente ejemplo,
el constructor de la clase Employee llama al constructor nombrado
para su superclase, Person. Para ejecutar el siguiente código, haz clic en Run.
Como Dart evalúa los argumentos para el constructor de la superclase antes de invocar el constructor, un argumento puede ser una expresión como una llamada a una función.
class Employee extends Person {
Employee() : super.fromJson(fetchDefaultData());
// ···
}
Parámetros super
#Para evitar pasar cada parámetro en la invocación super de un constructor, usa parámetros de superinicialización para reenviar parámetros al constructor de la superclase especificado o predeterminado. No puedes usar esta característica con constructores redireccionados. Los parámetros de superinicialización tienen una sintaxis y semántica similar a los parámetros formales de inicialización.
Si la invocación del superconstructor incluye argumentos posicionales, los parámetros de superinicialización no pueden ser posicionales.
class Vector2d {
final double x;
final double y;
Vector2d(this.x, this.y);
}
class Vector3d extends Vector2d {
final double z;
// Forward the x and y parameters to the default super constructor like:
// Vector3d(final double x, final double y, this.z) : super(x, y);
Vector3d(super.x, super.y, this.z);
}
Para ilustrar mejor, considera el siguiente ejemplo.
// If you invoke the super constructor (`super(0)`) with any
// positional arguments, using a super parameter (`super.x`)
// results in an error.
Vector3d.xAxisError(super.x): z = 0, super(0); // BAD
Este constructor nombrado intenta establecer el valor de x dos veces:
una en el superconstructor y otra como un
superparámetro posicional.
Dado que ambos se refieren al parámetro posicional x, esto genera un error.
Cuando el superconstructor tiene argumentos nombrados, puedes dividirlos
entre superparámetros nombrados (super.y en el siguiente ejemplo)
y argumentos nombrados para la invocación del superconstructor
(super.named(x: 0)).
class Vector2d {
// ...
Vector2d.named({required this.x, required this.y});
}
class Vector3d extends Vector2d {
final double z;
// Forward the y parameter to the named super constructor like:
// Vector3d.yzPlane({required double y, required this.z})
// : super.named(x: 0, y: y);
Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);
}
A menos que se indique lo contrario, la documentación en este sitio refleja Dart 3.12.2. Página actualizada por última vez el 2026-05-26. Ver fuente oreportar un problema.