Introdução ao Dart, parte 2: repetição, entrada de dados e um jogo
Continue os estudos de Dart com estruturas de repetição, enums, instalação local do SDK com VS Code, entrada e saída de dados, casas decimais, valores aleatórios e um jogo de pedra, papel e tesoura no terminal.
1. Visão geral
Neste material, prosseguimos com os estudos sobre as principais características da linguagem Dart. Este é o segundo codelab de uma série de três.
O que você vai aprender
- As estruturas de repetição
for, for each,whileedo/while - Como usar
enumpara representar listas de constantes - Como instalar o SDK de Dart, configurar o VS Code e executar programas localmente
- Entrada e saída de dados com
stdinestdout(pacotedart:io) e o tratamento deString? - Como controlar o número de casas decimais com
toStringAsFixed - Como gerar valores aleatórios com a classe
Random - Como criar um projeto Dart com
dart createe implementar um jogo de pedra, papel e tesoura
O que você vai precisar
- Conhecimentos da parte 1 da série (tipos, operadores e estruturas de seleção)
- Um computador onde seja possível instalar o SDK de Dart (ou o Flutter) e o VS Code
2. Estruturas de repetição
As estruturas de repetição de Dart são as seguintes.
- for
- for each
- while
- do/while
Veja um exemplo de for.
void main() {
for (int i = 0; i < 10; i++) {
print(i);
}
}
No exemplo a seguir, iteramos sobre uma coleção (veremos mais sobre coleções em breve) utilizando um for each.
void main() {
const nomes = ['Pedro', 'Maria', 'João'];
//em vez de final, podemos usar var e o tipo
//explícito da varíavel (String nesse caso)
//mas não podemos usar const
for (final nome in nomes) {
print(nome);
}
}
Como em outras linguagens de programação, as três regiões do for são opcionais. No exemplo a seguir, dado que não há condição de continuidade, temos um loop infinito.
void main() {
//loop infinito com for
for (;;) {
print('loop infinito');
}
}
O while é semelhante àquele que conhecemos de outras linguagens.
void main() {
//loop com while
int contador = 0;
while (contador <= 10) {
print("Contador: $contador");
contador++;
}
}
Assim como o do/while.
void main() {
//loop com do/while
int i = 0;
do {
print("O valor de i é $i");
i++;
} while (i < 10);
}
3. enum
Muitas linguagens de programação oferecem um recurso conhecido como enum. Ele permite que especifiquemos uma lista de constantes, promovendo a legibilidade do programa. Por exemplo, ao invés de representarmos os dias da semana como números de 1 a 7, podemos criar um enum, que é uma lista com os nomes dos dias explícitos.
enum DiaSemana { SEGUNDA, TERCA, QUARTA, QUINTA, SEXTA, SABADO, DOMINGO }
void main(){
//exibindo a lista inteira
print(DiaSemana.values);
DiaSemana dia = DiaSemana.SEGUNDA;
//exibindo o nome desse valor
print(dia.name);
switch (dia){
case DiaSemana.SEGUNDA:
print("Nããããããããooooooooooooo!!!!!!");
case DiaSemana.TERCA:
print(":(");
case DiaSemana.QUARTA:
print(":|");
case DiaSemana.QUINTA:
print(":)");
case DiaSemana.SEXTA:
print("Eeeeeeeeeeeeeeeeeeeee!!!!!!");
case DiaSemana.SABADO:
print("=DDDDDD");
case DiaSemana.DOMINGO:
print(" :):");
}
}
4. Instalação do SDK, VS Code, pasta e teste inicial
Passaremos a utilizar uma instalação local de Dart e editaremos código utilizando o VS Code. O SDK de Dart pode ser obtido a partir do seguinte link.
Se tiver planos de utilizar o framework Flutter, pode ser boa ideia fazer a sua instalação. Inclusive, ela já inclui o SDK de Dart, já que Flutter é escrito em Dart. Por isso, se optar por instalar o Flutter, não precisará instalar o Dart à parte. Flutter pode ser obtido em
https://docs.flutter.dev/get-started/install
A seguir, crie uma pasta para abrir os seus projetos. Se estiver usando o Windows, pode ser interessante usar algo como
C:\Users\seuUsuario\Documents\projetos_dart\mais_testes
A seguir, abra o VS Code e clique File >> Open Folder. Navegue até a pasta e vincule o VS Code a ela. No VS Code, crie um arquivo chamado testes.dart. Claro, o nome pode ser qualquer um.
É recomendável fazer a instalação da extensão Dart para o VS Code. Para tal, abra a seção Extensions dele e busque por Dart.

A extensão Dart para o VS Code.
Caso vá desenvolver com Flutter, também é interessante fazer a instalação da extensão própria para esse arcabouço. Observe que se optar por fazê-lo, estará instalando também a extensão Dart, pois ela é uma dependência da extensão Flutter.

A extensão Flutter para o VS Code.
Abra o arquivo testes.dart no VS Code e use o seguinte código para testar o seu ambiente.
testes.dart
void main(){
print('Hello, Dart');
}
Clique Terminal >> New Terminal para abrir um terminal interno do VS Code. Use
Terminal
dart testes.dart
para fazer o teste. Veja o resultado esperado.

Resultado da execução de testes.dart no terminal.
5. Entrada e saída de dados
Os objetos stdin e stdout representam, respectivamente, a entrada e a saída padrão. Eles fazem parte do pacote dart:io. Podemos utilizá-los para interagir com o usuário.
import 'dart:io';
void main() {
// Exibe o texto e pula uma linha
stdout.writeln('Digite o seu nome: ');
// Lê o que o usuário digitou
//devolve uma string
var nome = stdin.readLineSync();
// Exibe o texto sem pular uma linha
stdout.write('Olá, $nome!');
}

No Debug Console, a leitura com stdin causa erro.
Se desejar, é possível alterar o ambiente de execução utilizado pela extensão. Podemos deixar de utilizar o debug console e passar a utilizar o terminal padrão. Com isso
- perdemos o suporte a debug oferecido pelo debug console
- podemos capturar dados no terminal usando o pacote
dart:ioainda que executemos a aplicação usando os atalhos visuais da extensão.
Essa configuração pode ser feita clicando-se em File >> Preferences >> Settings. Depois disso, clique em Extensions >> Dart >> Run and Debug. Encontre a opção Cli Console à direita e troque para terminal. Observe.

Configurando a opção Cli Console para usar o terminal.
Se desejar, também é possível não fazer alteração alguma e, quando for necessário capturar dados usando o pacote dart:io, executar o programa com dart arquivo.dart direto no terminal, em vez de utilizar os atalhos gráficos da extensão.
Lidando com números e com null
Observe que o método de captura de dados devolve uma String. Se for necessário lidar com números, é preciso fazer a conversão usando os métodos parse de int, num e/ou double.
import 'dart:io';
void main() {
//erro, precisa converter antes
int idade = stdin.readLineSync();
}
Pode ser tentador fazer a seguinte conversão. Mas ela também causa erro.
import 'dart:io';
void main() {
//erro
//estamos tentando entregar String? para parse
//ele somente recebe String
int idade = int.parse(stdin.readLineSync());
}
Isso acontece pois
- O método
readLineSyncdevolveString?. É um tipo que representa uma string ou null. - O método
parserecebeString. É um tipo que representa uma string necessariamente diferente de null. Ou seja, é um erro tentar passar null ao método parse. Algo assim:int.parse(null);
E agora? Você pode fazer validações com estruturas de seleção e/ou repetição, dependendo do que deseja.
import 'dart:io';
void main() {
print('Digite sua idade: ');
//String? admite uma string ou null
String? entrada = stdin.readLineSync();
//validação com if else
if (entrada != null) {
int idade = int.parse(entrada);
print("Você tem $idade anos");
}
//validação com while
while (entrada == null) {
print('Digite sua idade: ');
entrada = stdin.readLineSync();
}
int idade = int.parse(entrada);
print("Você tem $idade anos");
}
Outra opção é usar o operador !. Ele é uma promessa de que a expressão sobre a qual opera é diferente de null. Mas observe que estamos nos sujeitando a um erro envolvendo null em tempo de execução.
import 'dart:io';
void main() {
print('Digite sua idade: ');
//o operador ! opera sobre a expressão que o antecede
//neste caso, o retorno do método readLineSync()
//estamos prometendo que o valor não será nulo para o compilador
//ele fala: blz, você se vira com a Dart VM
int idade = int.parse(stdin.readLineSync()!);
print(idade);
}
6. Casas decimais e valores aleatórios
Controlando o número de casas decimais
Podemos controlar o número de casas decimais usando o método toStringAsFixed.
void main() {
//com um número fixo
print('${154.3434234.toStringAsFixed(2)}');
//com uma variável
var variavel = 2.3134353543;
print('${variavel.toStringAsFixed(2)}');
}
Valores aleatórios
A geração de valores aleatórios pode ser feita por métodos da classe Random.
import 'dart:math';
void main() {
var gerador = Random();
//inteiro aleatório entre 0 e 5
var n1 = gerador.nextInt(6);
print(n1);
//inteiro aleatório entre 1 e 10
var n2 = gerador.nextInt(10) + 1;
print(n2);
//inteiro aleatório entre 0 e 100
var n3 = gerador.nextInt(101);
print(n3);
//real aleatório entre 0 e 1 ([0, 1))
var n4 = gerador.nextDouble();
print(n4);
//real aleatório entre 0 e 100 ([0, 100))
var n5 = gerador.nextDouble() * 100;
print(n5);
//real aleatório entre 100 e 150
var n6 = gerador.nextDouble() * 50 + 100;
print(n6);
//booleano aleatório
var n7 = gerador.nextBool();
print(n7);
}
7. Pedra, papel e tesoura: criando o projeto
Utilizemos os recursos vistos até então, ou grande parte deles, para fazer a implementação de um clássico Papel, pedra e tesoura. Para isso, vamos criar uma nova aplicação. Desta vez, ela será um projeto Dart "completo". Para tal, abra um terminal vinculado à sua pasta de projetos Dart (fora de qualquer projeto específico) e use
Terminal
dart create pedra_papel_tesoura -t console
para criar a aplicação.
Depois de criar o projeto, abra o VS Code e clique em File >> Open Folder. Abra a pasta que acaba de ser criada. Veja o resultado esperado.

Estrutura do projeto criado com dart create.
Veja uma descrição sucinta de cada item.
- .dart_tool: pasta que contém arquivos utilizados por várias ferramentas Dart.
- bin: pasta que contém o arquivo
.dartprincipal. A ideia é que definamos a funçãomainali. - lib: pasta que contém arquivos
.dartem geral. É comum fazermos a implementação da aplicação nesta pasta e em subpastas dela. - test: pasta que contém arquivos de teste. Eles podem ser executados com
dart test. - .gitignore: o clássico arquivo que o Git utiliza para decidir quais arquivos não incluir no repositório
- analysis_options.yaml: arquivo de configurações utilizado para, por exemplo, configurar o linter do Dart.
- CHANGELOG.md: um arquivo escrito em Markdown para registrar as alterações feitas na aplicação ao longo do tempo.
- pubspec.lock: utilizamos para garantir que todos os desenvolvedores de um projeto estão utilizando as mesmas versões de cada dependência. Veja mais aqui: https://medium.com/codingmountain-blog/what-is-pubspec-lock-and-why-should-flutter-devs-care-86ab4d0b47fc
- pubspec.yaml: especificamos as dependências do projeto aqui.
- README.md: arquivo em markdown que descreve aspectos do projeto, como ele pode ser executado etc.
8. Pedra, papel e tesoura: estrutura e funções auxiliares
Sua vez! Tente fazer o jogo, com a ajuda do seu professor. Seja criativo! Faça validações, use enum, variações de switch/case etc.
Veja uma possível implementação. Observe que usamos alguns recursos a serem estudados mais a fundo em breve.
Abra o arquivo pedra_papel_tesoura.dart da pasta lib. Apague todo o seu conteúdo e substitua pela seguinte função. Ela representa o nosso jogo.
lib/pedra_papel_tesoura.dart
void jogo(){
}
A seguir, abra o arquivo pedra_papel_tesoura.dart da pasta bin e ajuste seu conteúdo como a seguir.
bin/pedra_papel_tesoura.dart
//show indica que queremos apenas aquele membro
import 'package:pedra_papel_tesoura/pedra_papel_tesoura.dart' show jogo;
void main(List<String> arguments) {
jogo();
}
A seguir, no arquivo pedra_papel_tesoura.dart da pasta lib, importamos os pacotes
dart:iopara capturar as opções do usuáriodart:mathpara gerar valores aleatórios, representando a opção do usuário
lib/pedra_papel_tesoura.dart
import 'dart:io';
import 'dart:math';
Escrevemos um enum para representar as opções do jogo, incluindo a opção de sair.
lib/pedra_papel_tesoura.dart
import 'dart:io';
import 'dart:math';
enum OPCAO { pedra, papel, tesoura, sair }
Na função jogo, vamos
- escrever um loop que executa até que o usuário digite 4, indicando que deseja sair.
- a cada iteração, usar a função
sleepalimentada com uma instância deDurationpara que, a cada iteração, aguardemos 3 segundos.
Os comentários na função jogo indicam as funções que precisamos implementar para obter a implementação completa do jogo, aplicando o paradigma da divisão e conquista: cada pedacinho vai ser implementado por uma função separada. Observe que haverá um erro de compilação temporário já que a variável que representa a opção do usuário ainda não foi inicializada. Nada para se preocupar. Ela será inicializada na hora certa.
lib/pedra_papel_tesoura.dart
import 'dart:io';
import 'dart:math';
enum OPCAO { pedra, papel, tesoura, sair }
void jogo() {
int opUsuario;
do {
//exibir menu
//capturar opção do usuário, validando
//se o usuário digitar 4, sair
//senão
//sortear escolha do computador
//mapear opcao usuario, de int para enum
//mapear opcao computador, de int para enum
//exibir as opções de cada um
//decidir quem venceu, ou se houve empate
//exibir o resultado
sleep(Duration(seconds: 3));
} while (opUsuario != 4);
}
Embora não seja estritamente necessário, vamos escrever uma função que exibe algo recebido como parâmetro, essencialmente operando da mesma forma que a função print. De fato, ela usa a função print para cumprir a sua missão.
lib/pedra_papel_tesoura.dart
import 'dart:io';
import 'dart:math';
enum OPCAO { pedra, papel, tesoura, sair }
void exibe(String texto) {
print(texto);
}
void jogo() {
...
A seguir, escrevemos uma função responsável por capturar a opção do usuário.
lib/pedra_papel_tesoura.dart
import 'dart:io';
import 'dart:math';
enum OPCAO { pedra, papel, tesoura, sair }
void exibe(String texto) {
print(texto);
}
int pegaOpcaoUsuario() {
return int.parse(stdin.readLineSync()!);
}
void jogo() {
...
A função a seguir recebe uma opção e verifica se ela está no intervalo válido.
lib/pedra_papel_tesoura.dart
import 'dart:io';
import 'dart:math';
enum OPCAO { pedra, papel, tesoura, sair }
void exibe(String texto) {
print(texto);
}
int pegaOpcaoUsuario() {
return int.parse(stdin.readLineSync()!);
}
bool opcaoEhValida(int opcao) {
return opcao >= 1 && opcao <= 4;
}
void jogo() {
...
Uma vez que tenhamos uma opção em mãos, representada como um número inteiro, precisaremos fazer um mapeamento de int para enum. Lembre-se que o índice do enum começa a partir do zero (pedra = 0, papel = 1 etc). Para tornar intuitivo para o usuário, deixamos ele fazer a sua escolha no intervalo de 1 a 4. Por isso, subtraímos 1 na hora de indexar o enum.
lib/pedra_papel_tesoura.dart
import 'dart:io';
import 'dart:math';
enum OPCAO { pedra, papel, tesoura, sair }
void exibe(String texto) {
print(texto);
}
int pegaOpcaoUsuario() {
return int.parse(stdin.readLineSync()!);
}
bool opcaoEhValida(int opcao) {
return opcao >= 1 && opcao <= 4;
}
OPCAO mapeiaOpcao(int opcao) {
return OPCAO.values[opcao - 1];
}
void jogo() {
...
A próxima função verifica se houve empate. Se não houve, ela diz quem venceu.
lib/pedra_papel_tesoura.dart
...
OPCAO mapeiaOpcao(int opcao) {
return OPCAO.values[opcao - 1];
}
String decideResultado(OPCAO opcaoUsuario, OPCAO opcaoComputador) {
if (opcaoUsuario == opcaoComputador) return "Empate";
if (opcaoUsuario == OPCAO.papel && opcaoComputador == OPCAO.pedra ||
opcaoUsuario == OPCAO.pedra && opcaoComputador == OPCAO.tesoura ||
opcaoUsuario == OPCAO.tesoura && opcaoComputador == OPCAO.papel) {
return "Você venceu";
}
return "Computador venceu";
}
void jogo() {
...
9. Pedra, papel e tesoura: implementando o jogo
Agora podemos começar a implementar o jogo. Começamos exibindo o menu ao usuário, capturando a sua opção e verificando se ele já deseja sair, por ter digitado o valor 4.
lib/pedra_papel_tesoura.dart
...
void jogo() {
int opUsuario;
do {
//exibir menu
//capturar opção do usuário, validando
do {
exibe('1-Pedra\n2-Papel\n3-Tesoura\n4-Sair');
opUsuario = pegaOpcaoUsuario();
} while (!opcaoEhValida(opUsuario));
//se o usuário digitar 4, sair
//senão
if (opUsuario != 4){
//sortear escolha do computador
//mapear opcao usuario, de int para enum
//mapear opcao computador, de int para enum
//exibir as opções de cada um
//decidir quem venceu, ou se houve empate
//exibir o resultado
sleep(Duration(seconds: 3));
}
} while (opUsuario != 4);
exibe('Até logo');
}
Verifique se está tudo ok executando o programa com
Terminal
dart run
A escolha do computador pode ser simulada pelo método nextInt da classe Random.
lib/pedra_papel_tesoura.dart
void jogo() {
int opUsuario;
do {
//exibir menu
//capturar opção do usuário, validando
do {
exibe('1-Pedra\n2-Papel\n3-Tesoura\n4-Sair');
opUsuario = pegaOpcaoUsuario();
} while (!opcaoEhValida(opUsuario));
//se o usuário digitar 4, sair
//senão
if (opUsuario != 4){
//sortear escolha do computador
int opComputador = Random().nextInt(3) + 1;
//mapear opcao usuario, de int para enum
//mapear opcao computador, de int para enum
//exibir as opções de cada um
//decidir quem venceu, ou se houve empate
//exibir o resultado
sleep(Duration(seconds: 3));
}
} while (opUsuario != 4);
exibe('Até logo');
}
O próximo passo é traduzir as opções do usuário e do computador, fazendo uso do enum. Podemos também exibí-las.
lib/pedra_papel_tesoura.dart
void jogo() {
int opUsuario;
do {
//exibir menu
//capturar opção do usuário, validando
do {
exibe('1-Pedra\n2-Papel\n3-Tesoura\n4-Sair');
opUsuario = pegaOpcaoUsuario();
} while (!opcaoEhValida(opUsuario));
//se o usuário digitar 4, sair
//senão
if (opUsuario != 4){
//sortear escolha do computador
int opComputador = Random().nextInt(3) + 1;
//mapear opcao usuario, de int para enum
OPCAO opcaoUsuario = mapeiaOpcao(opUsuario);
//mapear opcao computador, de int para enum
OPCAO opcaoComputador = mapeiaOpcao(opComputador);
//exibir as opções de cada um
exibe('Você (${opcaoUsuario.name}) vs (${opcaoComputador.name}) Computador');
//decidir quem venceu, ou se houve empate
//exibir o resultado
sleep(Duration(seconds: 3));
}
} while (opUsuario != 4);
exibe('Até logo');
}
Execute novamente com
Terminal
dart run
A seguir, resta
- decidir o vencedor (ou se houve empate)
- exibir o resultado
- exibir
***********************para separar uma rodada da outra
lib/pedra_papel_tesoura.dart
void jogo() {
int opUsuario;
do {
//exibir menu
//capturar opção do usuário, validando
do {
exibe('1-Pedra\n2-Papel\n3-Tesoura\n4-Sair');
opUsuario = pegaOpcaoUsuario();
} while (!opcaoEhValida(opUsuario));
//se o usuário digitar 4, sair
//senão
if (opUsuario != 4){
//sortear escolha do computador
int opComputador = Random().nextInt(3) + 1;
//mapear opcao usuario, de int para enum
OPCAO opcaoUsuario = mapeiaOpcao(opUsuario);
//mapear opcao computador, de int para enum
OPCAO opcaoComputador = mapeiaOpcao(opComputador);
//exibir as opções de cada um
exibe('Você (${opcaoUsuario.name}) vs (${opcaoComputador.name}) Computador');
//decidir quem venceu, ou se houve empate
String vencedor = decideResultado(opcaoUsuario, opcaoComputador);
//exibir o resultado
exibe(vencedor);
exibe('***********************************');
sleep(Duration(seconds: 3));
}
} while (opUsuario != 4);
exibe('Até logo');
}
Faça novos testes com
Terminal
dart run
10. Encerramento
Parabéns! Você usou as estruturas de repetição, criou enums, configurou o ambiente local com o SDK de Dart e o VS Code, capturou dados com dart:io, gerou valores aleatórios e implementou um jogo de pedra, papel e tesoura aplicando divisão e conquista.
Próximos passos
Siga para a parte 3 da série, que trata das coleções de Dart: listas, tuplas (records), conjuntos e mapas.
Referências
- Dart programming language | Dart. Google, 2023. Disponível em https://dart.dev/. Acesso em agosto de 2023.