Correspondência com Patterns

O recurso Patterns só pode ser utilizado a partir da versão Dart ≥ 3.0

I. Funções centrais dos Patterns

Pattern é uma categoria sintática independente no Dart (mesmo nível de sentenças e expressões) que descreve a estrutura de um conjunto de valores. Ele possui duas funcionalidades principais que podem ser usadas juntas: correspondência (Match) e desestruturação (Destructure).

  1. Correspondência Match: Verificar se um valor se encaixa na estrutura esperada
    • Corresponde a uma constante definida?
    • Pertence a um tipo específico?
    • Possui uma estrutura definida (Lista/Mapa/Objeto/Record)?
    • Atende a uma condição extra?
  2. Desestruturação Destructure: Após uma correspondência bem-sucedida, extrair rapidamente campos e elementos internos do valor e associá-los a variáveis

1. Exemplo de correspondência por constante

void main() {
	int number = 1;
	switch (number) {
	  case 1:
		print('one'); // Correspondência de constante: number == 1
	  default:
		print('outro número');
	}
}Code language: Dart (dart)

Resultado da execução

one

2. Correspondência com subpadrões aninhados

Os padrões aceitam subpadrões aninhados para validar a estrutura interna passo a passo

void main() {
  const a = 'a';
  const b = 'b';
  var obj = [ 'a', 'b'];
  switch (obj) {
    case [a, b]:
      print('$a, $b');
    default:
      print('Sem correspondência');
  }
}Code language: Dart (dart)

Resultado da execução

a, b

3. Exemplo básico de desestruturação

var numList = [1, 2, 3];
// Padrão de Lista para desmontar elementos e associar a novas variáveis
var [x, y, z] = numList; 
print(x + y + z); // 6 x = 1, y = 2, z = 3Code language: PHP (php)

4. Correspondência + desestruturação com padrão aninhado

void main() {
  var list = ['b', 99];
  switch (list) {
    // Padrão OR lógico: primeiro elemento é a ou b, o segundo é associado à variável c
    case ['a' || 'b', var c]:
      print(c); // 99
  }
}Code language: Dart (dart)

II. Exemplos comuns de uso dos Patterns

1. Declaração de variáveis locais

Usado para atribuição desestruturante, cria várias variáveis de uma vez só

// Desestruturação com padrão Record + Lista aninhado
var (str, [n1, n2]) = ('text', [10, 20]);
print(str); //text
print(n1 + n2); //30Code language: Dart (dart)

2. Reatribuição de variáveis existentes (troca de valores sem variável temporária)

var (a, b) = ("esquerda", "direita");
(b, a) = (a, b); // Trocar os valores de duas variáveis
print('$a $b'); // direita esquerdaCode language: Dart (dart)

3. Ramificações switch / if-case (uso mais frequente, cláusula de guarda when)

Cada case aceita padrões refutáveis: se não houver correspondência, segue para o próximo case

Case básico para múltiplos tipos

void testSwitch(dynamic obj) {
  switch (obj) {
    case 1:
      print('número 1');
    case >= 10 && <= 20:
      print('número entre 10 e 20');
    case (var a, var b):
      print('Record: $a,$b');
    default:
      print('Nenhum caso correspondente');
  }
}Code language: Dart (dart)

Padrão OR lógico: mesma lógica para várias ramificações

enum Color { red, yellow, blue, green }
void main() {
  Color color = Color.red;
  bool isPrimary = switch (color) {
    Color.red || Color.yellow || Color.blue => true,
    _ => false,
  };
  print(isPrimary); // true
}
Code language: PHP (php)

Cláusula de guarda when (filtro condicional extra após correspondência)

sealed class Shape {}
class Square { final double size; Square(this.size); }
class Circle { final double size; Circle(this.size); }

void checkShape(Shape shape) {
  switch (shape) {
    // Corresponde Square e Circle ao mesmo tempo, adiciona condição size>0
    case Square(size: var s) || Circle(size: var s) when s > 0:
      print('figura simétrica válida');
    default:
      print('figura inválida');
  }
}Code language: Dart (dart)

Diferença entre when e um if comum

void compareWhen((int, int) pair) {
  switch (pair) {
    case (int a, int b):
      if (a > b) print('O primeiro valor é maior');
      // Se o if falhar: todo o switch é interrompido, os cases seguintes não rodam
    case (int a, int b) when a > b:
      print('【Guarda when】O primeiro valor é maior');
      // Se o when falhar: apenas pula esse case e testa o próximo
    case (int a, int b):
      print('O primeiro valor é menor ou igual');
  }
}

void main() {
  print("===== Teste (5, 2) a > b =====");
  compareWhen((5, 2));

  print("\n===== Teste (2, 5) a < b =====");
  compareWhen((2, 5));
}Code language: Dart (dart)

4. Laços for /for-in (desestruturação de MapEntry muito prática)


void main() {
	Map<String, int> hist = {'a': 23, 'b': 100};
	
	// Escrita completa
	for (var MapEntry(key: key, value: count) in hist.entries) {
	  print('$key : $count');
	}
	
	// Sintaxe abreviada: key:key simplificado para :key
	for (var MapEntry(:key, value: count) in hist.entries) {
	  print('$key : $count');
	}
}Code language: Dart (dart)

Resultado da execução

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
a : 23
b : 100
a : 23
b : 100
Code language: CSS (css)

III. Cenários práticos de uso empresarial dos Patterns

1: Múltiplos valores retornados por função (desestruturação de Records)

Método tradicional (verboso e trabalhoso)

(String, int) userInfo() => ("Lily", 13);
void main() {
  var info = userInfo();
  var name = info.$1;
  var age = info.$2;
}
Code language: Dart (dart)

Forma simplificada via padrão

var (name, age) = userInfo();
// Desestruturação de Record com campos nomeados
final (:name, :age) = getData(); // Retorna (name: "Doug", age:25)Code language: Dart (dart)

Exemplo prático completo


(String, int) userInfo() => ("Lily", 18);


({String name, int age}) getData() => (name: "Doug", age: 25);

void main() {
  {
    var (name, age) = userInfo();
    print("Nome: $name, idade: $age");
  }

  // As variáveis name/age internas não conflitam com as externas
  {
    final (:name, :age) = getData();
    print("Nome: $name, idade: $age");
  }
}Code language: Dart (dart)

2: Desestruturação de instâncias de classe (padrão de objeto)

class Foo {
  final String one;
  final int two;
  Foo({required this.one, required this.two});
}

void main() {
  final Foo myFoo = Foo(one: "test", two: 66);
  var Foo(:one, :two) = myFoo;
  print('$one $two'); //test 66
}Code language: Dart (dart)

3: Tipos de dados algébricos (classes sealed + cálculo com ramificações switch)

Processar um grupo de subclasses relacionadas de forma unificada, sem métodos de instância espalhados pelo código

import 'dart:math';

sealed class Shape {}
class Square implements Shape { final double length; Square(this.length); }
class Circle implements Shape { final double radius; Circle(this.radius); }

double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(radius: var r) => pi * r * r,
};
Code language: Dart (dart)

Exemplo de implementação completa

import 'dart:math';

// Classe pai selada, subclasses só podem ser declaradas neste arquivo
sealed class Shape {}

// Subclasse Quadrado
class Square implements Shape {
  final double length;
  Square(this.length);
}

// Subclasse Círculo
class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// Cálculo unificado de área com expressão switch + padrão de objeto
double calculateArea(Shape shape) => switch (shape) {
  // Corresponde Square e extrai length para a variável l
  Square(length: var l) => l * l,
  // Corresponde Circle e extrai radius para a variável r
  Circle(radius: var r) => pi * r * r,
};

void main() {
  // Cria instâncias das figuras
  Shape square = Square(5);
  Shape circle = Circle(3);

  // Calcula as áreas
  double squareArea = calculateArea(square);
  double circleArea = calculateArea(circle);

  print("Área do quadrado: $squareArea");    // 25.0
  print("Área do círculo: ${circleArea.toStringAsFixed(2)}"); // 28.27
}Code language: Dart (dart)

Resultado da execução

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Área do quadrado: 25.0
Área do círculo: 28.27Code language: CSS (css)

4: Validação e desestruturação de dados JSON (simplifica muito condicionais aninhados)

(Sem Patterns) Múltiplas verificações de tipo aninhadas geram código longo e repetitivo

void oldCheck(Map data) {
  if (data is Map<String, Object?> && data.containsKey('user')) {
    var user = data['user'];
    if (user is List && user.length == 2) {
      String name = user[0] as String;
      int age = user[1] as int;
      print('$name $age');
    }
  }
}Code language: Dart (dart)

Padrão if-case: uma linha só para validação de estrutura + tipo + associação de variáveis

void newCheck(Map data) {
  if (data case {'user': [String name, int age]}) {
    print('Usuário $name, idade $age');
  }
}Code language: Dart (dart)

Esse padrão valida simultaneamente todos os pontos abaixo:

  1. data é um Map e não é nulo
  2. A chave “user” existe no mapa
  3. O valor vinculado à chave user é uma lista com dois elementos
  4. Primeiro elemento String, segundo elemento int
  5. Associação automática das variáveis locais name e age

Exemplo de uso real

void main() {
  // 1. Estrutura JSON válida: correspondência com sucesso
  final validJson = {
    'user': ['Lily', 13]
  };
  newCheck(validJson); // Saída: Usuário Lily, idade 13

  // 2. Tipo incompatível na lista: idade como texto, sem correspondência
  final wrongTypeJson = {
    'user': ['Tom', '16']
  };
  newCheck(wrongTypeJson); // Sem saída

  // 3. Lista user incompleta: sem dado de idade, sem correspondência
  final shortJson = {
    'user': ['Jack']
  };
  newCheck(shortJson); // Sem saída

  // 4. Chave user inexistente: sem correspondência
  final noUserJson = {
    'info': ['Bob', 20]
  };
  newCheck(noUserJson); // Sem saída
}

// Validação de estrutura/tipo e desestruturação em um passo com padrão Map+List if-case
void newCheck(Map data) {
  if (data case {'user': [String name, int age]}) {
    print('Usuário $name, idade $age');
  } else {
    print('Formato de dados inválido');
  }
}Code language: PHP (php)

Correspondência com Patterns

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *