Mobile App Development · Dart/Flutter

22 · Nullability Safety — Exercícios

Seis exercícios progressivos: tipo nullable básico, ??, ?., promoção de tipo, o operador ! e late.

Exercício 1

exercicio_01_nullable_basico.dart

Declara uma variável String? apelido e exibe seu valor antes (implicitamente null) e depois de receber um texto, mostrando a diferença entre um tipo nullable sem valor e com valor atribuído.

// Demonstração de Null Safety básico em Dart.
// O Dart possui Null Safety ativado por padrão: variáveis não-nullable
// não podem conter null. Para permitir null, usa-se '?' após o tipo.
void main() {
  // String? declara uma variável que pode ser String ou null.
  // Sem inicialização, o valor padrão é null.
  String? apelido;
  // Exibe 'null' porque a variável ainda não foi atribuída.
  print('Apelido inicial: $apelido');

  // Agora atribuímos um valor String concreto à variável nullable.
  apelido = 'Aninha';
  // Exibe 'Aninha' — a variável agora contém um valor não-null.
  print('Apelido atualizado: $apelido');
}

Saída esperada

Apelido inicial: null
Apelido atualizado: Aninha

Como executar

dart run 22-Nullability_Safety/02_Exercicios/codigo_fonte/exercicio_01_nullable_basico.dart

Exercício 2

exercicio_02_operador_coalescencia.dart

Usa o operador ?? para exibir uma mensagem padrão quando apelido for null, equivalente a apelido != null ? apelido : 'Sem apelido', mas de forma mais direta.

// Demonstração do operador de coalescência nula '??' em Dart.
// O operador ?? retorna o valor à esquerda se ele não for null,
// ou o valor à direita como fallback caso o valor seja null.
void main() {
  // 'apelido' é null — nenhum valor foi atribuído.
  String? apelido;
  // ?? verifica: se 'apelido' for null, usa 'Sem apelido' como substituto.
  // Equivalente a: apelido != null ? apelido : 'Sem apelido'
  String nomeExibicao = apelido ?? 'Sem apelido';

  // Exibe 'Sem apelido' porque apelido é null.
  print(nomeExibicao);
}

Saída esperada

Sem apelido

Como executar

dart run 22-Nullability_Safety/02_Exercicios/codigo_fonte/exercicio_02_operador_coalescencia.dart

Exercício 3

exercicio_03_acesso_seguro.dart

Usa o operador ?. (safe call) para chamar toUpperCase() em uma variável nullable. Quando o valor é null, a expressão retorna null em vez de lançar uma exceção; quando não é, o método é chamado normalmente.

// Demonstração do operador de acesso seguro '?.' (safe call) em Dart.
// '?.' chama um método ou acessa uma propriedade somente se o valor não for null.
// Se o valor for null, a expressão retorna null em vez de lançar uma exceção.

void main() {
  // Busca sem nome: a função retorna null.
  String? nome = buscarNome(incluirNome: false);
  // nome?.toUpperCase() — se nome for null, retorna null sem chamar toUpperCase().
  // Saída esperada: 'null'
  print(nome?.toUpperCase());

  // Busca com nome: a função retorna 'ana'.
  nome = buscarNome(incluirNome: true);
  // nome?.toUpperCase() — nome não é null, então toUpperCase() é chamado.
  // Saída esperada: 'ANA'
  print(nome?.toUpperCase());
}

// Retorna uma String se 'incluirNome' for true, ou null caso contrário.
String? buscarNome({required bool incluirNome}) {
  return incluirNome ? 'ana' : null;
}

Saída esperada

null
ANA

Como executar

dart run 22-Nullability_Safety/02_Exercicios/codigo_fonte/exercicio_03_acesso_seguro.dart

Exercício 4

exercicio_04_promocao_tipo.dart

Verifica cidade != null em um if e, dentro do bloco, o Dart promove cidade de String? para String automaticamente — dispensando ?. ou ! para chamar toUpperCase().

// Demonstração de promoção de tipo (type promotion) em Dart.
// Quando verificamos que uma variável nullable não é null com 'if (x != null)',
// o Dart automaticamente trata a variável como não-nullable dentro do bloco if.
// Isso elimina a necessidade de usar '?.' ou '!' após a verificação.
void main() {
  // 'cidade' pode ser null — tipo String?.
  String? cidade = buscarCidade();

  // Dentro do bloco if, após verificar 'cidade != null',
  // o Dart promove 'cidade' para String (não-nullable).
  // Portanto, podemos chamar toUpperCase() diretamente sem '?.'.
  if (cidade != null) {
    print('Cidade: ${cidade.toUpperCase()}');
  } else {
    print('Cidade nao informada');
  }
}

// Retorna uma cidade como String, mas poderia retornar null em outro contexto.
String? buscarCidade() {
  return 'Curitiba';
}

Saída esperada

Cidade: CURITIBA

Como executar

dart run 22-Nullability_Safety/02_Exercicios/codigo_fonte/exercicio_04_promocao_tipo.dart

Exercício 5

exercicio_05_operador_bang.dart

Mostra um caso seguro do operador !: só é usado depois de verificar if (dados['nome'] != null), convertendo String? em String naquele ponto para chamar toUpperCase().

// Demonstração do operador bang '!' (non-null assertion) em Dart.
// O operador '!' afirma ao compilador que o valor NÃO é null naquele ponto.
// ATENÇÃO: se o valor for null em runtime, lança NullReferenceError.
// Use '!' apenas quando tiver certeza absoluta de que o valor não é null.
void main() {
  // Map com uma chave 'nome' que tem valor String não-null.
  Map<String, String?> dados = {'nome': 'Ana'};

  // dados['nome'] retorna String? (pode ser null se a chave não existir).
  // Verificamos com 'if (dados['nome'] != null)' antes de usar '!'.
  if (dados['nome'] != null) {
    // '!' converte String? para String dentro do bloco (garantido não ser null).
    // Sem '!', o compilador recusaria chamar toUpperCase() em String?.
    print(dados['nome']!.toUpperCase());
  }
}

Saída esperada

ANA

Como executar

dart run 22-Nullability_Safety/02_Exercicios/codigo_fonte/exercicio_05_operador_bang.dart

Exercício 6

exercicio_06_late.dart

Cria a classe Configuracao com o campo late String ambiente, inicializado fora do construtor pelo método inicializar(). Demonstra como adiar a inicialização de um campo não-nullable até um momento posterior garantido.

// Demonstração da palavra-chave 'late' em Dart.
// 'late' indica que uma variável não-nullable será inicializada
// antes do primeiro uso — mas não necessariamente no construtor.
// Evita ter que usar null para variáveis que serão inicializadas depois.

class Configuracao {
  // 'late' adia a inicialização de 'ambiente' para um momento posterior.
  // Sem 'late', o Dart exigiria valor inicial ou permitir null (String?).
  // Se 'ambiente' for acessado antes de ser inicializado, lança LateInitializationError.
  late String ambiente;

  // Método que inicializa 'ambiente' após a criação do objeto.
  void inicializar() {
    ambiente = 'desenvolvimento';
  }
}

void main() {
  Configuracao configuracao = Configuracao();
  // Inicializa 'ambiente' antes de acessá-lo — necessário com 'late'.
  configuracao.inicializar();
  // Acessa 'ambiente' com segurança: já foi inicializado pelo método inicializar().
  print('Ambiente: ${configuracao.ambiente}');
}

Saída esperada

Ambiente: desenvolvimento

Como executar

dart run 22-Nullability_Safety/02_Exercicios/codigo_fonte/exercicio_06_late.dart