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).
- 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?
- 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:
- data é um Map e não é nulo
- A chave “user” existe no mapa
- O valor vinculado à chave user é uma lista com dois elementos
- Primeiro elemento String, segundo elemento int
- 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