diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

View Categories

🧩 Rexistros

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.

Exemplos #

🔗 Ver o código en DartPad