Flutter - Null Safety

Kako (Caio Couto Moreira)
Kako (Caio Couto Moreira)

Compartilhe

logo flutter

A partir do Dart 2.12, temos a possibilidade de usar o Null Safety para facilitar nossas aplicações, dificultando erros de valores nulos. Aprenderemos a identificar erros de valores nulos, aplicar no nosso projeto do ByteBank, atualizar o Dart para a nova versão e migrar um projeto inteiro! Assim podemos criar códigos complexos com mais facilidade!

Erros de valor Null

Existem infinitos erros que podem acontecer por conta de um valor nulo:

  • Você pode estar esperando um dado do seu back-end, mas ele não existe ainda…
  • Você pode criar uma lista que muda de tamanho de acordo com a quantidade de produtos…

Vamos dar uma olhada no nosso antigo conhecido: o ByteBank. Nele, temos uma lista de transferências que depende da quantidade de transferências disponíveis no nosso servidor API.

Imagem da lista de transações no aplicativo, exibindo duas transações de $200 para a conta número 1000

return ListView.builder(
 itemBuilder: (context, index) {
   final Transaction transaction = transactions[index];
   return Card(
     child: ListTile(
       leading: Icon(Icons.monetization_on),
       title: Text(
         transaction.value.toString(),
         style: TextStyle(
           fontSize: 24.0,
           fontWeight: FontWeight.bold,
         ),
       ),
       subtitle: Text(
         transaction.contact.accountNumber.toString(),
         style: TextStyle(
           fontSize: 16.0,
         ),
       ),
     ),
   );
 },
 itemCount: transactions.length, /// não pode ser nulo
);

Mas e se não tivéssemos nenhuma transferência?

imagem do erro no aplicativo, que é exibido em uma tela vermelha

Nesse caso, nossas transferências teriam o valor null e a ListView não poderia ser buildada, causando a famosa Tela Vermelha.

Para resolver isso, no passado, usávamos uma condição que ignorava o valor nulo.

if(snapshot.hasData){ // condição inicial para impedir o nulo
 final List<Transaction> transactions = snapshot.data;
 if(transactions.isNotEmpty){ // segunda condição para impedir o nulo
   return ListView.builder(...);

 }
}
return CenteredMessage('No Transactions Found', icon: Icons.warning); // caso seja nulo, buildar essa mensagem
   break;
}
return CenteredMessage('Unknown Error'); // caso seja nulo, buildar essa mensagem

Aplicação do Null Safety

Agora, as coisas funcionam de uma forma levemente diferente. Por padrão, toda variável que criamos não pode receber valor nulo.

código int cafe = null exibindo a mensagem que avisa que o valor não pode ser nulo

Mas e se quisermos ter um valor nulo? Existem alguns momentos em que precisamos lidar com valores nulos, então, como podemos garantir que nossa variável vai receber o valor nulo? Para esses casos, basta adicionar o sinal ? e, em seguida, o tipo da variável.

int? cafe = null;

Existem casos extremos em que precisamos ficar alterando a propriedade da nossa variável. Imagina o seguinte: você está fazendo uma função, e ela não pode receber um valor nulo, mas, por algum motivo, um dos atributos dela veio nulo. O que podemos fazer para garantir que esse valor vai ser transformado em não nulo?

class Cafe {
  String _temperatura; // criei a Temperatura

  void esquentar() { _temperatura = 'quente'; } // Defino a Temperatura para quente

  void esfriar() { _temperatura = 'gelado'; }

  String servir() => _temperatura + ' cafe'; // Servir o Cafe com a Temperatura 
}

main() {
  var cafe = Cafe(); // Fazer o Cafe
  cafe.esquentar(); // Esquentar o Cafe
  cafe.servir(); // Servir o Cafe sem esperar que ele já esteja quente
}

Note que, no momento em que estamos servindo, estamos esquentando! Nossa função não espera o _temperatura ser = ‘quente’ antes de servir… Ou seja, se for rápida o suficiente, ela pode entregar o café com a _temperatura = ‘null’

Para esses casos, basta adicionar o sinal ! no tipo da variável.

class Cafe {
  String? _temperatura; // Criar a Temperatura que não pode ser nula

  void esquentar() { _temperatura = 'quente'; } // Definir a Temperatura para quente

  void esfriar() { _temperatura = 'gelado'; }

  String servir() => _temperatura! + ' cafe'; // Servir o Cafe com a Temperatura que pode ser nula.
}

Mas já dá pra notar que isso não é uma boa prática, né? Afinal, ficar colocando ? e ! a torto e a direito no código pode confundir qualquer pessoa!

Existe um novo modificador chamado late, que permite que o valor inicialmente seja nulo, mas, quando for utilizado em alguma função ou classe, ele vai ser considerado não nulo.

class Cafe {
  late String _temperatura; // criei a Temperatura

  void esquentar() { _temperatura = 'quente'; } // Definir a Temperatura para quente

  void esfriar() { _temperatura = 'gelado'; }

  String servir() => _temperatura + ' cafe'; // Servir o Cafe com a Temperatura só depois que a _temperatura for diferente de nula.
}

O que acontece é que o late pede para a _temperatura ser usada na função somente quando ela já tiver deixado de ser null.

Migração

Vamos agora migrar um aplicativo para utilizar o Null Safety. Antes de tudo, temos de ter certeza de que tudo está nos conformes e para isso vamos usar o comando flutter doctor e dart --version no terminal para ver se temos tudo atualizado!

Resposta do comando flutter doctor, com o Flutter atualizado para a versão 2.0.3, o DART na versão 2.12.2 e sem nenhuma pendência

Para que tudo funcione, precisamos do Flutter na versão mínima 2.0.0 e o Dart na versão mínima 2.12.0. Caso você precise atualizar algum deles, utilize o comando flutter upgrade e dart upgrade, respectivamente.

Agora temos de atualizar nosso projeto!

Existem dois jeitos de migrar seu projeto. A grande maioria de nós vai tentar migrar um projeto do jeito manual:

  • Ir manualmente no pubspec.yaml e atualizar os pacotes um por um, “na mão”, depois substituir as depreciações uma a uma até que o projeto não tenha nenhum erro ou warning.

Devo alertar você que o jeito manual é muito trabalhoso e pode te dar algumas noites sem sono e alguns cabelos brancos. Então, vou te convencer a fazer de um outro jeito!

Primeiro passo: Verificar

Saber quando devemos atualizar nosso projeto é essencial! Para isso, verifique quais são os pacotes que você usa no seu projeto e se eles já têm a versão que aceite o Dart 2.12 (Null Safety).

Atente-se aos pacotes que dependem de outros pacotes! Okay, mas como vou saber se meus pacotes têm suporte pro Null Safety? Simples, para isso basta usarmos o comando no terminal: dart pub outdated --mode=null-safety

código

Esse comando vai analisar cada um dos seus pacotes e confirmar se eles já aceitam a Migração! (Bem melhor do que sair procurando cada um no Google, não é?)

Caso seus pacotes tenham suporte para o Null Safety, basta usar dois comandos simples para atualizá-los!

dart pub upgrade --null-safety e

dart pub get

Segundo passo: Migrar

Agora nós temos o terreno pronto, então, vamos começar a migração! A primeira coisa a se fazer é lançar o comando:

dart migrate

Em seguida, ele vai te mandar um link que você deve acessar pelo seu navegador:

View the migration suggestions by visiting: http://127.0.0.1:60278/Users/you/project/mypkg.console-simple?authToken=Xfz0jvpyeMI%3D

Já no navegador, você vai ver uma interface linda com o seu código atualizado!

Interface Interativa com o código do projeto alterado e edições propostas. Na barra superior da tela, lê-se Dart Propostas de alteração do null safety e três botões: voltar para fonte, fazer migração e ajuda. A tela está dividida em três partes: à esquerda estão as pastas e os arquivos do projeto, no centro está o código do arquivo selecionado; à direita são exibidas as edições propostas no código e também os detalhes da edição proposta

Calma, seu código não foi alterado ainda! Essa interface está apenas sugerindo as alterações. Antes de aplicar as alterações, vamos conferir se todos os valores que devem ser nulos estão especificados e se os valores não nulos estão corretos. Caso você precise fazer uma alteração vá em Edit Details e selecione o hint pertinente (?ou !). Uma vez que você conferiu todo o código e, estando tudo certo, clique em Apply migration. Pronto! Seu projeto está todo atualizado para o Null Safety!

Incrível, não é? Muito mais fácil do que ir manualmente em cada pacote e verificar todas as variáveis no projeto a fim de encontrar algum que aceite o valor nulo, não acha?

Conclusão

Aprendemos bastante sobre o Null Safety e como ele funciona. Descobrimos a sua importância e entendemos as diferenças entre ? , ! e o late.

Vimos como atualizar o Dart e como ter certeza se podemos migrar o nosso projeto para aceitar o Null Safety. Utilizamos uma nova ferramenta que faz alterações inteligentes no nosso código – tudo isso de um jeito leve e rápido. Por fim, percebemos que é muito melhor usar ferramentas que fazem o trabalho pesado por nós, em vez de atualizar o código manualmente.

Referências

Veja outros artigos sobre Mobile