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.

Quantas vezes você gostaria de ter um novo método em uma classe padrão? Certamente não foram poucas e se já estiver a algum tempo trabalhando com Flutter/Dart. Helpers como também pode ser chamada em outras linguagens de programação, são implementações relativamente novas entre as linguagens e se propõe ir além da estrutura original desenhada pelo autor da classe.

Dart introduziu “extension” no ano passado (2019) e seu uso vem sendo ampliado pelos criadores de “package” como é o exemplo do pacote “Provider” que estendeu o BuildContext para incluír métodos read, watch, select como mecanismo para obter ou escutar alteração nos valores armazenados nas classes gerenciadas pelo “Provider”.

Anatomia de “extension” – a simplicidade

extension [novo nome] on [classe] {
}

Neste ponto os métodos que forem implementados na nova classe ira estender a referência a classe original, talvez fique mais claro com alguns exemplos.

extension StringExtension on String{ 
   int parseInt()=>int.parse(this);
}

A classe “StringExtension” agora estende a classe “String” para incluír o método “parseInt()” que converte uma string em int:

  var texto = '123';
  /// converte para int
  int  i =  texto.parseInt();
  print(i);

Buscando solução para chamadas complexas ou encadeadas onde é necessário retornar um conteúdo para o chamador de uma rotina com o resultado.

Se temos 2 ou mais chamadas encadeadas para um processo e queremos que o resultado retorne para o primeiro – temos para isto a possibilidade de propagar eventos em objetos ou passar parâmetros como um objeto no todo – creio que é mais comum entre os developers.

Penso para estes caso o uso de “anonimous” pode ser uma solução de fácil implementação não exigindo criar um estrutura complexa de eventos ou objetos cabendo perfeitamente como solução.

Uma das características de anônimos é sua passagem como parâmetro em um procedimento/função e poder esperar um retorno ou um chamada de volta, ou seja, um “callback” de retorno (é um ponteiro que passamos para a chamada e ele é chamado de volta pelo procedimento chamado – callback).

Neste caso precisamos de um procedimento que esteja esperando um tipo de paramento, no caso um “anonimous” ex:

     /// criando um procedimento que aceita um callback 
     TUmObjeto.executar( aCallBack:TProc<String> );
          begin
            // faz alguma coisa
            // chama o callback
            aCallBack( 'ok' );
          end;

na chamada do procedimento podemos então fazer:

/// chamando o procedimento e passando como parâmentro um procedimento "anonimous"
  umObjeto.executar(
     procedure (aDado:String)
        begin
           //.... faz alguma coisa
        end);

O Delphi faz diferenciação de assinaturas entre “procecures” e “functions”, onde as functions retornam algum valor enquanto as procedures não retorna nada (algo equivalente a “void” em outras linguagens);

Sim, é possível usar uma “function” como anônimo, veja:

    /// para procedure:     TProc, ou TProc<tipo>
    /// para function:       TFunc<boolean> ou  TFunc<tipo,boolean>

</tipo,boolean></boolean></tipo>

No caso de TFunc o último parâmetro é sempre o tipo a ser retornado pela “function”;

Codificando no Browser

Um pouco antes de sair de férias peguei uma dica de um youtuber que falava sobre possibilidade de desenvolver  diretamente no browser utilizando uma estrutura de docker. Para quem vem do desktop, aquilo parecia muito estranho e me chamou a atenção.

Por outro lado, tenho alguns projetos em typescript e nodejs que seria perfeitamente aplicável esta ideia. Fui lá conferir o material e fiquei impressionado com facilidade ao utilizar a ferramenta.
De partida queria tentar usar “Flutter” lá na ferramenta, depois de muitas buscas no google cheguei a algumas soluções para configurar o docker a carregar as ferramentas necessária para o desafio que vou descrever aqui em passos a serem seguidos.

GitPod como ferramenta

1 – Ter uma conta no GitHub;
2 – instalar no chrome a extenção https://www.gitpod.io/docs/20_browser_extension/
3 – entrar na conta no GitHub, agora irá ver um novo botão ao lado dos seus projetos “GitPod”; executando o GitPod irá criar uma instância do docker para editar o código do seu projeto;

VSCode – no Docker

Ao criar o GitPod já irá iniciar um VSCode para você editar o código e já vem instalado com algumas extensões para sair usando. Vale a pena ler a documentação do GitPod para ver como que ele funciona, quando irá descobrir que eles não tiveram autorização para baixar as extensões automaticamente para o VSC. Não chega a ser um complicador, basta baixa a extensão desejada para a máquina local (download) e arrastá-la para sobre a pasta de extensões do VSC no browser e ele irá subir a extensão e fazer a instalação lá no docker.

Se estiver trabalhado com Typescript, javascript e outros, talvez já esteja pronto – já para flutter é preciso baixar o SDK e aquelas coisas que já estamos acostumados. Pensando em docker, é possível configurar para fazer o trabalho ao iniciar, ao que passarei a discorrer.

Se ainda não tem um projeto Flutter lá no GitHub, crie uma pasta nova vazia para ele. Se já tem, pode reaproveitá-la para criar o conteúdo necessário a configuração. Neste ponto você já entrou pelo botão do GitPod que aparece no GitHub e já está na máquina do docker, então vamos criar dois arquivos. Vai no terminal (prompt) do VSC e digite “gp init”, isto fará com que o GitPod crie dois novos arquivos na pasta.
Arquivo: .gitpod.yml

image:
  file: .gitpod.Dockerfile

tasks:
  - before: |
      export PATH=$FLUTTER_HOME/bin:$ANDROID_HOME/bin:$ANDROID_HOME/platform-tools:$PATH
      mkdir -p /home/gitpod/.android
      touch /home/gitpod/.android/repositories.cfg
    init: |
      echo "Installing Flutter SDK..."
      cd /workspace &amp;&amp; wget -qO flutter_sdk.tar.xz https://storage.googleapis.com/flutter_infra/releases/stable/linux/flutter_linux_v1.9.1+hotfix.4-stable.tar.xz &amp;&amp; tar -xf flutter_sdk.tar.xz &amp;&amp; rm -f flutter_sdk.tar.xz
      echo "Installing Android SDK..."
      mkdir -p /workspace/android-sdk &amp;&amp; cd /workspace/android-sdk &amp;&amp; wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip &amp;&amp; unzip -qq sdk-tools-linux-4333796.zip &amp;&amp; rm -f sdk-tools-linux-4333796.zip
      echo y | /workspace/android-sdk/tools/bin/sdkmanager "platform-tools" "platforms;android-28" "build-tools;28.0.3"
      echo "Init Flutter..."
      flutter channel master
      flutter upgrade
      yes | flutter doctor --android-licenses
      cd /workspace/gitpod-flutter
      flutter pub get
      flutter config --enable-web
      flutter doctor
    command: |
      echo "Ready to go!"
  - command: |
      export PATH=$FLUTTER_HOME/bin:$ANDROID_HOME/bin:$ANDROID_HOME/platform-tools:$PATH
      flutter upgrade
      flutter pub get
      flutter config --enable-web
      flutter doctor
      echo "Ready to start!"
vscode:
  extensions:
    - Dart-Code.dart-code@3.5.0-beta.1:Wg2nTABftVR/Dry4tqeY1w==
    - Dart-Code.flutter@3.5.0:/kOacEWdiDRLyN/idUiM4A==
    - Nash.awesome-flutter-snippets@2.0.3:6jLJSOqEGV5k8R0xYG7Oqg==
A parte da extensions, pode ocorrer que seja necessário instalar elas em separado, fica de exemplo como que eu estou usando.
Arquivo:.gitpod.Dockerfile
FROM gitpod/workspace-full-vnc

ENV ANDROID_HOME=/workspace/android-sdk \
    FLUTTER_ROOT=/workspace/flutter \
    FLUTTER_HOME=/workspace/flutter

USER root

RUN apt-get update &amp;&amp; \
    apt-get -y install build-essential libkrb5-dev gcc make gradle openjdk-8-jdk &amp;&amp; \
    apt-get clean &amp;&amp; \
    apt-get -y autoremove

Agora que já tem os arquivos de configuração do docker é necessário parar a área de trabalho “stop workspace” que fica lá nas opções de usuário (no menu do avatar) e depois iniciar novamente o workspace quando será instalado o SDK e deixar tudo configurado para iniciar o trabalho.

Se for mais confortável, pode fazer um fork de https://github.com/amarildolacerda/flutter_dart e pegar as configurações diretamente no projeto – ainda de brinde vai alguns exemplos que tenho usado no aprendizado.

Se gostou, deixa os seus comentários para que eu me reoriente as novas publicações.

Gratidão e bom uso.