Introdução ao Dart
O Dart é uma linguagem de programação criada para desenvolver aplicações modernas. Ela é muito usada com Flutter, mas Dart não é Flutter. Dart é a linguagem e Flutter é um framework que usa Dart para criar interfaces gráficas, principalmente aplicativos mobile, web e desktop.
Você pode usar Dart para criar:
- Aplicações de linha de comando
- Aplicações web
- APIs
- Aplicativos com Flutter
- Ferramentas internas e scripts
Para que Dart é usado
O Dart resolve principalmente o problema de escrever aplicações multiplataforma com uma linguagem só, um exemplo prático seria:
void main() {
print('Sistema de cadastro iniciado');
}
Esse código pode ser usado em um programa simples de terminal. Mas com o Flutter, a mesma linguagem Dart também pode ser usada para criar telas, botões, formulários e aplicativos completos.
Dart não é Java
Apesar do Dart poder parecer um pouco com Java, C# ou JavaScript em alguns pontos, ele tem suas próprias regras.
Um exemplo de código Dart seria:
String nome = 'fulano';
int idade = 27;
print(nome);
print(idade);
Explicação:
String nome = 'fulano';
Cria uma variável chamadanomeque armazena texto.int idade = 27;
Cria uma variável chamadaidadeque armazena um número inteiro.print(nome);
Mostra o valor da variávelnomena tela.print(idade);
Mostra o valor da variávelidadena tela.
Em Dart, o tipo da variável vem antes do nome da variável, parecido com Java e C#.
Como Dart é executado
O Dart pode ser executado de duas formas principais. Durante o desenvolvimento, normalmente se usa:
dart run
Nesse modo, o código roda na Dart VM usando JIT (que significa Just-in-Time). O código é compilado enquanto roda. Isso ajuda durante o desenvolvimento porque facilita testes rápidos e depuração. Para gerar um executável, você pode usar:
dart compile exe bin/meu_app.dart
# Para compilar explicitamente para Linux, use --target-os=linux:
dart compile exe bin/cli.dart \
--target-os=linux \
--target-arch=x64 \
-o cli
Nesse caso, Dart gera um executável nativo. Esse processo usa AOT (que significa Ahead-of-Time). O código é compilado antes da execução, gerando um binário para produção.
Quando uma aplicação Dart é compilada como binário executável, o servidor não precisa ter o Dart instalado. O comando dart compile exe gera um executável autônomo para o sistema de destino. Esse arquivo já inclui o código nativo da aplicação, as dependências usadas pelo projeto e uma pequena parte do runtime do Dart necessária para executar o programa.
Existe apenas um cuidado importante. O binário precisa ser compatível com o sistema onde será executado. Por exemplo, um binário gerado para Linux x64 deve ser executado em um servidor Linux x64. Não basta compilar no Windows e copiar o arquivo para um servidor Linux esperando que ele funcione diretamente. O binário precisa ser gerado para o sistema operacional e para a arquitetura corretos.
Ferramentas básicas
O comando dart é a interface principal do SDK. Com ele você cria projetos, executa programas, analisa código, testa e compila aplicações.
Instalando no Linux Ubuntu/Debian
No Ubuntu ou Debian, a documentação oficial recomenda instalar pelo gerenciador de pacotes.
sudo apt-get update
sudo apt-get install apt-transport-https
Adicione a chave GPG:
wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub \
| sudo gpg --dearmor -o /usr/share/keyrings/dart.gpg
Adicione o repositório:
echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' \
| sudo tee /etc/apt/sources.list.d/dart_stable.list
Instale o Dart:
sudo apt-get update
sudo apt-get install dart
Verifique a instalação:
dart --version
Criando um projeto Dart
Depois de instalar, você pode criar um projeto de console:
dart create -t console meu_app
Entre no diretório:
cd meu_app
Execute:
dart run
dart create
Cria um novo projeto Dart.-t console
Define que o projeto será uma aplicação de terminal.meu_app
É o nome da pasta do projeto.dart run
Executa o projeto.
Estrutura inicial de um projeto
Um projeto Dart simples costuma ter uma estrutura parecida com esta:
meu_app/
├── bin/
│ └── meu_app.dart
├── pubspec.yaml
└── README.md
bin/
Guarda o arquivo principal de execução da aplicação.meu_app.dart
Arquivo Dart principal do projeto.pubspec.yaml
Arquivo de configuração do projeto. Nele ficam nome, versão e dependências.README.md
Arquivo de documentação do projeto.
Erros comuns de iniciantes
Esquecer o ponto e vírgula. Em Dart, a maioria das instruções termina com ;.
# Errado:
print('Olá')
# Correto:
print('Olá');
Variáveis e tipos de dados
Uma variável é um nome usado para guardar um valor na memória, exemplo:
void main() {
String usuario = 'Fulano';
int idade = 27;
print(usuario);
print(idade);
}
Saída:
Fulano
27
O trecho abaixo cria uma variável chamada usuario. Esse variável é do tipo String, que é usado para armazenar texto.
String usuario = 'Fulano';
O trecho abaixo cria uma variável chamada idade. Esse variável é do tipo int, que é usado para armazenar números inteiros.
int idade = 27;
Em Dart, toda variável aponta para um objeto, e os tipos principais incluem int, double, String, bool, List, Set, Map e Null. A tabela abaixo mostra as diferenças entre os tipos:
| Tipo | Uso | Exemplo |
|---|---|---|
String | Usado para armazenar texto. Pode ser um nome, email, mensagem, endereço IP ou qualquer valor textual. | String nome = 'Fulano'; |
int | Usado para armazenar números inteiros, sem casas decimais. | int idade = 27; |
double | Usado para armazenar números com casas decimais. | double preco = 19.90; |
num | Usado quando o valor pode ser inteiro ou decimal. É um tipo mais genérico para números. | num valor = 10.5; |
bool | Usado para armazenar verdadeiro ou falso. Muito usado em condições. | bool ativo = true; |
List | Usado para armazenar vários valores em ordem. É parecido com um array em outras linguagens. | List<String> usuarios = ['Ana', 'Bruno']; |
Map | Usado para armazenar dados no formato chave e valor. É parecido com dicionário em Python ou objeto em JavaScript. | Map<String, String> usuario = {'nome': 'Ana'}; |
Set | Usado para armazenar vários valores sem repetição. | Set<String> permissoes = {'read', 'write'}; |
String? | Usado quando uma variável de texto também pode não ter valor, ou seja, pode ser null. | String? telefone = null; |
O tipo List em Dart apesar de parecer com array em outras linguagens, segue a mesma premissa da Lista em Python. No entanto, a lista em Python aceita qualquer tipo por padrão, mas em Dart, você precisa definir o tipo dos itens.
Na maioria dos casos, o Map vai receber chave sendo string e valores também sendo string ou int. Nesse caso, temos duas opções principais.
Usar Map<String, Object>. Quando as chaves são String, mas os valores podem ser de tipos diferentes, devemos usar Object.
Por enquanto vou deixar apenas essa opção.
Criando variáveis com tipo explícito
A forma mais clara de criar uma variável é informando o tipo.
void main() {
String nome = 'Ana';
int quantidade = 3;
double preco = 19.90;
bool ativo = true;
print(nome);
print(quantidade);
print(preco);
print(ativo);
}
Criando variáveis com var
No Dart também é possível usar var. Isso informa ao Dart que ele deve descobrir o tipo da variável pelo valor dela.
void main() {
var produto = 'Teclado';
var quantidade = 2;
var preco = 150.50;
print(produto);
print(quantidade);
print(preco);
}
Nesse caso, ficaria assim:
| Código | Tipo entendido pelo Dart |
|---|---|
var produto = 'Teclado'; | String |
var quantidade = 2; | int |
var preco = 150.50; | double |
Depois que o tipo é definido, ele não muda.
var não é igual a dynamic
O var deixa Dart descobrir o tipo da variável sozinho, já o dynamic desativa boa parte da checagem de tipo. Exemplo com var:
void main() {
var servidor = 'dns01';
servidor = 'dns02';
print(servidor);
}
Isso funciona, porque o valor continua sendo String. Vejamos com dynamic:
void main() {
dynamic valor = 'Servidor';
valor = 10;
valor = true;
print(valor);
}
Isso funciona, mas pode deixar o código mais perigoso. Dart permite dynamic, mas a própria documentação recomenda usar Object ou Object? quando a intenção for aceitar valores de tipos diferentes, porque dynamic reduz a checagem estática.
Criando variáveis com late
O late é usado quando uma variável ou atributo não recebe valor imediatamente, mas será inicializado depois, antes de ser usado. Em Dart, variáveis não nulas precisam receber valor antes de serem usadas. Quando usamos late, estamos dizendo ao Dart: essa variável não será inicializada agora, mas será preenchida antes do uso".
Um exemplo simples:
class Configuracao {
late String caminhoArquivo;
void carregar() {
caminhoArquivo = '/etc/app/config.yml';
}
void exibir() {
print(caminhoArquivo);
}
}
void main() {
Configuracao config = Configuracao();
config.carregar();
config.exibir();
}
Nesse exemplo, o atributo caminhoArquivo não recebe valor no momento em que o objeto é criado. Ele só recebe valor depois, dentro do método carregar.
Depois disso, o método exibir pode usar o valor normalmente:
print(caminhoArquivo);
Mas é preciso tomar cuidado. Se uma variável late for usada antes de receber valor, o programa dará erro em tempo de execução. Exemplo ruim:
void main() {
late String nome;
print(nome);
}
Esse código dá erro porque nome foi declarado como late, mas nunca recebeu valor antes de ser usado. O late pode ser útil em alguns casos, mas não deve ser usado sem necessidade. Para atributos que já são recebidos no construtor, prefira inicializar diretamente.
final e const
Além de variáveis comuns, Dart tem final e const.
final
Use final quando o valor só pode ser definido uma vez.
void main() {
final String cpf = '123.456.789-00';
print(cpf);
}
Depois disso, você não pode alterar cpf.
const
O const quer dizer que o valor é fixo e já é conhecido antes do programa rodar. O valor também não pode ser alterado.
void main() {
const double pi = 3.14159;
print(pi);
}
Resumindo, use const quando você sabe que aquele valor sempre será o mesmo e já está escrito no código.
Principais tipos de dados
Vamos ver um pouco mais em detalhes sobre os principais tipos de dados.
String
Usado para texto.
void main() {
String nome = 'Servidor DNS';
String endereco = "192.168.0.10";
print(nome);
print(endereco);
}
Dart aceita aspas simples e aspas duplas para textos. Para interpolação de texto:
void main() {
String usuario = 'Bruno';
String sistema = 'Financeiro';
print('Usuário $usuario acessou o sistema $sistema');
}
int
Usado para números inteiros.
void main() {
int portas = 48;
int usuarios = 120;
print(portas);
print(usuarios);
}
double
Usado para números com casas decimais.
void main() {
double preco = 199.90;
double temperatura = 36.5;
print(preco);
print(temperatura);
}
Em Dart, double é um número decimal de 64 bits, parecido com float64 em outras linguagens. Na prática, ele consegue trabalhar bem com algo em torno de 15 a 17 dígitos significativos. Em cálculos, o double pode gerar pequenas imprecisões, já que ele acaba arredondando.
Para mostrar com uma quantidade fixa de casas decimais, podemos usar toStringAsFixed().
num
Use num quando a variável pode receber int ou double.
void main() {
num valor = 10;
print(valor);
valor = 10.50;
print(valor);
}
Use com moderação, o melhor é Especificar o tipo com int ou double.
bool
Usado para verdadeiro ou falso.
void main() {
bool usuarioAtivo = true;
bool pagamentoAprovado = false;
print(usuarioAtivo);
print(pagamentoAprovado);
}
Uso comum com condição:
void main() {
bool servidorOnline = true;
if (servidorOnline) {
print('Servidor disponível');
} else {
print('Servidor indisponível');
}
}
List
Uma lista guarda vários valores em ordem.
void main() {
List<String> usuarios = ['Ana', 'Jose', 'Carlos'];
print(usuarios); // Exibe: [Ana, Jose, Carlos]
print(usuarios[0]); // Exibe: Ana
}
O trecho abaixo cria uma lista de textos:
List<String> usuarios
Map
Um Map guarda dados no formato chave e valor.
void main() {
Map<String, String> usuario = {
'nome': 'Fulano',
'email': 'fulano@example.com',
};
print(usuario['nome']);
print(usuario['email']);
}
Set
Um Set guarda valores únicos.
void main() {
Set<String> permissoes = {'read', 'write', 'read'};
print(permissoes);
}
Resultado:
{read, write}
Como o valor read aparece duas vezes, ele imprime apenas uma ocorrência, eliminando entradas repetidas.
Valores nulos
O Dart possui null safety. Isso significa que, por padrão, uma variável não pode receber null. O método correto pra fazer uma variável ser null é:
void main() {
String? nome = null;
print(nome);
}
Conversão de tipos
Conversão de tipos é transformar um valor de um tipo para outro.
String para int
void main() {
String texto = '150';
int valor = int.parse(texto);
print(valor);
}
O trecho abaixo cria um texto (parece número mas não é):
String texto = '150';
Então fazemos a conversão usando int.parse(texto);:
int.parse(texto);
String para double
void main() {
String texto = '19.90';
double preco = double.parse(texto);
print(preco);
}
int para String
void main() {
int idade = 27;
String texto = idade.toString();
print(texto);
}
double para String com casas decimais
void main() {
double valor = 19.9876;
String formatado = valor.toStringAsFixed(2);
print(formatado);
}