O comportamento de variáveis na memória ao longo do tempo pode assumir dois estados bem simples, mas relevante do ponto de visto do controle de fluxo de dados.

Se ao enviar uma variável a uma função e a função assume um novo endereço de memória, nomeamos a passagem desta variável como “by value” (passagem por valor). De outro lado se a função recebe a variável e manipula seu valor no mesmo endereço de memória de origem, então a passagem da variável foi “by reference” (por referência).

Quando uma variáviel é passada para a função por referência, as alterações de seus valores (ou valor) será propagado para o mesmo endereço ou seja, quem chamou a variável também passará a acessar o valor alterado.

Em muitos casos este comportamento é fundamental para fazer controle de valores dos dados onde as alterações são propagadas para todos os pontos de seu uso, mas… nem sempre isto é o desejado – as vezes o desejado é fazer um simples clone dos valores e armazenado em outro endereço sem afetar a valor original.

Como Dart trata – Primitivos x Objetos

Há uma separação no Dart em duas formas básicas: a) tipos primitivos, como “int”, “String”, “double”, “num”; b) objetos;

Os tipos primitivos ao serem enviados como parâmetro de função/método são passados por valor, ou seja, a função recebe um novo endereço de memória. De outro lado, os objetos de classes são passador por referência (o mesmo endereço);

Dilema Referência X Valor

Se dados primitivos são passados por valor, então como posso passar esses valores por referência ? A resposta é trivial, mas nem sempre tão evidente – encapsula o dado primitivo em uma classe e passa o objeto desta classe.

Ex: criando a classe que irá encapsular o valor primitivo

class MinhaClasse{
  double valor;
  MinhaClasse(this.valor);
}

Mas… sempre tem um mas, tenho um objeto e desejo que as informações de origem sejam mantidas sem interferência das manipulações que a função o fez – Hummm, você vai precisa fazer um CLONE da classe gerando uma nova instância da mesma classe e colando os seus valores, então agora, pode passar a segunda instância para a função, garantindo que os dados originais não serão afetados.

ex: usando “MinhaClasse”

/// valor original
class MinhaClasse{  
    double valor;  
    MinhaClasse(this.valor);
   /// gera uma nava instancia com o mesmo valor de origem
    clone(){
        return MinhaClasse(valor);  
       
    }
}    

classe.copyWith

Várias classes de Flutter/Dart possuem uma função “copyWith” que gera uma cópia da classe permitindo por parâmetro alterar algumas de suas variáveis.

Um exemplo desta implementação é possível encontrar em ThemeData, onde o copyWith gera um nova instância e ainda permite passar valores alterando seu valores originais.

ex: alterando “primaryColor” para os próximos widgets

/// trocando o primaryColor
return  Theme(
         data:      Theme.of(context).copyWith(primaryColor:Colors.green),
      child: Container(.....)
 );

No exemplo, copyWith irá gerar novos valores para o “Theme” sem alterar os valores originais, funcionando como um “CLONE” parametrizável.