Un rexistro (record
) é un tipo agregado dispoñible en Dart, que permite agrupar varios valores nun único obxecto. É similar a unha tupla, pero ofrece:
- Tipos heteroxéneos
- Tamaño fixo
- Acceso aos valores por posición ou por nome
🔤 Sintaxe básica #
(String, int) record1 = ('Ola', 42); // Campos posicionais
({int a, bool b}) record2 = (a: 123, b: true); // Campos nomeados
Tamén podes combinar ambos:
var record = ('inicio', a: 2, b: true, 'fin');
📝 Anotacións de tipo #
As anotacións de tipo para rexistros pódense usar para definir tipos de retorno ou parámetros en funcións.
🧪 Exemplo:
// Función que troca (intercambia) os valores dun rexistro con dous int
(int, int) trocar((int, int) rexistro) {
// Desestrutura o rexistro en dúas variables: 'a' e 'b'
var (a, b) = rexistro;
// Devolve un novo rexistro cos valores invertidos
return (b, a);
}
🔢 Campos posicionais #
Os campos posicionais colócanse directamente entre parénteses:
// Anotación de tipo con campos posicionais
(String, int) rexistro;
// Inicialización
rexistro = ('Un texto', 123);
🧾 Campos nomeados #
- Nunha anotación de tipo, os campos nomeados van entre chaves
{}
como pares tipo-nome. - Nunha expresión de rexistro, o nome vai antes do valor, seguido de dous puntos
:
.
// Anotación de tipo con campos nomeados
({int a, bool b}) record;
// Inicialización
record = (a: 123, b: true);
// Acceso aos campos do rexistro
print('O valor de a é: ${record.a}');
print('O valor de b é: ${record.b}');
🔐 O nome dos campos nomeados forma parte do tipo #
Se dous rexistros teñen campos nomeados con nomes distintos, son de tipos diferentes, aínda que os tipos dos valores coincidan:
({int a, int b}) recordAB = (a: 1, b: 2);
({int x, int y}) recordXY = (x: 3, y: 4);
// Acceso aos campos de recordAB
print('recordAB.a = ${recordAB.a}');
print('recordAB.b = ${recordAB.b}');
// Acceso aos campos de recordXY
print('recordXY.x = ${recordXY.x}');
print('recordXY.y = ${recordXY.y}');
// ❌ Erro de compilación: tipos distintos
// recordAB = recordXY;
🏷️ Nomes documentais en campos posicionais #
Tamén podes poñer nomes aos campos posicionais na anotación de tipo. Estes nomes son só informativos e non afectan ao tipo do rexistro:
(int a, int b) recordAB = (1, 2);
(int x, int y) recordXY = (3, 4);
recordAB = recordXY; // ✅ Correcto
🧠 Isto é similar ás funcións en Dart, onde os parámetros posicionais poden ter nome, pero este non afecta á súa firma.
🧷 Acceso aos campos #
- Os campos posicionais accédense a través de
.$1
,.$2
, etc. - Os campos nomeados accédense directamente polo seu nome.
var record = ('inicio', a: 2, b: true, 'fin');
print(record.$1); // 'inicio'
print(record.a); // 2
print(record.b); // true
print(record.$2); // 'fin'
🧪 Tipado de rexistros #
O tipo dun rexistro está definido pola súa estrutura: número de campos, tipo e nome (se hai).
(int a, int b) recordAB = (1, 2);
(int x, int y) recordXY = (3, 4);
recordAB = recordXY; // ✅ Compatibles
({int a, int b}) r1 = (a: 1, b: 2);
({int x, int y}) r2 = (x: 3, y: 4);
// r1 = r2; ❌ Tipos diferentes: nomes distintos
🧮 Igualdade de rexistros #
Dous rexistros son iguais se teñen:
- A mesma forma (estrutura de tipos e nomes)
- Os mesmos valores
💡 A orde dos campos nomeados non importa para a igualdade.
(int x, int y, int z) punto = (1, 2, 3);
(int r, int g, int b) cor = (1, 2, 3);
print(punto == cor); // true
({int x, int y, int z}) punto2 = (x: 1, y: 2, z: 3);
({int r, int g, int b}) cor2 = (r: 1, g: 2, b: 3);
print(punto2 == cor2); // false
🔁 Funcións con retorno múltiple #
Os rexistros son ideais para devolver varios valores dunha función sen necesidade de crear unha clase ou estrutura adicional.
✅ Exemplo con campos posicionais (sen nomear) #
(int, int) coordenadas() {
return (10, 20);
}
void main() {
var (x, y) = coordenadas();
print('x = $x, y = $y');
}
✅ Exemplo con campos nomeados #
({int ancho, int alto}) tamanioPantalla() {
return (ancho: 1920, alto: 1080);
}
void main() {
final (:ancho, :alto) = tamanioPantalla();
print('Resolución: $ancho x $alto');
}
🧰 Rexistros como estrutura de datos simple #
Podes usar rexistros para representar estruturas de datos simples, como listas de elementos, sen definir clases explícitas:
final botons = [
(
label: "Botón 1",
icon: const Icon(Icons.upload),
onPressed: () => print("Acción 1"),
),
(
label: "Botón 2",
icon: const Icon(Icons.info),
onPressed: () => print("Acción 2"),
)
];
🏷️ Usar typedef
con rexistros #
Podes mellorar a lexibilidade do código definindo un alias de tipo con typedef
:
typedef Boton = ({String label, Icon icon, void Function()? onPressed});
final List<Boton> botons = [
// ...
];
📌 NOTA: Isto permite reutilizar o tipo sen repetir a estrutura. Máis adiante podes substituílo por unha clase ou extensión sen cambiar o código que o usa.