1. Introdução
Dart é a linguagem de programação do Flutter, o kit de ferramentas de IU do Google para criar apps nativos incríveis para computador, dispositivos móveis e Web com uma única base do código.
Este codelab apresenta o Dart com foco em recursos que os desenvolvedores Java não esperam. É possível gravar funções do Dart em um minuto, scripts em 5 minutos e apps em 10 minutos.
O que você aprenderá
- Instruções para criar construtores
- Maneiras diferentes de especificar parâmetros
- Quando e como criar getters e setters
- Como o Dart cuida da privacidade
- Instruções para criar fábricas
- Como é a programação funcional no Dart
- Outros conceitos centrais do Dart
Pré-requisitos
Para concluir este codelab, você só precisa de um navegador.
Todos os exemplos são gravados e executados no DartPad, uma ferramenta interativa com base em navegador que permite brincar com recursos da linguagem Dart e bibliotecas principais. Se preferir, você pode usar um ambiente de desenvolvimento integrado, como o WebStorm, o IntelliJ com o plug-in Dart ou o Visual Studio Code com a extensão Dart Code.
O que você quer aprender com este codelab?
2. Criar uma classe Dart simples
Para começar, você criará uma classe Dart simples com a mesma funcionalidade da classe Bicycle
do tutorial sobre Java. A classe Bicycle
contém algumas variáveis de instância privadas com getters e setters. Um método main()
instancia uma Bicycle
e a imprime no console.
Iniciar o DartPad
Este codelab fornece uma nova instância do DartPad para cada conjunto de exercícios. O link abaixo abre uma nova instância, que contém um exemplo "Hello" padrão. Você pode continuar usando o mesmo DartPad em todo o codelab, mas, se clicar em Redefinir, o DartPad voltará ao exemplo padrão e seu trabalho será perdido.
Definir uma classe Bicycle
Acima da função
main()
, adicione uma classe Bicycle
com três variáveis de instância. Além disso, remova o conteúdo de main()
, conforme mostrado no snippet de código a seguir:
class Bicycle {
int cadence;
int speed;
int gear;
}
void main() {
}
Observações
- Neste exemplo, o analisador do Dart produz um erro informando que as variáveis precisam ser inicializadas porque não são anuláveis. Isso será corrigido na próxima seção.
- O método principal do Dart é chamado de
main()
. Se você precisar de acesso a argumentos da linha de comando, poderá adicioná-los:main(List<String> args)
. - O método
main()
fica no nível superior. No Dart, é possível definir o código fora das classes. Variáveis, funções, getters e setters podem ficar fora das classes. - O exemplo Java original declara variáveis de instância privadas usando a tag
private
, que o Dart não usa. Você aprenderá mais sobre privacidade posteriormente, em "Adicionar uma variável somente leitura". main()
eBicycle
não são declarados comopublic
porque, por padrão, todos os identificadores são públicos. O Dart não tem palavras-chave parapublic
,private
ouprotected
.- Por convenção, o Dart usa o recuo de dois caracteres, em vez de quatro. Não é preciso se preocupar com as convenções de espaço em branco do Dart, graças a uma ferramenta útil chamada formato Dart (link em inglês). Como dizem as convenções de código do Dart (Effective Dart, link em inglês), "as regras oficiais de processamento de espaços em branco para Dart são qualquer tipo de formato do Dart."
Definir um construtor da classe Bicycle
Adicione o seguinte construtor à classe
Bicycle
:
Bicycle(this.cadence, this.speed, this.gear);
Observações
- Esse construtor não tem corpo, o que é válido no Dart.
- Se você esquecer o ponto e vírgula (
;
) no final de um construtor sem corpo, o DartPad exibirá o seguinte erro: "Um corpo de função precisa ser fornecido". - O uso de
this
na lista de parâmetros de um construtor é um atalho útil para atribuir valores a variáveis de instância. - O código acima é equivalente ao exemplo a seguir, que usa uma lista de inicializadores:
Bicycle(int cadence, int speed, int gear)
: this.cadence = cadence,
this.speed = speed,
this.gear = gear;
Formatar o código
Reformate o código do Dart a qualquer momento clicando em Formatar na parte superior da IU do DartPad. A reformatação é particularmente útil quando você cola o código no DartPad e a justificativa está desativada.
Clique em Formatar.
Instanciar e imprimir uma instância da classe Bicycle
Adicione o seguinte código à função
main()
:
void main() {
var bike = new Bicycle(2, 0, 1);
print(bike);
}
Remova a palavra-chave
new
opcional:
var bike = Bicycle(2, 0, 1);
Observação
- A palavra-chave
new
passou a ser opcional no Dart 2. - Se você souber que o valor de uma variável não será alterado, poderá usar
final
em vez devar
. - A função
print()
aceita qualquer objeto (não apenas strings). Ele a converte em umString
usando o métodotoString()
do objeto.
Executar o exemplo
Execute o exemplo clicando em Executar na parte superior da janela do DartPad. Se Executar não estiver ativado, consulte a seção Problemas mais adiante nesta página.
Você verá esta resposta:
Instance of 'Bicycle'
Observação
- Nenhum erro ou aviso pode ser exibido para indicar que a inferência de tipo está funcionando e que o analisador infere que a instrução que começa com
var bike =
define uma instância de Bicycle.
Melhorar a resposta
Embora a resposta "Instância de Bicycle'" esteja correta, ela não é muito informativa. Todas as classes do Dart têm um método toString()
que pode ser substituído para fornecer uma resposta mais útil.
Adicione o seguinte método
toString()
em qualquer lugar na classe Bicycle
:
@override
String toString() => 'Bicycle: $speed mph';
Observações
- A anotação
@override
informa ao analisador que você está substituindo um membro intencionalmente. O analisador gerará um erro se você não executar corretamente a substituição. - O Dart aceita aspas simples ou duplas ao especificar strings.
- Use a interpolação de string para colocar o valor de uma expressão dentro de uma literal de string:
${expression}
. Se a expressão for um identificador, ignore as chaves:$variableName
. - Encurte funções ou métodos de uma linha usando a notação de seta dupla (
=>
).
Executar o exemplo
Clique em Executar.
Você verá esta resposta:
Bicycle: 0 mph
Problemas?Verifique seu código.
Adicionar uma variável somente leitura
O exemplo Java original define speed
como uma variável somente leitura. Ele a declara como privada e fornece apenas um getter. Depois, você fornecerá a mesma funcionalidade no Dart.
Abra
bicycle.dart
no DartPad (ou continue usando sua cópia).
Para marcar um identificador do Dart como privado na biblioteca, comece o nome com um sublinhado (_
). É possível converter speed
em somente leitura mudando o nome e adicionando um getter.
Transformar a velocidade em uma variável de instância privada somente leitura
No construtor
Bicycle
, remova o parâmetro speed
:
Bicycle(this.cadence, this.gear);
Em
main()
, remova o segundo parâmetro (speed
) da chamada para o construtor Bicycle
:
var bike = Bicycle(2, 1);
Mude as ocorrências restantes de
speed
para _speed
. (Dois lugares)
Inicialize
_speed
como 0:
int _speed = 0;
Adicione o seguinte getter à classe
Bicycle
:
int get speed => _speed;
Observações
- Cada variável, mesmo que seja um número, precisa ser inicializada ou declarada como nulo, adicionando
?
à declaração de tipo. - O compilador do Dart aplica a privacidade da biblioteca para qualquer identificador com um sublinhado como prefixo. Privacidade da biblioteca geralmente significa que o identificador só é visível dentro do arquivo (não apenas na classe) em que ele é definido.
- Por padrão, o Dart fornece getters e setters implícitos para todas as variáveis de instância públicas. Não é necessário definir seus próprios getters ou setters, a menos que você queira aplicar variáveis somente leitura ou somente gravação, calcular ou verificar um valor ou atualizar um valor em outro lugar.
- O exemplo de Java original forneceu getters e setters para
cadence
egear
. Como o exemplo de Dart não precisa de getters e setters explícitos, ele usa apenas variáveis de instância. - Você pode começar com um campo simples, como
bike.cadence
, e depois refatorar para usar getters e setters. A API permanece igual. Em outras palavras, ir de um campo para um getter e setter não é uma alteração interruptiva no Dart.
Concluir a implementação da velocidade como uma variável de instância somente leitura
Adicione os seguintes métodos à classe
Bicycle
:
void applyBrake(int decrement) {
_speed -= decrement;
}
void speedUp(int increment) {
_speed += increment;
}
O exemplo de Dart final é semelhante ao de Java original, mas é mais compacto em 23 linhas, em vez de 40:
class Bicycle {
int cadence;
int _speed = 0;
int get speed => _speed;
int gear;
Bicycle(this.cadence, this.gear);
void applyBrake(int decrement) {
_speed -= decrement;
}
void speedUp(int increment) {
_speed += increment;
}
@override
String toString() => 'Bicycle: $_speed mph';
}
void main() {
var bike = Bicycle(2, 1);
print(bike);
}
Problemas?Verifique seu código.
3. Usar parâmetros opcionais (em vez de sobrecarga)
O próximo exercício define uma classe Rectangle
, outro exemplo do tutorial sobre Java.
O código Java mostra a sobrecarga de construtores, uma prática comum em Java em que os construtores têm o mesmo nome, mas diferem no número ou no tipo de parâmetros. O Dart não é compatível com a sobrecarga de construtores e trata a situação de maneira diferente, como você verá nesta seção.
Abra o exemplo da classe Rectangle no DartPad.
Adicionar um construtor da classe Rectangle
Adicione um único construtor vazio que substitui os quatro construtores no exemplo de Java:
Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0});
Esse construtor usa parâmetros nomeados opcionais.
Observações
this.origin
,this.width
ethis.height
usam a abreviação da atalho para atribuir variáveis de instância na declaração de um construtor.this.origin
,this.width
ethis.height
são parâmetros nomeados opcionais. Os parâmetros nomeados ficam entre chaves ({}
).- A sintaxe de
this.origin = const Point(0, 0)
especifica um valor padrão dePoint(0,0)
para a variável de instânciaorigin
. O padrão especificado precisa ser uma constante de tempo de compilação. Esse construtor fornece valores padrão para as três variáveis de instância.
Melhorar a resposta
Adicione a seguinte função
toString()
à classe Rectangle
:
@override
String toString() =>
'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height';
Usar o construtor
Substitua
main()
pelo seguinte código para confirmar se é possível instanciar Rectangle
usando apenas os parâmetros necessários:
main() {
print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
print(Rectangle(origin: const Point(10, 10)));
print(Rectangle(width: 200));
print(Rectangle());
}
Observação
- O construtor do Dart para
Rectangle
é uma linha de código, em comparação com as 16 linhas de código para construtores equivalentes na versão para Java.
Executar o exemplo
Você verá esta resposta:
Origin: (10, 20), width: 100, height: 200
Origin: (10, 10), width: 0, height: 0
Origin: (0, 0), width: 200, height: 0
Origin: (0, 0), width: 0, height: 0
Problemas?Verifique seu código.
4. Criar uma fábrica
As fábricas, um padrão de design usado com frequência em Java, têm diversas vantagens em relação à instanciação direta de objetos, como ocultar os detalhes da instanciação, permitir o retorno de um subtipo do tipo de retorno de fábrica e, opcionalmente, o retorno de um objeto existente em vez de um novo.
Essa etapa demonstra duas maneiras de implementar uma fábrica de criação de formas:
- Opção 1: criar uma função de nível superior
- Opção 2: criar um construtor de fábrica
Neste exercício, você usará o exemplo da classe Shapes, que instancia formas e imprime a área calculada:
import 'dart:math';
abstract class Shape {
num get area;
}
class Circle implements Shape {
final num radius;
Circle(this.radius);
num get area => pi * pow(radius, 2);
}
class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}
main() {
final circle = Circle(2);
final square = Square(2);
print(circle.area);
print(square.area);
}
Abra o exemplo da classe Shapes no DartPad.
Na área do console, você verá as áreas calculadas de um círculo e um quadrado:
12.566370614359172
4
Observações
- O Dart é compatível com classes abstratas.
- É possível definir várias classes em um arquivo.
dart:math
é uma das bibliotecas principais do Dart. Outras bibliotecas principais incluemdart:core
,dart:async
,dart:convert
edart:collection
.- Por convenção, as constantes de biblioteca do Dart são
lowerCamelCase
(por exemplo,pi
em vez dePI)
. Se você quiser saber mais sobre o motivo, consulte a diretriz de estilo PREFIRA lowerCamelCase para nomes de constante (link em inglês). - O código a seguir mostra dois getters que calculam um valor:
num get area => pi * pow(radius, 2); // Circle num get area => pow(side, 2); // Square
Opção 1: criar uma função de nível superior
Implemente uma fábrica como uma função de nível superior adicionando a seguinte função no nível mais alto (fora de qualquer classe):
Shape shapeFactory(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
throw 'Can\'t create $type.';
}
Invoque a função de fábrica substituindo as duas primeiras linhas no método
main()
:
final circle = shapeFactory('circle');
final square = shapeFactory('square');
Executar o exemplo
A saída precisa parecer a mesma de antes.
Observações
- Se a função for chamada com qualquer string diferente de
'circle'
ou'square'
, uma exceção será gerada. - O SDK do Dart define classes para muitas exceções comuns, é possível implementar a classe
Exception
para criar exceções mais específicas ou, como neste exemplo, é possível gerar uma string que descreve o problema encontrado. - Quando uma exceção é encontrada, o DartPad relata
Uncaught
. Para ver informações mais úteis, envolva o código em uma instruçãotry-catch
e imprima a exceção. Como exercício opcional, confira este exemplo do DartPad. - Para usar aspas simples dentro de uma string, crie um escape para a aspa incorporada com uma barra (
'Can\'t create $type.'
) ou especifique a string com aspas duplas ("Can't create $type."
).
Problemas?Verifique seu código.
Opção 2: criar um construtor de fábrica
Use a palavra-chave factory
do Dart para criar um construtor de fábrica.
Adicione um construtor de fábrica à classe abstrata
Shape
:
abstract class Shape {
factory Shape(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
throw 'Can\'t create $type.';
}
num get area;
}
Substitua as duas primeiras linhas de
main()
pelo código a seguir para instanciar as formas:
final circle = Shape('circle');
final square = Shape('square');
Exclua a função
shapeFactory()
que você adicionou anteriormente.
Observação
- O código no construtor da fábrica é idêntico ao usado na função
shapeFactory()
.
Problemas?Verifique seu código.
5. Implementar uma interface
A linguagem Dart não inclui uma palavra-chave interface
porque cada classe define uma interface.
Abra o exemplo da classe Shapes no DartPad (ou continue usando a cópia).
Adicione uma classe
CircleMock
que implemente a interface Circle
:
class CircleMock implements Circle {}
Você verá um erro "Implementações concretas ausentes" porque
CircleMock
não herda a implementação de Circle
. Ele usa apenas a interface correspondente. Para corrigir esse erro, defina as variáveis de instância area
e radius
:
class CircleMock implements Circle {
num area = 0;
num radius = 0;
}
Observação
- A classe
CircleMock
não define nenhum comportamento, mas é um Dart válido. O analisador não gera erros. - A variável de instância
area
deCircleMock
implementa o getter dearea
deCircle
.
Problemas?Verifique seu código.
6. Usar o Dart para programação funcional
Na programação funcional, é possível fazer o seguinte:
- Transmitir funções como argumentos.
- Atribuir uma função a uma variável.
- Desconstruir uma função que usa vários argumentos em uma sequência de funções em que cada uma usa um único argumento (também chamado de currying).
- Criar uma função sem nome que possa ser usada como um valor constante (também chamada de expressão lambda. As expressões lambda foram adicionadas ao Java no JDK 8).
O Dart é compatível com todos esses recursos. No Dart, até mesmo as funções são objetos e têm um tipo: Function
. Isso significa que as funções podem ser atribuídas a variáveis ou transmitidas como argumentos para outras funções. Você também pode chamar uma instância de uma classe Dart como se ela fosse uma função, como neste exemplo.
O exemplo a seguir usa o código imperativo (estilo não funcional):
String scream(int length) => "A${'a' * length}h!";
main() {
final values = [1, 2, 3, 5, 10, 50];
for (var length in values) {
print(scream(length));
}
}
Abra o exemplo da classe Scream no DartPad.
A saída será semelhante a esta:
Aah!
Aaah!
Aaaah!
Aaaaaah!
Aaaaaaaaaaah!
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah!
Observação
- Ao usar a interpolação de strings, a string
${'a' * length}
avalia como "o caractere'a'
foi repetidolength
vezes".
Converter o código imperativo em funcional
Remova o loop imperativo
for() {...}
em main()
e substitua-o por uma única linha de código que use o encadeamento de métodos:
values.map(scream).forEach(print);
Executar o exemplo
A abordagem funcional imprime as mesmas seis classes Scream do exemplo imperativo.
Problemas?Verifique seu código.
Usar mais recursos de iteração
As classes principais List
e Iterable
são compatíveis com fold()
, where()
, join()
, skip()
e muito mais. O Dart também tem suporte integrado para mapas e conjuntos.
Substitua a linha
values.map()
em main()
pelo seguinte:
values.skip(1).take(3).map(scream).forEach(print);
Executar o exemplo
A saída será semelhante a esta:
Aaah!
Aaaah!
Aaaaaah!
Observações
skip(1)
ignora o primeiro valor, 1, no literal da lista devalues
.take(3)
recebe os próximos três valores (2, 3 e 5) no literal de lista devalues
.- Os valores restantes são ignorados.
Problemas?Verifique seu código.
7. Parabéns!
Com este codelab, você aprendeu algumas diferenças entre Java e Dart. O Dart é fácil de aprender e, além disso, as bibliotecas principais e o rico conjunto de pacotes disponíveis aumentam sua produtividade. O Dart funciona bem para apps grandes. Centenas de engenheiros do Google usam o Dart para escrever apps essenciais que geram grande parte da receita do Google.
Próximas etapas
Um codelab de 20 minutos não é suficiente para mostrar todas as diferenças entre Java e Dart. Por exemplo, este codelab não falou sobre:
- async/await, que permite escrever código assíncrono como se fosse síncrono. Veja este exemplo do DartPad, que anima o cálculo dos cinco primeiros decimais de π.
- Cascatas de método, em que tudo é builder.
- Operadores cientes de valores nulos
Para ver as tecnologias do Dart em ação, confira os codelabs do Flutter.
Saiba mais
Aprenda sobre o Dart com os artigos, recursos e sites a seguir.
Artigos
- Por que o Flutter usa o Dart?
- Anúncio do Dart 2: otimizado para desenvolvimento no lado do cliente
- Por que mudei de Java para Dart?
Recursos
Websites