Variáveis e Tipos de Dados no Dart
Variáveis são contêineres para dados, e tipos são a forma do contêiner — escolha o contêiner certo e seu código será seguro.
1. O que Você Aprenderá
- Comparação das cinco formas de declaração: var / final / const / late / dynamic
- Tipos embutidos: int / double / String / bool / num
- Melhores práticas para inferência de tipos e anotação explícita
- Interpolação de Strings e strings de múltiplas linhas (triplas aspas + prefixo r)
- Cenário do Bob: Design de variáveis de configuração no DataPipeline
2. A História Real de um Desenvolvedor
(1) Dor: Crashes em tempo de execução causados por declarações de variáveis descuidadas
Quando Bob estava desenvolvendo a versão inicial do DataPipeline, declarou todas as variáveis com var e frequentemente mudava seus tipos no meio do caminho. Durante um refactor, ele mudou int orderCount para String orderCount por engano. A compilação não reportou erro (tipo dinâmico), mas em tempo de execução, orderCount * price lançou uma exceção, causando a interrupção do processamento em lote de 500.000 pedidos e atrasando a entrega do relatório em 4 horas.
(2) A Solução Type-Safe
O Dart oferece uma gama de métodos de declaração do mais solto ao mais estrito: const (constante em tempo de compilação) → final (constante em tempo de execução) → var (tipo inferido) → late (inicialização preguiçosa) → dynamic (tipo dinâmico). Usá-los apropriadamente permite que o compilador ajude você a encontrar bugs.
// Melhor prática para configuração do DataPipeline
const int maxRecords = 1000000; // Constante em tempo de compilação
final String pipelineName; // Constante em tempo de execução (definida uma vez)
var processedCount = 0; // Inferido como int, mutável
late String outputPath; // Inicializado depois
// dynamic deve ser evitado se possível
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
(3) Benefícios
- O compilador detecta erros de tipo durante a compilação, evitando crashes em tempo de execução.
- Variáveis
constpermitem que o compilador otimize a performance. finalprevine modificação acidental, tornando a intenção do código mais clara.
3. Cinco Formas de Declaração
(1) Árvore de Decisão de Declaração
graph LR
subgraph Declarations
A[var] --> B[Inferência de tipo]
C[final] --> D[Constante em runtime]
E[const] --> F[Constante em tempo de compilação]
G[late] --> H[Inicialização preguiçosa]
I[dynamic] --> J[Tipo dinâmico]
end
subgraph Built-in Types
K[int] --- L[double]
M[String] --- N[bool]
end
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: var — Inferência de Tipo
void main() {
var name = 'DataPipeline'; // Inferido como String
var count = 1000; // Inferido como int
var rate = 0.08; // Inferido como double
// Pode reatribuir mesmo tipo
name = 'Analytics'; // OK - ainda String
count = 2000; // OK - ainda int
// Não pode mudar tipo
// count = 'two thousand'; // Erro de compilação!
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: final — Constante em Tempo de Execução
void main() {
// Definida uma vez em runtime
final DateTime startTime = DateTime.now();
final String configPath = '/etc/datapipeline/config.yaml';
// Não pode reatribuir
// configPath = '/other/path'; // Erro de compilação!
// final com coleção - conteúdo pode mudar
final List<String> sources = ['orders.csv', 'products.csv'];
sources.add('customers.csv'); // OK - modificando conteúdo
// sources = ['new_list']; // Erro - não pode reatribuir referência
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: const — Constante em Tempo de Compilação
void main() {
// Deve ser conhecido em tempo de compilação
const int maxRecords = 1000000;
const double taxRate = 0.08;
const String version = '1.0.0';
// Coleções const são profundamente imutáveis
const List<String> formats = ['json', 'csv', 'html'];
// formats.add('xml'); // Erro! Não pode modificar lista const
// Construtor const
const config = PipelineConfig(batchSize: 10000);
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: late — Inicialização Preguiçosa
class DataPipeline {
late String outputPath; // Será definido depois
void configure(String path) {
outputPath = path;
}
late final int maxRecords = _loadMaxRecords(); // Computado no primeiro acesso
int _loadMaxRecords() {
print('Loading max records config...');
return 1000000;
}
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: dynamic — Tipo Dinâmico
void main() {
dynamic value = 'hello'; // String
value = 42; // OK - agora int
value = [1, 2, 3]; // OK - agora List
// Sem verificação de tipo em tempo de compilação - arriscado!
// value.nonExistentMethod(); // Sem erro de compilação, crash em runtime
// Prefira Object? ao invés de dynamic quando precisar de flexibilidade
Object? safeValue = 'test';
// safeValue.nonExistentMethod(); // Erro de compilação - mais seguro
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
(2) Comparação das Cinco Formas de Declaração
| Forma de Declaração | Tipo | Mutável | Momento da Inicialização | Segurança | Caso de Uso |
|---|---|---|---|---|---|
const |
Determinado em tempo de compilação | Imutável | Na declaração | Mais alta | Constantes de configuração, valores de enum |
final |
Determinado em runtime | Imutável | Na declaração ou no construtor | Alta | Configuração de runtime, valores injetados |
var |
Inferido | Mutável | Na declaração | Média | Variáveis locais, contadores |
late |
Anotado explicitamente | Mutável | Adiada | Média | Campos de inicialiação adiada |
dynamic |
Runtime | Mutável | A qualquer momento | Baixa | Parsing JSON, interoperabilidade |
4. Tipos Embutidos em Detalhe
(1) Tipos Numéricos
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: int e double
void main() {
int orderCount = 1500;
double totalAmount = 52500.75;
num genericNum = 42; // num é supertipo de int e double
genericNum = 3.14; // OK - num aceita ambos
// Operações aritméticas
double avgOrderValue = totalAmount / orderCount;
int roundedDown = totalAmount.toInt();
// Métodos convenientes
print(totalAmount.toStringAsFixed(2)); // 52500.75
print(orderCount.isEven); // false (1500 is even, true)
print(orderCount.clamp(0, 1000)); // 1000 (clamped to max)
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
| Tipo | Intervalo | Propósito |
|---|---|---|
int |
64-bit (varia no JS) | Contagem, indexação |
double |
64-bit IEEE 754 | Valores, proporções |
num |
Supertipo de int + double | Valores numéricos gerais |
(2) Tipo String
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: Interpolação de Strings e Operações
void main() {
// Interpolação de strings
String product = 'DataPipeline';
int users = 5000;
print('$product has $users users'); // Interpolação simples
print('Revenue: ${users * 99} USD'); // Interpolação de expressão
print('Average: ${(125000 / users).toStringAsFixed(2)} USD');
// Strings de múltiplas linhas
String description = '''
DataPipeline - E-Commerce Analytics Tool
Processes up to 1,000,000 orders per batch
Supports JSON, CSV, and HTML output formats
''';
// Raw strings (sem processamento de escape)
String path = r'C:\Users\Bob\data\orders.csv';
String regex = r'\d{4}-\d{2}-\d{2}'; // Padrão de data
// Métodos úteis
String raw = ' Hello, World! ';
print(raw.trim()); // 'Hello, World!'
print(raw.toUpperCase()); // ' HELLO, WORLD! '
print('USD 1,500.00'.replaceAll(',', '')); // 'USD 1500.00'
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
| Recurso | Sintaxe | Exemplo |
|---|---|---|
| Interpolação simples | $variable |
'$name' |
| Interpolação de expressão | ${expr} |
'${a + b}' |
| Strings de múltiplas linhas | '''...''' |
Triplas aspas |
| Raw strings | r'...' |
r'\n' permanece como está |
(3) Tipo bool
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: Valores Booleanos e Condições
void main() {
bool isProduction = true;
bool isDebug = false;
// Dart requer bool explícito em condições
int count = 0;
// if (count) {} // Erro! Deve ser bool
if (count > 0) { // OK - comparação explícita
print('Has records');
}
// Operadores lógicos
bool shouldProcess = isProduction && !isDebug;
bool hasData = count > 0 || isProduction;
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
5. Inferência de Tipos e Anotação Explícita
(1) Melhores Práticas
// Bom - deixe o compilador inferir para variáveis locais
var name = 'DataPipeline'; // Inferido String
var count = 100; // Inferido int
var items = <String>[]; // Genérico explícito, inferido List
// Bom - tipo explícito para API pública
String get displayName => name;
int get maxCapacity => 1000000;
// Bom - final para valores que não mudam
final startTime = DateTime.now();
// Evitar - tipo explícito desnecessário em locais
// String name = 'DataPipeline'; // Redundante
// int count = 100; // Redundante
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
| Cenário | Recomendação | Motivo |
|---|---|---|
| Variáveis locais | var / final |
Reduz redundância, inferência é suficiente |
| API pública | Anotação explícita | Documentação clara, interface estável |
| Genéricos de coleção | Genérico explícito | Evita inferência como List<dynamic> |
| Parâmetros de construtor | Anotação explícita | Contrato claro |
6. Cenário do Bob: Variáveis de Configuração do DataPipeline
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
: Design de Variáveis de Configuração
// Constantes de configuração do DataPipeline
const String appName = 'DataPipeline';
const int defaultBatchSize = 10000;
const double defaultTaxRate = 0.08;
// Configuração de runtime
class PipelineConfig {
final String version;
final int batchSize;
final String outputFormat;
PipelineConfig({
this.version = '1.0.0',
this.batchSize = defaultBatchSize,
this.outputFormat = 'json',
});
@override
String toString() =>
'$appName v$version | Batch: $batchSize | Format: $outputFormat';
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
7. Exemplo Completo: Declaração de Variáveis do DataPipeline na Prática
// ============================================
// Demo de Declaração de Variáveis do DataPipeline
// Mostra uso adequado de todos os tipos de declaração
// ============================================
// Constantes em tempo de compilação - nunca mudam entre execuções
const String appName = 'DataPipeline';
const int maxRecords = 1000000;
const double taxRate = 0.08;
const List<String> supportedFormats = ['json', 'csv', 'html'];
class Order {
final String id; // Constante de runtime após construção
final double amount; // Constante de runtime após construção
late String status; // Será definido pela lógica de processamento
Order({required this.id, required this.amount});
double get tax => amount * taxRate;
double get total => amount + tax;
String get formattedAmount =>
'\$${amount.toStringAsFixed(2)} USD';
}
class DataPipeline {
final String name;
var processedCount = 0; // Contador mutável
late final DateTime startTime; // Definido na primeira execução
DataPipeline({required this.name});
void start() {
startTime = DateTime.now();
print('$appName started at $startTime');
}
void processOrder(Order order) {
processedCount++;
order.status = 'processed';
print('Order ${order.id}: ${order.formattedAmount} '
'(tax: \$${order.tax.toStringAsFixed(2)} USD)');
}
void printSummary() {
print('\n=== Summary ===');
print('Pipeline: $name');
print('Processed: $processedCount orders');
print('Capacity: $processedCount / $maxRecords');
}
}
void main() {
final pipeline = DataPipeline(name: 'E-Commerce Analytics');
pipeline.start();
final orders = [
Order(id: 'ORD-001', amount: 1500.00),
Order(id: 'ORD-002', amount: 3250.50),
Order(id: 'ORD-003', amount: 890.25),
];
for (final order in orders) {
pipeline.processOrder(order);
}
pipeline.printSummary();
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x, e os resultados podem variar ligeiramente dependendo da versão do SDK.
Saída:
DataPipeline started at 2024-01-15 10:30:00.000
Order ORD-001: $1500.00 USD (tax: $120.00 USD)
Order ORD-002: $3250.50 USD (tax: $260.04 USD)
Order ORD-003: $890.25 USD (tax: $71.22 USD)
=== Summary ===
Pipeline: E-Commerce Analytics
Processed: 3 orders
Capacity: 3 / 1000000
❓ Perguntas Frequentes
P: Qual devo usar, var ou final? R: Se a variável não será reatribuída, prefira
final. Se precisar modificá-la, usevar. A regra de lint oficial do Dartprefer_final_localsrecomenda priorizarfinal.
P: Qual a diferença entre const e final? R:
consté uma constante em tempo de compilação; seu valor deve ser determinado em tempo de compilação.finalé uma constante em tempo de execução; pode ser atribuída apenas uma vez, mas seu valor pode ser computado em runtime.DateTime.now()não pode serconst.
P: Quando devo usar late? R: Use
latequando uma variável não pode ser inicializada no ponto da declaração, mas você pode garantir que será inicializada antes do uso. Cenários comuns incluem injeção de dependência e configuração pós-construção. Uso incorreto levará a umLateInitializationErrorem runtime.
P: Qual a diferença entre dynamic e Object?? R:
dynamicdesliga toda verificação de tipo; o compilador não verifica nenhuma chamada de método.Object?retém a verificação de tipo; você só pode chamar métodos deObject. PrefiraObject?quando precisar de flexibilidade.
P: O int do Dart é o mesmo na Web e na VM? R: Não exatamente. Na Dart VM,
inté um inteiro de 64 bits. Quando compilado para JS,intestá sujeito às limitações do JS Number (precisão de 53 bits). Tenha cautela com aritmética de números grandes.
P: Posso usar aspas simples ou aspas duplas para String? R: Ambas são aceitas e têm exatamente o mesmo efeito. O guia de estilo oficial do Dart recomenda aspas simples, usando aspas duplas apenas quando a string em si contém aspa simples.
P: Quando devo usar o tipo num? R: Use
numquando precisar que um parâmetro ou variável aceite tantointquantodouble.numé o supertipo deintedoublee suporta operações aritméticas básicas.
📖 Resumo
- Cinco formas de declaração do mais estrito ao mais solto: const → final → var → late → dynamic
consté determinado em tempo de compilação,finalé determinado em runtime mas atribuído apenas uma vez,varé mutável e infere o tipo- Tipos embutidos:
int(inteiro),double(ponto flutuante),String(string + interpolação),bool(booleano) - Use
var/finalpara inferência de variáveis locais; anote tipos explicitamente para APIs públicas - O DataPipeline usa
constpara definir constantes de configuração,finalpara definir valores imutáveis em runtime
📝 Exercícios
- Básico (Dificuldade ⭐): Declare as seguintes variáveis: um nome de aplicação
const, um horário atualfinale um contadorvar. Imprima seus valores e tipos respectivamente. - Intermediário (Dificuldade ⭐⭐): Escreva uma função que aceite um parâmetro do tipo
nume retorne uma string formatada em USD desse número (ex.:formatUSD(1500.5)retorna"$1,500.50 USD"). Trate tanto entradasintquantodouble. - Desafio (Dificuldade ⭐⭐⭐): Projete uma classe
Configque uselatepara inicializar preguiçosamente uma string de conexão de banco de dados, useconstpara definir valores padrão e usefinalpara armazenar configuração lida de variáveis de ambiente. Demonstre a colaboração dessas três formas de declaração.